commit 399c441e7a79d231442b6f6a906d5712c555ca6e Author: Xiaokailnol <2519840456@qq.com> Date: Tue Feb 3 22:48:23 2026 +0800 Merge Official Source diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..be278c7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,2260 @@ +# Changelog + +# Changelog + +## [2.6.0](https://github.com/Zephyruso/zashboard/compare/v2.5.0...v2.6.0) (2026-01-12) + + +### Features + +* add RuleHitCountCard component and update related translations for enhanced statistics display ([372cd67](https://github.com/Zephyruso/zashboard/commit/372cd67450d4dafe913daad94d33c3ff946028c3)) +* enhance rule management with mihomo API for toggling rule disable status and display rule hit/miss statistics in RuleCard component ([d93ae9b](https://github.com/Zephyruso/zashboard/commit/d93ae9bfd363718be2f93483ad00fcc33aac1394)) +* implement disconnect option on rule disable in RuleCard and update translations ([0476333](https://github.com/Zephyruso/zashboard/commit/04763334be4b6a264a7a870c4b97072d641a967c)) + + +### Bug Fixes + +* ensure dbPromise is awaited in put, clear, and del functions of useIndexedDB for proper transaction handling ([b194dfd](https://github.com/Zephyruso/zashboard/commit/b194dfd2c787cafa3b3d00be3dc2bf10803fdf86)) + +## [2.5.0](https://github.com/Zephyruso/zashboard/compare/v2.4.1...v2.5.0) (2025-12-24) + + +### Features + +* add auto cleanup interval feature to ConnectionHistory component with multilingual support ([1d358bd](https://github.com/Zephyruso/zashboard/commit/1d358bdb775a1129e5635400eaf34b32137ca3e5)) +* allow user input for DNS query type ([e230304](https://github.com/Zephyruso/zashboard/commit/e230304a5b56dd0fb5a72f6a7849f774134b2290)) + + +### Bug Fixes + +* adjust styling for ProxyIcon and ProxyNodeCard components for better alignment ([d14e6ff](https://github.com/Zephyruso/zashboard/commit/d14e6ff226a4c7b596b83bbe9e6662196424d9f9)) +* enhance styling and structure of menu items in TextInput component for improved user experience ([21c6a82](https://github.com/Zephyruso/zashboard/commit/21c6a829240c7393a3e93a1dacb3e01ce711bcb0)) + +## [2.4.1](https://github.com/Zephyruso/zashboard/compare/v2.4.0...v2.4.1) (2025-12-11) + + +### Bug Fixes + +* implement tooltip activation handling in TopologyCharts component to prevent data updates during tooltip visibility ([ccf3ee4](https://github.com/Zephyruso/zashboard/commit/ccf3ee40ece01c512bc6dacc245b0e6605af80ad)) +* update destination and destinationType labels in multiple language files for clarity ([5add1d8](https://github.com/Zephyruso/zashboard/commit/5add1d847963f3f7e095f520ba5b567da5967fbf)) + +## [2.4.0](https://github.com/Zephyruso/zashboard/compare/v2.3.1...v2.4.0) (2025-12-09) + + +### Features + +* add log filtering functionality with regex support ([1b4c56b](https://github.com/Zephyruso/zashboard/commit/1b4c56b5ee03414b5bd4a595f11992c00d24b772)) +* add toggle for full proxy chain display and update related components for improved user experience ([a378cc1](https://github.com/Zephyruso/zashboard/commit/a378cc1255413f6eb76b1c53603d07a240bfd01b)) +* enhance ProxyName component to display dialer proxy information dynamically ([d4faa97](https://github.com/Zephyruso/zashboard/commit/d4faa97ba1879a564917b8d7f43b22ad1e5653e1)) + + +### Bug Fixes + +* enhance TopologyCharts component by implementing node sorting and ID remapping for improved data visualization ([4873bf3](https://github.com/Zephyruso/zashboard/commit/4873bf311d3900c150d0ad256ec476121769391c)) +* update ProxyNodeCard layout and text styling for improved readability and responsiveness ([dd59806](https://github.com/Zephyruso/zashboard/commit/dd598062246e40ac975a901a08a24b80e2e600a6)) + +## [2.3.1](https://github.com/Zephyruso/zashboard/compare/v2.3.0...v2.3.1) (2025-12-02) + + +### Bug Fixes + +* control bar loses focus when the search result is empty ([32c8ca0](https://github.com/Zephyruso/zashboard/commit/32c8ca043b34e9ff122f84b583ed004136e01c17)) +* latency tests for all will now follow the logic of the independent latency test. ([8ccd6f3](https://github.com/Zephyruso/zashboard/commit/8ccd6f3d176b6bb9f69edff603d7460b86e9553a)) + +## [2.3.0](https://github.com/Zephyruso/zashboard/compare/v2.2.0...v2.3.0) (2025-11-28) + + +### Features + +* add provider traffic overview localization and integrate component into OverviewPage ([5c432c0](https://github.com/Zephyruso/zashboard/commit/5c432c0a4b1847a93e36cca56f6dfb06b6271711)) +* add quick filter toggle for connection visibility in ctrl with tooltip updates ([02e4441](https://github.com/Zephyruso/zashboard/commit/02e4441504d1cb1a19bb90bd1b9f5ff1e2844562)) +* implement touchend event handling for mobile charts and improve chart disposal logic ([b099a2a](https://github.com/Zephyruso/zashboard/commit/b099a2aa2ecf34189d2c91b820f8f8d1a1ad519c)) +* refactor OverviewPage to dynamically render cards based on visibility settings ([251fe79](https://github.com/Zephyruso/zashboard/commit/251fe79bed055c8effec94b5cb923bdc9576e642)) + + +### Bug Fixes + +* update regex handling in restructMatchs function to correctly match keys with and without colons ([528fcc8](https://github.com/Zephyruso/zashboard/commit/528fcc8333a2bafa2328acf621bc72bc79610301)) + +## [2.2.0](https://github.com/Zephyruso/zashboard/compare/v2.1.0...v2.2.0) (2025-11-18) + + +### Features + +* add backend selection dialog to sidebar ([9b31df2](https://github.com/Zephyruso/zashboard/commit/9b31df2e78d12f47900304f88b80fd4602732440)) +* add full-screen toggle functionality to TopologyCharts component with responsive chart rendering ([565ea7e](https://github.com/Zephyruso/zashboard/commit/565ea7edb66bb4958d18a5d3c03014e293cc06be)) + + +### Bug Fixes + +* improve getHostFromConnection function to handle different host scenarios and support IPv6 formatting ([33003b8](https://github.com/Zephyruso/zashboard/commit/33003b851d980ce9becbbfe06c052745fa93257d)) +* make table grouping state persistent ([017cbf9](https://github.com/Zephyruso/zashboard/commit/017cbf9a69c7306e7add1b2b1f9a08dcb6530354)) + +## [2.1.0](https://github.com/Zephyruso/zashboard/compare/v2.0.0...v2.1.0) (2025-11-10) + + +### Features + +* enhance ConnectionHistory component with aggregation options and clear history functionality inspired by metacubexd ([b052b83](https://github.com/Zephyruso/zashboard/commit/b052b83c8757d3e3cc6c08629cee3f2f9a2c7cba)) +* implement visibility checks for settings items across multiple components; enhance user experience by conditionally rendering UI elements based on visibility state ([bb6990e](https://github.com/Zephyruso/zashboard/commit/bb6990e8c7b1c2c2cb9cdc1bd963edc011a9326d)) +* implement visibility control for settings items across multiple components; enhance user experience by conditionally rendering settings based on hiddenSettingsItems ([0509244](https://github.com/Zephyruso/zashboard/commit/050924475d7b3a94424d4cb7e43ee7ce82f98979)) + + +### Bug Fixes + +* add settings-menu and ctrls-bar styles in main.css; update SettingsMenu.vue class for improved styling and functionality ([ec00509](https://github.com/Zephyruso/zashboard/commit/ec00509bd0fca0ae35736dbbdfb3c977f04a0560)) +* adjust setting item height in main.css; conditionally render OverviewCard in OverviewSettings.vue; improve scroll behavior logic in SettingsPage.vue ([d68c581](https://github.com/Zephyruso/zashboard/commit/d68c58180797531842b1c3570eebd61ae9235189)) + +## [2.0.0](https://github.com/Zephyruso/zashboard/compare/v1.108.1...v2.0.0) (2025-11-06) + + +### ⚠ BREAKING CHANGES + +* add styles to the dock in a style similar to the new Apple design +* add styles to the controls in a style similar to the new Apple design + +### Features + +* add styles to the controls in a style similar to the new Apple design ([11aa106](https://github.com/Zephyruso/zashboard/commit/11aa10611c29413668454074390cae0486d8f3b9)) +* add styles to the dock in a style similar to the new Apple design ([9680744](https://github.com/Zephyruso/zashboard/commit/9680744d11e8558815fb66fc3d879af631b6b906)) +* enhance settings UI by restructuring components for better layout and styling; implement consistent setting item design across various settings pages ([081141e](https://github.com/Zephyruso/zashboard/commit/081141eb9a5b1921304e9b2bf58c552468895b66)) +* simplify SettingsPage layout by replacing inline menu with a dedicated SettingsMenu component; enhance scroll behavior for menu item navigation ([7793ee2](https://github.com/Zephyruso/zashboard/commit/7793ee2910ecffbb5b4095d4424f3dd6e8885ee8)) + +## [1.108.1](https://github.com/Zephyruso/zashboard/compare/v1.108.0...v1.108.1) (2025-11-04) + + +### Bug Fixes + +* implement dynamic favicon switching based on color scheme preference for firfox ([005ff49](https://github.com/Zephyruso/zashboard/commit/005ff49a315db6e3668ee4a13d11add78d2bb4ea)) +* update RuleCard component to enhance toggle functionality and adjust checkbox styling; modify HomePage dialog layout ([9e123fc](https://github.com/Zephyruso/zashboard/commit/9e123fca102f88f707fb36e89141bfb8c90b7b9e)) + +## [1.108.0](https://github.com/Zephyruso/zashboard/compare/v1.107.0...v1.108.0) (2025-10-28) + + +### Features + +* add dark mode favicon and update asset inclusion in Vite configuration ([50a66c3](https://github.com/Zephyruso/zashboard/commit/50a66c3f478cf50942ba1d40751fa38e09fd8353)) +* add toggle functionality for rule disabling in RuleCard component and update API integration ([6e4512d](https://github.com/Zephyruso/zashboard/commit/6e4512dd69dc013701eabfff38a059bca4c3799f)) +* add Traditional Chinese localization support in zh-tw.ts ([5e837d1](https://github.com/Zephyruso/zashboard/commit/5e837d1a55e24fb4b1635fcf2382e0be29197201)) +* adjust smart rank display ([#520](https://github.com/Zephyruso/zashboard/issues/520)) ([7e2b125](https://github.com/Zephyruso/zashboard/commit/7e2b125d77897b47e691fda4e1a1830967f7750a)) +* remove ProxiesCharts component and update to TopologyCharts ([724f444](https://github.com/Zephyruso/zashboard/commit/724f4443f06fa41f50f33fe59ea49d5d2b3d9a45)) + + +### Bug Fixes + +* Dockerfile ([f604811](https://github.com/Zephyruso/zashboard/commit/f604811801a8573a9f95faa38732304a750f7391)) +* improve menu item interaction in TextInput component by adding overflow handling and refining delete functionality ([7010d41](https://github.com/Zephyruso/zashboard/commit/7010d41cc1bd7d11e2f9a1820fc815819f6efbf7)) +* standardize capitalization in English localization strings ([89bdeeb](https://github.com/Zephyruso/zashboard/commit/89bdeeb71834ed509fa6802121724aa635378325)) + +## [1.107.0](https://github.com/Zephyruso/zashboard/compare/v1.106.1...v1.107.0) (2025-10-11) + + +### Features + +* add more settings button and improve layout in sidebar components ([73de34f](https://github.com/Zephyruso/zashboard/commit/73de34f00d1abf242766ea8eacb90640d29445ae)) + + +### Bug Fixes + +* dialog style ([616cecb](https://github.com/Zephyruso/zashboard/commit/616cecb25386407f41fc0f16269c77c60a31c7ab)) +* url will only be shown in independentLatencyTest mode ([fd28fd4](https://github.com/Zephyruso/zashboard/commit/fd28fd4033445f515ec3793718e1ebeee85fc25b)) + +## [1.106.1](https://github.com/Zephyruso/zashboard/compare/v1.106.0...v1.106.1) (2025-10-01) + + +### Bug Fixes + +* unexpected dual column for proxies provider ([ace7864](https://github.com/Zephyruso/zashboard/commit/ace78646489a9ebf5c28abb6616e29acd64028c1)) + +## [1.106.0](https://github.com/Zephyruso/zashboard/compare/v1.105.0...v1.106.0) (2025-09-28) + + +### Features + +* add manual block(degrade) btn for conn which belongs to smart group ([#502](https://github.com/Zephyruso/zashboard/issues/502)) ([1a9c12b](https://github.com/Zephyruso/zashboard/commit/1a9c12b9b40979f480707d227ea588f58a484552)) +* select proxy node in connections and rules ([acd1cd2](https://github.com/Zephyruso/zashboard/commit/acd1cd286d71c9bf67f34c91fb70069e9901a422)) + + +### Bug Fixes + +* connection card style ([717108f](https://github.com/Zephyruso/zashboard/commit/717108fdd48e9bbe563614c2d1e474a1bd1b2bc3)) +* notification style ([9da9272](https://github.com/Zephyruso/zashboard/commit/9da927232cd0a73f260d52794fddf2a82179c618)) +* proxies page dual column style ([edda6f3](https://github.com/Zephyruso/zashboard/commit/edda6f37b7f6a073452ffc8ea49542d351dc4829)) + +## [1.105.0](https://github.com/Zephyruso/zashboard/compare/v1.104.0...v1.105.0) (2025-09-22) + + +### Features + +* auto switch to url backend if exist ([1315204](https://github.com/Zephyruso/zashboard/commit/1315204385835c3367415f55db0d636b6a940415)) +* interrupt connection when switching clash_mode ([3921572](https://github.com/Zephyruso/zashboard/commit/3921572103aa1c6918cff94e59fbed8dbc4916a4)) + + +### Bug Fixes + +* grouped connection table style ([3f4e082](https://github.com/Zephyruso/zashboard/commit/3f4e0828ef68fe8b4369c43175224b712d7d8f87)) +* notification style ([9ceeca4](https://github.com/Zephyruso/zashboard/commit/9ceeca42c7fd1c7ea8dae68b9d2e908ff87362d7)) +* total is 0 in subscription ([b45e17a](https://github.com/Zephyruso/zashboard/commit/b45e17a30fad0a4afa8d72cdf028a0489fcb8115)) + +## [1.104.0](https://github.com/Zephyruso/zashboard/compare/v1.103.1...v1.104.0) (2025-09-16) + + +### Features + +* allow setting separate test URLs for each group ([a7ef57a](https://github.com/Zephyruso/zashboard/commit/a7ef57a0ff5e466ab4f4177948dc2f57f5b93c58)) +* display URL in latency test result ([789bfed](https://github.com/Zephyruso/zashboard/commit/789bfed4814f56ae74ef75f55aaa424c120349c4)) + + +### Bug Fixes + +* add p-limiter for latency test ([7c656e8](https://github.com/Zephyruso/zashboard/commit/7c656e8bc0e5e21011ca40c97a297d536640e142)) + +## [1.103.1](https://github.com/Zephyruso/zashboard/compare/v1.103.0...v1.103.1) (2025-09-08) + + +### Bug Fixes + +* latency test result tip ([cd50643](https://github.com/Zephyruso/zashboard/commit/cd50643d6743fa224c3ec6300a835d00f46548a1)) +* proxies page performance ([b73f2ac](https://github.com/Zephyruso/zashboard/commit/b73f2acdb298307af362c346eab3ffbb582ed0dd)) +* smart core weights fetch ([748272b](https://github.com/Zephyruso/zashboard/commit/748272b082a2505ad4b37713a3e19f5893382593)) + +## [1.103.0](https://github.com/Zephyruso/zashboard/compare/v1.102.0...v1.103.0) (2025-08-31) + + +### Features + +* build with only one font ([fcb5592](https://github.com/Zephyruso/zashboard/commit/fcb559216fa6ef517bb9fbf3dc6c0f04d5060ed3)) +* display final outbound in proxy group ([e974d50](https://github.com/Zephyruso/zashboard/commit/e974d50f7cd8a0bacb404ca9a0de01087cbd1d77)) + + +### Bug Fixes + +* build release ([f6abc58](https://github.com/Zephyruso/zashboard/commit/f6abc58be3dc603692c4c426a849b7746f23fab0)) +* set history after latency test ([cee66f8](https://github.com/Zephyruso/zashboard/commit/cee66f8ecca239b388e1133e7bead2d9a9503785)) +* style for vertical info ([82c390c](https://github.com/Zephyruso/zashboard/commit/82c390c22a5bcab86b890802631aadec05a42e37)) +* twemoji color on ios ([b533a17](https://github.com/Zephyruso/zashboard/commit/b533a1757d6e7079f71a98390d45fc11cf8070f8)) + +## [1.102.0](https://github.com/Zephyruso/zashboard/compare/v1.101.1...v1.102.0) (2025-08-15) + + +### Features + +* add gh-pages-cdn-fonts branch ([899c1d0](https://github.com/Zephyruso/zashboard/commit/899c1d0a1e0af4ab807e90724c8cea7c434cfba1)) +* add toast for backend API call ([07833ba](https://github.com/Zephyruso/zashboard/commit/07833ba696865c9e54cce655a383a94da58e7ec0)) +* flush dns cache for sing-box ([acdee6f](https://github.com/Zephyruso/zashboard/commit/acdee6f4eb587cb4f66a802588f757ee58cb6806)) +* rule card style ([b0a421f](https://github.com/Zephyruso/zashboard/commit/b0a421f867512fbadc60eed3aaa12d326375b4cb)) +* style for proxy group ([b1de8f6](https://github.com/Zephyruso/zashboard/commit/b1de8f68eece3d767f66af577516e778c1141a62)) +* use twemoji for apple device ([5a7a6ff](https://github.com/Zephyruso/zashboard/commit/5a7a6ff804fb0f5ff8c6f4da452db94b8beb6971)) + + +### Bug Fixes + +* add default test turl ([be21958](https://github.com/Zephyruso/zashboard/commit/be21958e53024ee53996a3aa9e19593208041a21)) +* animation performance for proxy group mobile ([8547702](https://github.com/Zephyruso/zashboard/commit/85477025fa372705b7360567f585dc08e87f3f43)) +* placeholder for ip check failed ([e3a5aa3](https://github.com/Zephyruso/zashboard/commit/e3a5aa3c46ded51219d2be9d6083903ecc73b2a5)) + +## [1.101.1](https://github.com/Zephyruso/zashboard/compare/v1.101.0...v1.101.1) (2025-08-11) + + +### Bug Fixes + +* add timestamp for geoip api ([82258a9](https://github.com/Zephyruso/zashboard/commit/82258a9432bc22d2b2cccbdde50abde73545b60a)) +* add tip for mmdb file size ([e71aa4c](https://github.com/Zephyruso/zashboard/commit/e71aa4c49ff93ffb5ab34917d6010b4c6c46bbec)) +* tip for latency test failed ([47196a8](https://github.com/Zephyruso/zashboard/commit/47196a80215e430d1f3cd2b242bf86d881f2b8db)) +* tip position for import config ([6caf07c](https://github.com/Zephyruso/zashboard/commit/6caf07cb3d230da0efcb62227f1453e8ea6e76ad)) + +## [1.101.0](https://github.com/Zephyruso/zashboard/compare/v1.100.0...v1.101.0) (2025-08-05) + + +### Features + +* adaptation for sing-box ui upgrade API ([8f35a14](https://github.com/Zephyruso/zashboard/commit/8f35a14189057d693c66d0c575e95db0d3d52722)) +* channel selector for core upgrade ([b181dd1](https://github.com/Zephyruso/zashboard/commit/b181dd1aeef80fbb2c9f648477afce3ed06b1886)) +* check update for `mihomo_smart`'s fork ([#461](https://github.com/Zephyruso/zashboard/issues/461)) ([0a48f44](https://github.com/Zephyruso/zashboard/commit/0a48f4411c81ee742434907d66ee664497df67d4)) +* log card style ([ecaaea7](https://github.com/Zephyruso/zashboard/commit/ecaaea7a33af5f489198af74347da61b6b2b2029)) + + +### Bug Fixes + +* backend api buttons grid style ([375ab8e](https://github.com/Zephyruso/zashboard/commit/375ab8ee8ca1745d9a40cf7c035bcdf701c1a187)) +* collapse animation ([8453456](https://github.com/Zephyruso/zashboard/commit/8453456412e7af5e2378fc6e0a027656844dfc13)) +* force import settings ([e7ce551](https://github.com/Zephyruso/zashboard/commit/e7ce55108ef952ff1eed637baf9a67575243af61)) +* proxies scroll style ([96389aa](https://github.com/Zephyruso/zashboard/commit/96389aacdfbe055cd82e9678c0ca14199f2b6157)) + +## [1.100.0](https://github.com/Zephyruso/zashboard/compare/v1.99.0...v1.100.0) (2025-07-27) + + +### Features + +* flush dns cache ([81e67ab](https://github.com/Zephyruso/zashboard/commit/81e67abe19f3296f91e420674aa03d83a342b380)) +* outbound column ([715481c](https://github.com/Zephyruso/zashboard/commit/715481c18b4dcf181e483d494f4f52cf803bd2cf)) + + +### Bug Fixes + +* isProxyGroup for smart ([48efc1f](https://github.com/Zephyruso/zashboard/commit/48efc1fc2abf95a5b7eecec2de97d4ae5ec8d9b9)) +* log & toast style ([d2be891](https://github.com/Zephyruso/zashboard/commit/d2be891e07f3eda7460bf199883af2e8c7f841a7)) +* remove plimit for latency test ([5f8f70e](https://github.com/Zephyruso/zashboard/commit/5f8f70e63e5cc558ce919f2e6115496a8c14ca97)) + +## [1.99.0](https://github.com/Zephyruso/zashboard/compare/v1.98.0...v1.99.0) (2025-07-16) + + +### Features + +* shortcut key for router switch ([f246d36](https://github.com/Zephyruso/zashboard/commit/f246d3620fcb2e3fe359a0f53af9e4769bb18c01)) + + +### Bug Fixes + +* import settings modal style ([48fa4f8](https://github.com/Zephyruso/zashboard/commit/48fa4f8c6cba0ab5309be3a75cb7dfc9e6546ef5)) +* optgroup bg with opacity ([4b57b0b](https://github.com/Zephyruso/zashboard/commit/4b57b0b578cc0afa7b9642e843ea5f490d3d536d)) +* proxy sorting will no longer affect policy groups ([4467657](https://github.com/Zephyruso/zashboard/commit/446765780a142025d4e3752b388e45b8b5310033)) +* sort for proxy group not in GLOBAL ([27a6c5a](https://github.com/Zephyruso/zashboard/commit/27a6c5a2e4d926362539a34e01aa67f8d5ae1a83)) + +## [1.98.0](https://github.com/Zephyruso/zashboard/compare/v1.97.0...v1.98.0) (2025-07-08) + + +### Features + +* new looking for sidebar ([934d949](https://github.com/Zephyruso/zashboard/commit/934d94942d797c81317bccec21804305ba5b7ded)) + + +### Bug Fixes + +* i18n ([37e3a01](https://github.com/Zephyruso/zashboard/commit/37e3a01fa7113fe92149ca60919966af7cbd5a13)) +* performance for massive connections ([866ea1d](https://github.com/Zephyruso/zashboard/commit/866ea1d46358b946b76a2aadeb4d28460878c01d)) + +## [1.97.0](https://github.com/Zephyruso/zashboard/compare/v1.96.0...v1.97.0) (2025-07-04) + + +### Features + +* import settings with auto update ([2935456](https://github.com/Zephyruso/zashboard/commit/2935456f8dbba9d192543e3255d2dfdb08071ac4)) + + +### Bug Fixes + +* icon size settings ([95dc3a5](https://github.com/Zephyruso/zashboard/commit/95dc3a51d566f7189c5804a2d8d72e1bb4b43ff0)) +* some minor fix ([6f2edb6](https://github.com/Zephyruso/zashboard/commit/6f2edb651f32bfcda05f341647650d9cc5dfda84)) + +## [1.96.0](https://github.com/Zephyruso/zashboard/compare/v1.95.1...v1.96.0) (2025-06-27) + + +### Features + +* add network info switch for overview page ([bc5403d](https://github.com/Zephyruso/zashboard/commit/bc5403da40134b0d6800806321930e7e62dfb955)) + + +### Bug Fixes + +* add key for proxy chains ([c2ec9b6](https://github.com/Zephyruso/zashboard/commit/c2ec9b66f91e4576ede113f00277911c0a5411bf)) +* performance for massive connections ([a1de977](https://github.com/Zephyruso/zashboard/commit/a1de977005eeae0e02e139ea466390fa9623b0a5)) + +## [1.95.1](https://github.com/Zephyruso/zashboard/compare/v1.95.0...v1.95.1) (2025-06-23) + + +### Bug Fixes + +* proxy group sort by latency ([8ee9827](https://github.com/Zephyruso/zashboard/commit/8ee9827030fce65530036dd44181e3673ff0b078)) + +## [1.95.0](https://github.com/Zephyruso/zashboard/compare/v1.94.2...v1.95.0) (2025-06-21) + + +### Features + +* add level filter options for logs ([6f5c9dc](https://github.com/Zephyruso/zashboard/commit/6f5c9dcf5fe4d44ac8c57880fdb684b27eb667cd)) +* smart group sort by usage ([116f01d](https://github.com/Zephyruso/zashboard/commit/116f01dda993fe5dc19bb84506ef76041936cf03)) + + +### Bug Fixes + +* drag for thead resize handler ([bdea376](https://github.com/Zephyruso/zashboard/commit/bdea3765252f86997a12914fd1789d7b4ba6bfbb)) +* duplicate theme selector ([b76f9b7](https://github.com/Zephyruso/zashboard/commit/b76f9b75b87ef487fd5015136c45bbcdb5522a6a)) + +## [1.94.2](https://github.com/Zephyruso/zashboard/compare/v1.94.1...v1.94.2) (2025-06-16) + + +### Bug Fixes + +* drag behavier ([3fdfd73](https://github.com/Zephyruso/zashboard/commit/3fdfd73b4b160a63d61b88a20a64873fa37f20de)) +* proxy chains sort ([851ebb9](https://github.com/Zephyruso/zashboard/commit/851ebb9b8fec95063601936b76be537d7fca309e)) +* remove close btn for closed conns ([23abefe](https://github.com/Zephyruso/zashboard/commit/23abefe66de64723dc47026461b58596f73727df)) + +## [1.94.1](https://github.com/Zephyruso/zashboard/compare/v1.94.0...v1.94.1) (2025-06-13) + + +### Bug Fixes + +* auto scroll into view for mobile ([c5e701c](https://github.com/Zephyruso/zashboard/commit/c5e701cd970903b290f97ca93c6ecdbd56e2d64c)) +* blur bg style for pinned columns ([3f4f8ec](https://github.com/Zephyruso/zashboard/commit/3f4f8ec4603c3a15767d22e76a354e20b89eee08)) +* copy value for proxy chains column ([1ab23bf](https://github.com/Zephyruso/zashboard/commit/1ab23bf0fe45ff76d27688d140d23d80d1a91847)) +* fullscreen mode for proxies chart with bg-image ([acac4b6](https://github.com/Zephyruso/zashboard/commit/acac4b64596ee38483b7337228c2f12166cc6ebd)) + +## [1.94.0](https://github.com/Zephyruso/zashboard/compare/v1.93.1...v1.94.0) (2025-06-12) + + +### Features + +* copy table content with right click ([c932df2](https://github.com/Zephyruso/zashboard/commit/c932df2f2a17ba8a74afec59c320244f8ccc78dc)) +* drag the table with left mouse button ([4832487](https://github.com/Zephyruso/zashboard/commit/4832487886beecf412c3f4a243483a9661080a75)) +* make backend editable ([1bc72de](https://github.com/Zephyruso/zashboard/commit/1bc72deaa9e47589970a56ceb5e4d55775015198)) +* pinnable columns ([abd8184](https://github.com/Zephyruso/zashboard/commit/abd8184707c153533fbd2619a60a51721ff2009c)) + + +### Bug Fixes + +* sourceip scope for logs ([16379ff](https://github.com/Zephyruso/zashboard/commit/16379ffa423a99bd133672282b21d6dde8554d75)) + +## [1.93.1](https://github.com/Zephyruso/zashboard/compare/v1.93.0...v1.93.1) (2025-06-05) + + +### Bug Fixes + +* collapse style with opacity ([78eefe3](https://github.com/Zephyruso/zashboard/commit/78eefe39a862acbef852c2a47420d7edf3cbcfa0)) +* final destination for direct connection ([0702d1a](https://github.com/Zephyruso/zashboard/commit/0702d1afdb46b11ab0c4f31021d3570e3f19b0cc)) +* smart weights display for mobile ([4183ece](https://github.com/Zephyruso/zashboard/commit/4183ece9049114cfcf4c686c45e6833f56635128)) + +## [1.93.0](https://github.com/Zephyruso/zashboard/compare/v1.92.0...v1.93.0) (2025-05-29) + + +### Features + +* support multiple keywords in search ([0fc9775](https://github.com/Zephyruso/zashboard/commit/0fc97751a3d31268f0e6bbf7d00e65574882ecea)) + + +### Bug Fixes + +* incorrect scrolling status when switching tabs in proxies ([bce9465](https://github.com/Zephyruso/zashboard/commit/bce9465b21b9206a277b6ec0f63581f6ff34da5c)) +* popover position for theme selector ([e9adf77](https://github.com/Zephyruso/zashboard/commit/e9adf77874d443d301fc86877c917b13f57c2129)) + +## [1.92.0](https://github.com/Zephyruso/zashboard/compare/v1.91.0...v1.92.0) (2025-05-27) + + +### Features + +* proxies relationship chart ([c504440](https://github.com/Zephyruso/zashboard/commit/c504440e2e9f4596c6e707b67c6d61a437ce3141)) + + +### Bug Fixes + +* tab style for mobile ([ed5b7fd](https://github.com/Zephyruso/zashboard/commit/ed5b7fd1db813d58b8bc8a3f60b9052f8f665d9a)) + +## [1.91.0](https://github.com/Zephyruso/zashboard/compare/v1.90.0...v1.91.0) (2025-05-26) + + +### Features + +* auto scroll into view for proxy node ([3064505](https://github.com/Zephyruso/zashboard/commit/30645058f639dbe01cf07654b1460c73db905129)) +* collapsible sliders for opacity and blur ([5d88196](https://github.com/Zephyruso/zashboard/commit/5d881964ce0c952ab9e2299139cb876573703305)) +* download logs ([16fc9ee](https://github.com/Zephyruso/zashboard/commit/16fc9ee86685a472835e01d9d6108cf6c3a243d9)) +* store scroll status for proxies page ([0522c68](https://github.com/Zephyruso/zashboard/commit/0522c68ca0eed7ae47745e5deb6bd5c3980c1f29)) + + +### Bug Fixes + +* display issue of the popover for sourceip scope ([6db326e](https://github.com/Zephyruso/zashboard/commit/6db326e2d2c89333e1aa261bdb6703042562c34d)) +* flickering scrollbar ([7aaa6aa](https://github.com/Zephyruso/zashboard/commit/7aaa6aa6c3bd20a479bb53f9f41fa7c7797c73f6)) +* freezing issue when scroll animation is on ([e293832](https://github.com/Zephyruso/zashboard/commit/e2938326b98b8ed9ffd62a66fb7121569df2f479)) +* missing sourceip filter when ip is invalid ([e5ff530](https://github.com/Zephyruso/zashboard/commit/e5ff53080c30e6e7afcea0cc357a3fc964a2fe4a)) +* unable to disable blur in proxies for mobile ([16441fc](https://github.com/Zephyruso/zashboard/commit/16441fc788a7c00c328e5b4617b93102dba01b77)) + +## [1.90.0](https://github.com/Zephyruso/zashboard/compare/v1.89.2...v1.90.0) (2025-05-23) + + +### Features + +* custom global node for sing-box ([9ead3bd](https://github.com/Zephyruso/zashboard/commit/9ead3bdb3fcac40f0c1c0abc6238853ba958560b)) +* new animation for proxy group mobile ([e06225e](https://github.com/Zephyruso/zashboard/commit/e06225ed9065e883ef64bdb6ddbe2d333cab1402)) + + +### Bug Fixes + +* independent latency test for provider healthcheck ([9f26a5d](https://github.com/Zephyruso/zashboard/commit/9f26a5defd581c18fb20fc828373d9dc57ec6b0b)) + +## [1.89.2](https://github.com/Zephyruso/zashboard/compare/v1.89.1...v1.89.2) (2025-05-22) + + +### Bug Fixes + +* dropdown position for safari ([51ca0a4](https://github.com/Zephyruso/zashboard/commit/51ca0a4f42a7e9e8a86186e767b3a6117390795c)) +* font name of system-ui ([#381](https://github.com/Zephyruso/zashboard/issues/381)) ([f5a9db9](https://github.com/Zephyruso/zashboard/commit/f5a9db98be666e7c66be6674d51d8b9ea7998a2b)) + +## [1.89.1](https://github.com/Zephyruso/zashboard/compare/v1.89.0...v1.89.1) (2025-05-21) + + +### Bug Fixes + +* animation performance for proxy group mobile ([d80c8b0](https://github.com/Zephyruso/zashboard/commit/d80c8b086116df94ffbcc47e4a1ba156515900fd)) +* lazy load for proxies ([80ef19c](https://github.com/Zephyruso/zashboard/commit/80ef19c911dc8ad03f17e84b6566659f51040b2b)) +* truncate name for proxy group mobile ([bf74766](https://github.com/Zephyruso/zashboard/commit/bf74766dfa574640f78eeee6403439eb9fa2dc89)) + +## [1.89.0](https://github.com/Zephyruso/zashboard/compare/v1.88.0...v1.89.0) (2025-05-20) + + +### Features + +* theme selector with preview ([e8a4335](https://github.com/Zephyruso/zashboard/commit/e8a4335bdf2c3e85b3378d2b8a64c98d25884220)) + + +### Bug Fixes + +* asn name -> org name ([337f2c4](https://github.com/Zephyruso/zashboard/commit/337f2c48c2e0520e93d3e1a5e7f834926c931ca2)) +* is private ip ([570aae9](https://github.com/Zephyruso/zashboard/commit/570aae9be701c41abd8f107e54dba4ac41791b6b)) +* sourceip label scope style ([1cb4c56](https://github.com/Zephyruso/zashboard/commit/1cb4c56f34fdb87716ce637d08baa0e925deddf9)) + +## [1.88.0](https://github.com/Zephyruso/zashboard/compare/v1.87.0...v1.88.0) (2025-05-19) + + +### Features + +* effective scope for source ip label ([672df69](https://github.com/Zephyruso/zashboard/commit/672df6996c90926b5c04ac69a6c016257cccf34e)) + + +### Bug Fixes + +* display backend label instead of url ([6cc3469](https://github.com/Zephyruso/zashboard/commit/6cc3469e2b3eda6f064f2fa49e408da4f90facef)) +* truncate long version numbers ([534891b](https://github.com/Zephyruso/zashboard/commit/534891bf7d3450713a23e847c78af12aad4f6b21)) + +## [1.87.0](https://github.com/Zephyruso/zashboard/compare/v1.86.0...v1.87.0) (2025-05-16) + + +### Features + +* adaptive modal height for proxy group mobile ([7a5db3d](https://github.com/Zephyruso/zashboard/commit/7a5db3d2d42c8c1c8086c499512ba2bf2dff8947)) +* auto disconnect idle udp ([7943d73](https://github.com/Zephyruso/zashboard/commit/7943d7304b0716daf687d605cb58ca58a00c92ce)) +* switch for swipe in pages ([6833d5e](https://github.com/Zephyruso/zashboard/commit/6833d5e76bd043b30f3ecafb997f28b8c583d8e5)) + +## [1.86.0](https://github.com/Zephyruso/zashboard/compare/v1.85.0...v1.86.0) (2025-05-15) + + +### Features + +* search log with type selector ([8741f2c](https://github.com/Zephyruso/zashboard/commit/8741f2cfa59692b564e4851e8c1c12233fea6b24)) + + +### Bug Fixes + +* delete fixed for smart group ([5bcbd2b](https://github.com/Zephyruso/zashboard/commit/5bcbd2b31819e721a10f76a357e8e881ab59498a)) + +## [1.85.0](https://github.com/Zephyruso/zashboard/compare/v1.84.0...v1.85.0) (2025-05-14) + + +### Features + +* display proxies grouped by provider ([5207e76](https://github.com/Zephyruso/zashboard/commit/5207e76aee03773bf43099070de1c418a0cf6055)) +* usage desc in proxy card for smart group ([8f1b370](https://github.com/Zephyruso/zashboard/commit/8f1b37009222c083ba63c30cda7c72ef13928671)) + + +### Bug Fixes + +* cache for github api ([51c6926](https://github.com/Zephyruso/zashboard/commit/51c692656405065d6e18a8b6c35cf3fe8a1c0daf)) +* missing lock icon for proxy group in mobile ([c66c846](https://github.com/Zephyruso/zashboard/commit/c66c84635fbf1c379b97c15ab4a333fb54a2708d)) + +## [1.84.0](https://github.com/Zephyruso/zashboard/compare/v1.83.0...v1.84.0) (2025-05-13) + + +### Features + +* latency test result tip ([02e4628](https://github.com/Zephyruso/zashboard/commit/02e462841835d7efddf4a58483401c328e4c1079)) +* options for geoip api ([ef693c8](https://github.com/Zephyruso/zashboard/commit/ef693c821686b9d56194787bf9fd0d36b4f0b443)) + + +### Bug Fixes + +* cache github version check result ([b9f9864](https://github.com/Zephyruso/zashboard/commit/b9f9864594e9306874a060c026259eef155f09d6)) +* display now node for smart ([856a4a6](https://github.com/Zephyruso/zashboard/commit/856a4a6cc8f7b410a25741453b5c99c274088964)) + +## [1.83.0](https://github.com/Zephyruso/zashboard/compare/v1.82.0...v1.83.0) (2025-05-12) + + +### Features + +* make logs history deletable ([efe4bdb](https://github.com/Zephyruso/zashboard/commit/efe4bdb47b8ce98543fe0845f8987e324d9147f2)) +* smart group api ([55510ae](https://github.com/Zephyruso/zashboard/commit/55510ae801a458cd933dae63b332c0826d78845d)) + + +### Bug Fixes + +* block error tip for latency test ([a84c608](https://github.com/Zephyruso/zashboard/commit/a84c60846d221632cb3a206f7d2e5796894415cc)) +* sourceip options sort ([7907be0](https://github.com/Zephyruso/zashboard/commit/7907be04d52433227d0ae5bde32c0a71ec44ec23)) +* style ([193acc2](https://github.com/Zephyruso/zashboard/commit/193acc23cfdb60722b4b16e1544f6c458d111518)) + +## [1.82.0](https://github.com/Zephyruso/zashboard/compare/v1.81.0...v1.82.0) (2025-04-27) + + +### Features + +* display icon in connection details ([182b51f](https://github.com/Zephyruso/zashboard/commit/182b51f1266ae945d058795d1a8a5aa52336aba7)) + + +### Bug Fixes + +* case insensitive search for connections ([771119a](https://github.com/Zephyruso/zashboard/commit/771119a9fde71b97dccd7f096a75592201b1169e)) +* discard outdated latency result ([97cce60](https://github.com/Zephyruso/zashboard/commit/97cce608067b038db05f7c418b9410b29f160d99)) +* xudp tag display ([c281d7c](https://github.com/Zephyruso/zashboard/commit/c281d7c6736c01f90b31a56bd3cf0ced66bc8452)) + +## [1.81.0](https://github.com/Zephyruso/zashboard/compare/v1.80.2...v1.81.0) (2025-04-23) + + +### Features + +* inbound user column ([7db4d1b](https://github.com/Zephyruso/zashboard/commit/7db4d1bbdd23c043e5b8cd9f503a96239053667e)) +* notification for upgrade result ([a5adac3](https://github.com/Zephyruso/zashboard/commit/a5adac3be271598db4baf85f39f4dc98855650c8)) +* setting for number of charts in sidebar ([0f61f4d](https://github.com/Zephyruso/zashboard/commit/0f61f4da39a57600cd978a40bd1462810120395e)) + + +### Bug Fixes + +* icon settings collapse style ([7a17311](https://github.com/Zephyruso/zashboard/commit/7a173114da06fe31a7df25a0c1049c308b7b19c7)) +* safe area for bottom ([72b3682](https://github.com/Zephyruso/zashboard/commit/72b36821e32675850796564f2f23994fcf03cef4)) + +## [1.80.2](https://github.com/Zephyruso/zashboard/compare/v1.80.1...v1.80.2) (2025-04-18) + + +### Bug Fixes + +* input select style ([f20ed11](https://github.com/Zephyruso/zashboard/commit/f20ed1186c2ce63f670bc0e42cff7d78751b063e)) +* modal opacity style ([5c56ddc](https://github.com/Zephyruso/zashboard/commit/5c56ddc7f9184871deacdfa29814ca8bb94b04cc)) + +## [1.80.1](https://github.com/Zephyruso/zashboard/compare/v1.80.0...v1.80.1) (2025-04-17) + + +### Bug Fixes + +* 0 in memory ws ([870dc0f](https://github.com/Zephyruso/zashboard/commit/870dc0f127b4a8047f8e31a0ad33dbfb6b2c0666)) +* style for transparent ([6e54ac3](https://github.com/Zephyruso/zashboard/commit/6e54ac322b6d8feb304ff370a003056c7e99ed39)) + +## [1.80.0](https://github.com/Zephyruso/zashboard/compare/v1.79.0...v1.80.0) (2025-04-16) + + +### Features + +* blur intensity ([cc73668](https://github.com/Zephyruso/zashboard/commit/cc73668889d18f177065406b9633329c76edba1f)) +* import settings from url ([a67a337](https://github.com/Zephyruso/zashboard/commit/a67a33718b51217e86eeef4b819af573a7a973f6)) + + +### Bug Fixes + +* proxy group invisible ([461aa0b](https://github.com/Zephyruso/zashboard/commit/461aa0b1ea81b640ce7a174df31531ced4636999)) + +## [1.79.0](https://github.com/Zephyruso/zashboard/compare/v1.78.0...v1.79.0) (2025-04-15) + + +### Features + +* blurry effect ([8398a43](https://github.com/Zephyruso/zashboard/commit/8398a435bfffb8aaaaac22423fb33dc6e76378ad)) +* display all features setting for sing-box fork ([f309a89](https://github.com/Zephyruso/zashboard/commit/f309a89d193556f01663782b2ca8772390b8a6bf)) +* switch for scroll animation ([1d9062b](https://github.com/Zephyruso/zashboard/commit/1d9062b60274e09a4afd350eddbdbe9155f4c477)) + + +### Bug Fixes + +* bounce animation flicker ([7ceafb9](https://github.com/Zephyruso/zashboard/commit/7ceafb91cf0ae1f433c22abfc9d25f465bbacb8a)) +* translate for mode ([2660118](https://github.com/Zephyruso/zashboard/commit/2660118f9103b802d6ec4051238974146eb8d040)) + +## [1.78.0](https://github.com/Zephyruso/zashboard/compare/v1.77.0...v1.78.0) (2025-04-13) + + +### Features + +* reF1nd sing-box ([16c4f47](https://github.com/Zephyruso/zashboard/commit/16c4f47ba6a19d7196c4774d96c4de4917adedce)) +* update rule provider in rules ([1a9605c](https://github.com/Zephyruso/zashboard/commit/1a9605c89d4c99e67cff76e460ab9118b862e385)) + +## [1.77.0](https://github.com/Zephyruso/zashboard/compare/v1.76.3...v1.77.0) (2025-04-11) + + +### Features + +* bouncein animation for mobile ([a1e9d38](https://github.com/Zephyruso/zashboard/commit/a1e9d38b3f98d28ee0a100635e5c4e79aa0c660f)) + + +### Bug Fixes + +* chart performance ([ca7b861](https://github.com/Zephyruso/zashboard/commit/ca7b861fea8e03fc8d9ba94863d561fb1dd52c8c)) + +## [1.76.3](https://github.com/Zephyruso/zashboard/compare/v1.76.2...v1.76.3) (2025-04-10) + + +### Bug Fixes + +* merge same sourceip label options ([43a8576](https://github.com/Zephyruso/zashboard/commit/43a8576eddab71f5c7590a166b9a84dc738bea1a)) +* performance for page switch ([f25b5bc](https://github.com/Zephyruso/zashboard/commit/f25b5bc12e46e4482da48597ad967ddbaa5293ee)) +* swipe animation performance ([9837522](https://github.com/Zephyruso/zashboard/commit/98375229e1db1efb8d8047e2f592ca56950fee53)) + +## [1.76.2](https://github.com/Zephyruso/zashboard/compare/v1.76.1...v1.76.2) (2025-04-02) + + +### Bug Fixes + +* hidden group icon status ([89d459a](https://github.com/Zephyruso/zashboard/commit/89d459ab56c718de2d0cc3440bf71c13493abaaf)) +* make disable pull to refresh optional ([56e2273](https://github.com/Zephyruso/zashboard/commit/56e2273da856510f95b95aba19b208798366183e)) + +## [1.76.1](https://github.com/Zephyruso/zashboard/compare/v1.76.0...v1.76.1) (2025-03-30) + + +### Bug Fixes + +* catch icon settings empty ([1e95227](https://github.com/Zephyruso/zashboard/commit/1e95227f34c3b076db09c310a3d7b6e210d5b071)) +* chart style in sidebar ([88e4b17](https://github.com/Zephyruso/zashboard/commit/88e4b176cd28d636a542d2dda820396638ee00c5)) + +## [1.76.0](https://github.com/Zephyruso/zashboard/compare/v1.75.2...v1.76.0) (2025-03-26) + + +### Features + +* custom icon for sing-box ([6db29e2](https://github.com/Zephyruso/zashboard/commit/6db29e2b7a2cc885a3ae67fd301bf18df1922996)) + + +### Bug Fixes + +* outline style ([c840ab2](https://github.com/Zephyruso/zashboard/commit/c840ab2d71ff8f4f118ac98dc7d00bff3b400e71)) + +## [1.75.2](https://github.com/Zephyruso/zashboard/compare/v1.75.1...v1.75.2) (2025-03-24) + + +### Bug Fixes + +* collapse animation performance ([9c72e96](https://github.com/Zephyruso/zashboard/commit/9c72e961725a15196188edb3731c8c48d6944237)) +* missing collapse all btn in mobile ([3e7ee22](https://github.com/Zephyruso/zashboard/commit/3e7ee224f0646820f6f3635c37d8b05a95644457)) +* region for details ([eaf8cf1](https://github.com/Zephyruso/zashboard/commit/eaf8cf1127bafaa6dfa83d9d90f60ae972489e3c)) + +## [1.75.1](https://github.com/Zephyruso/zashboard/compare/v1.75.0...v1.75.1) (2025-03-21) + + +### Bug Fixes + +* long rule for sing-box in table ([164f98f](https://github.com/Zephyruso/zashboard/commit/164f98ff6a54fec9a8e10563f72f496094bd26ce)) +* setup page style ([5305893](https://github.com/Zephyruso/zashboard/commit/5305893312e0a35f4c8d859df9146be97039a465)) + +## [1.75.0](https://github.com/Zephyruso/zashboard/compare/v1.74.0...v1.75.0) (2025-03-19) + + +### Features + +* city location for connection details ([44eb86d](https://github.com/Zephyruso/zashboard/commit/44eb86d11f24afc1a5eca138b0688cca7a4078f0)) +* using the existing theme to reset custom theme ([daadc8f](https://github.com/Zephyruso/zashboard/commit/daadc8f6ba43cf66841aff80280bd42a447633e3)) + + +### Bug Fixes + +* long host style in connection table ([9428cc5](https://github.com/Zephyruso/zashboard/commit/9428cc505d0d46200042b21e5164f56d58f449e2)) + +## [1.74.0](https://github.com/Zephyruso/zashboard/compare/v1.73.1...v1.74.0) (2025-03-18) + + +### Features + +* add custom theme ([4679ddd](https://github.com/Zephyruso/zashboard/commit/4679dddb900271386e430e7bc691bbf06fb44180)) + + +### Bug Fixes + +* highlight text for dark theme ([902af5c](https://github.com/Zephyruso/zashboard/commit/902af5cff064d9f3b29329383be43b48f1d6d089)) + +## [1.73.1](https://github.com/Zephyruso/zashboard/compare/v1.73.0...v1.73.1) (2025-03-17) + + +### Bug Fixes + +* make daisyui v5 theme as default ([e0bc078](https://github.com/Zephyruso/zashboard/commit/e0bc07881ce62258534e40874513455864c6a987)) +* proxy chains sort ([3415c3f](https://github.com/Zephyruso/zashboard/commit/3415c3f89b29df6f2bdefca7e66e5aba8be482db)) +* style for connection details ([17d3504](https://github.com/Zephyruso/zashboard/commit/17d3504eac86856d24cb8e7b3312c1d299d42f96)) + +## [1.73.0](https://github.com/Zephyruso/zashboard/compare/v1.72.3...v1.73.0) (2025-03-14) + + +### Features + +* support all sing-box log level ([864997c](https://github.com/Zephyruso/zashboard/commit/864997c21efd1f14c8abe3cd76247529f0c4cb7c)) + + +### Bug Fixes + +* catch testurl is empty ([a40c7e3](https://github.com/Zephyruso/zashboard/commit/a40c7e312d5d9e1ca7a2e9d32ad1a5e870e75578)) +* remove openai cdn test ([78bfe09](https://github.com/Zephyruso/zashboard/commit/78bfe0945bb09fa9f9d5f30227b6e1a4c3fdda93)) +* remove redirect to setup when api 404 ([50ca346](https://github.com/Zephyruso/zashboard/commit/50ca346b806491c12fa066ffca1bc75353a6310e)) + +## [1.72.3](https://github.com/Zephyruso/zashboard/compare/v1.72.2...v1.72.3) (2025-03-12) + + +### Bug Fixes + +* prevent selecting a selected node ([b7fd831](https://github.com/Zephyruso/zashboard/commit/b7fd8316420574da51b591bfac55a5afc085f1aa)) + +## [1.72.2](https://github.com/Zephyruso/zashboard/compare/v1.72.1...v1.72.2) (2025-03-10) + + +### Bug Fixes + +* animation for swipe page ([f25078c](https://github.com/Zephyruso/zashboard/commit/f25078cad670cc4dcae709a8fe095d79722768c1)) +* i18n for remote destination ([788fef0](https://github.com/Zephyruso/zashboard/commit/788fef0fa8c0a979c4d290032a2c507826868474)) + +## [1.72.1](https://github.com/Zephyruso/zashboard/compare/v1.72.0...v1.72.1) (2025-03-07) + + +### Bug Fixes + +* connection & proxies ctrl style for iPad ([9c02345](https://github.com/Zephyruso/zashboard/commit/9c023451662e1922de4d7f0128a338c694cabda8)) +* separate destination and proxy node IP column ([51a502e](https://github.com/Zephyruso/zashboard/commit/51a502e942d63be1fab9fbb0b81c94714f97237c)) + +## [1.72.0](https://github.com/Zephyruso/zashboard/compare/v1.71.0...v1.72.0) (2025-03-06) + + +### Features + +* policy fixed now tip ([f1fab79](https://github.com/Zephyruso/zashboard/commit/f1fab79c191a727bb1e134ffa6795827299cefd3)) + + +### Bug Fixes + +* remove CNAME in dist ([5e1c4d3](https://github.com/Zephyruso/zashboard/commit/5e1c4d31884c56327f38fb91925d69d025e4be3c)) +* sort desc first for speeds ([f3030bc](https://github.com/Zephyruso/zashboard/commit/f3030bcc567a12324cfdcfdc6b08b3c32d9603de)) +* url https params ([c53165a](https://github.com/Zephyruso/zashboard/commit/c53165a3f1bf1695457a2970912da6513b5b6903)) + +## [1.71.0](https://github.com/Zephyruso/zashboard/compare/v1.70.2...v1.71.0) (2025-03-05) + + +### Features + +* daisyuiv5-theme ([eec8209](https://github.com/Zephyruso/zashboard/commit/eec8209f23f6dab9ab1247d66286870bb9604052)) +* link to backend github ([ae6a3e0](https://github.com/Zephyruso/zashboard/commit/ae6a3e05e97acabc6f61f751775ce85ceee6a594)) +* setting for auto theme ([1aabb78](https://github.com/Zephyruso/zashboard/commit/1aabb784e07ce050f77c4cd3a970e716455b525f)) + +## [1.70.2](https://github.com/Zephyruso/zashboard/compare/v1.70.1...v1.70.2) (2025-03-04) + + +### Bug Fixes + +* bring old default theme back ([ecd0a5b](https://github.com/Zephyruso/zashboard/commit/ecd0a5b30af7ac3240c86b742fa7a970f553988a)) + +## [1.70.1](https://github.com/Zephyruso/zashboard/compare/v1.70.0...v1.70.1) (2025-03-04) + + +### Bug Fixes + +* bring old v4 light and dark theme back to v5 ([d73484a](https://github.com/Zephyruso/zashboard/commit/d73484a84d2e429857e4eb899fe479c78e1a4f4c)) + +## [1.70.0](https://github.com/Zephyruso/zashboard/compare/v1.69.0...v1.70.0) (2025-03-03) + + +### Features + +* daisyui v5 & tailwindcss v4 ([9df2083](https://github.com/Zephyruso/zashboard/commit/9df2083da99dc5baf008edc36f49d68c9c89171e)) +* transfer type for connection table ([28f59c3](https://github.com/Zephyruso/zashboard/commit/28f59c3c8006054c9d40b29b96ee29f5e8b2ff12)) + + +### Bug Fixes + +* hide toggle collapse btn ([3f3bc03](https://github.com/Zephyruso/zashboard/commit/3f3bc03394925005abbc9e3f9101c172f00ee7aa)) +* promise race for automatic backend switch ([be8fecb](https://github.com/Zephyruso/zashboard/commit/be8fecbc50643dd71919233914ae1b867d348379)) + +## [1.69.0](https://github.com/Zephyruso/zashboard/compare/v1.68.3...v1.69.0) (2025-02-26) + + +### Features + +* switch for display now node & latency number in rule ([5a24958](https://github.com/Zephyruso/zashboard/commit/5a24958d18258136abe8af33e7870ae23ba1a9af)) + + +### Bug Fixes + +* provider style and count ([8a2eb32](https://github.com/Zephyruso/zashboard/commit/8a2eb32ff73648d7a6537f5875344dcd7fc681e9)) +* proxies preview auto ([9c1f00b](https://github.com/Zephyruso/zashboard/commit/9c1f00b9163ee44ba87bb054c7410da857d31237)) + +## [1.68.3](https://github.com/Zephyruso/zashboard/compare/v1.68.2...v1.68.3) (2025-02-25) + + +### Bug Fixes + +* manage hidden groups and other minor style ([cde403e](https://github.com/Zephyruso/zashboard/commit/cde403ec9e8ce6466bedb7b18c5d354f36111edc)) +* proxy group grid style ([460d426](https://github.com/Zephyruso/zashboard/commit/460d426208f9873f48026ee1fed16447ea0822e5)) + +## [1.68.2](https://github.com/Zephyruso/zashboard/compare/v1.68.1...v1.68.2) (2025-02-24) + + +### Bug Fixes + +* animation and performance for two-column ([1e38efc](https://github.com/Zephyruso/zashboard/commit/1e38efc5ebe7af0672aea8e3a205d2aa34794673)) + +## [1.68.1](https://github.com/Zephyruso/zashboard/compare/v1.68.0...v1.68.1) (2025-02-24) + + +### Bug Fixes + +* proxy provider style with two-columns ([1e2d07e](https://github.com/Zephyruso/zashboard/commit/1e2d07ea943f2764e8d0f6c2a324cdb6cd7b9402)) + +## [1.68.0](https://github.com/Zephyruso/zashboard/compare/v1.67.1...v1.68.0) (2025-02-24) + + +### Features + +* two-columns groups for mobile inspired by Clash Dash and Surge ([9e9fc5f](https://github.com/Zephyruso/zashboard/commit/9e9fc5f5d954d9b70055b99c23f03fde134263f5)) + + +### Bug Fixes + +* backend available detect ([0d8db22](https://github.com/Zephyruso/zashboard/commit/0d8db2271bef2aa1da0a4c0a5481c1ac31c5c3ec)) + +## [1.67.1](https://github.com/Zephyruso/zashboard/compare/v1.67.0...v1.67.1) (2025-02-22) + + +### Bug Fixes + +* disable proxy select for loadbalance ([e37f83e](https://github.com/Zephyruso/zashboard/commit/e37f83e1e12fd6a5ce984a55bb2076c585579306)) +* switch for display GLOBAL by mode ([60fc0c3](https://github.com/Zephyruso/zashboard/commit/60fc0c33adb13d7fd0e1c18d6a320fd7d5a8a08b)) + +## [1.67.0](https://github.com/Zephyruso/zashboard/compare/v1.66.0...v1.67.0) (2025-02-21) + + +### Features + +* available proxy count ([163f261](https://github.com/Zephyruso/zashboard/commit/163f261d71d0704d2c44e601530b02af560698b5)) +* customizable min card width ([2958beb](https://github.com/Zephyruso/zashboard/commit/2958beb7c3b5b6e20ddb4d9af00312978a9d0283)) +* hover tip for proxygroup now node ([fb3b58e](https://github.com/Zephyruso/zashboard/commit/fb3b58eaa80e2f201323ae9d4149cf99be33cb29)) +* options for display global in non global mode ([9f2ca36](https://github.com/Zephyruso/zashboard/commit/9f2ca36569ced463957c9e8f5493af5aae475501)) + + +### Bug Fixes + +* options for proxies count ([e0e51af](https://github.com/Zephyruso/zashboard/commit/e0e51af831a62933a2137b7e7230c78d91e2d4f1)) +* proxy provider style ([9eb400b](https://github.com/Zephyruso/zashboard/commit/9eb400b19cfdf905c879616af8e78d2863823dfb)) + +## [1.66.0](https://github.com/Zephyruso/zashboard/compare/v1.65.1...v1.66.0) (2025-02-19) + + +### Features + +* log search history ([091afd4](https://github.com/Zephyruso/zashboard/commit/091afd4ea81782d490458ccaf56880bd1d7a0812)) +* refresh btn for pwa ([791a287](https://github.com/Zephyruso/zashboard/commit/791a28780cc2c2f812de7e5053cf8c143d05e858)) + + +### Bug Fixes + +* global for sing-box ([ae25805](https://github.com/Zephyruso/zashboard/commit/ae25805370970a2faa82f9a7a65cb36ea6f21b5c)) +* icon cache by browser ([b459108](https://github.com/Zephyruso/zashboard/commit/b459108dae76e5329ed8b0cc8031250257e68fc2)) + +## [1.65.1](https://github.com/Zephyruso/zashboard/compare/v1.65.0...v1.65.1) (2025-02-18) + + +### Bug Fixes + +* scrollbar & proxy chains style ([16bdbc4](https://github.com/Zephyruso/zashboard/commit/16bdbc438214bc00be65f57ce3dfd881a3e238b9)) + +## [1.65.0](https://github.com/Zephyruso/zashboard/compare/v1.64.0...v1.65.0) (2025-02-17) + + +### Features + +* hover tip for latency history ([4814e90](https://github.com/Zephyruso/zashboard/commit/4814e905c8c05280513cc7bf9f815d62731ab670)) +* url params disable upgrade core ([cfca43c](https://github.com/Zephyruso/zashboard/commit/cfca43c3fd58a183fc58f595560cc317273735a7)) + + +### Bug Fixes + +* connections details modal ([de18946](https://github.com/Zephyruso/zashboard/commit/de189463ac2359e7260ad3fc5d90dde90b6e6b42)) +* ip details when destinationIP is null ([3d1e4cd](https://github.com/Zephyruso/zashboard/commit/3d1e4cdbdbbe6256265553d675156ab2d5f7a678)) +* rule provider style ([f0dda31](https://github.com/Zephyruso/zashboard/commit/f0dda319db40382c82527814803996fdc6343938)) + +## [1.64.0](https://github.com/Zephyruso/zashboard/compare/v1.63.1...v1.64.0) (2025-02-13) + + +### Features + +* draggable & editable source ip label ([36d19a9](https://github.com/Zephyruso/zashboard/commit/36d19a9aff9f9df6a53039fce3d408fb3a5420db)) + + +### Bug Fixes + +* disable swipe when input focused ([6426052](https://github.com/Zephyruso/zashboard/commit/6426052a9be131a0c032fd8a699160e168002539)) +* placeholder for sourceip & icon ([bbbf52f](https://github.com/Zephyruso/zashboard/commit/bbbf52fe295632dc18b612207f6176ff2c03f6af)) + +## [1.63.1](https://github.com/Zephyruso/zashboard/compare/v1.63.0...v1.63.1) (2025-02-12) + + +### Bug Fixes + +* source ip input & backend version style ([441871c](https://github.com/Zephyruso/zashboard/commit/441871cef532d52787273270e489cd3cf7d3d210)) +* timeout for notification ([2ecbb6b](https://github.com/Zephyruso/zashboard/commit/2ecbb6bd40780266608c4cdc33ba1756ab44aeb8)) + +## [1.63.0](https://github.com/Zephyruso/zashboard/compare/v1.62.1...v1.63.0) (2025-02-10) + + +### Features + +* autocomplete for source ip label ([83640ca](https://github.com/Zephyruso/zashboard/commit/83640ca5e0a334a86202cc379042fb9c72ac6254)) +* switch for core upgrade check ([5ceaa73](https://github.com/Zephyruso/zashboard/commit/5ceaa737bc51d052d71a883ed4db56124d3e76a3)) + + +### Bug Fixes + +* card/table customize style ([8d3a382](https://github.com/Zephyruso/zashboard/commit/8d3a382d6a10f9d21b08f8c176cba1145d905527)) +* grid style ([677abad](https://github.com/Zephyruso/zashboard/commit/677abad58014930d6cadbc68316dd6a861158018)) +* remove deprecated details button ([b06492a](https://github.com/Zephyruso/zashboard/commit/b06492af4b6ec28d403ccc001b0436140b420ec9)) + +## [1.62.1](https://github.com/Zephyruso/zashboard/compare/v1.62.0...v1.62.1) (2025-02-08) + + +### Bug Fixes + +* card customize style ([3cb1e84](https://github.com/Zephyruso/zashboard/commit/3cb1e84bced195d370e049a64e3aed3a188ff4bf)) + +## [1.62.0](https://github.com/Zephyruso/zashboard/compare/v1.61.2...v1.62.0) (2025-02-08) + + +### Features + +* move card/table customization to connections popup ([e372da1](https://github.com/Zephyruso/zashboard/commit/e372da1ed60cd2c2e85fa93b0ccc54cd4da2312d)) +* preset for connection card style ([539e23c](https://github.com/Zephyruso/zashboard/commit/539e23c968d5384f7fb7d8027fb91ae6580875b8)) + + +### Bug Fixes + +* default testurl with https ([a1eb0a4](https://github.com/Zephyruso/zashboard/commit/a1eb0a41f2a63ee2ca469467609033b91bd5eeb8)) +* toast & settings style ([f33a077](https://github.com/Zephyruso/zashboard/commit/f33a077e7386779ec438f59b55a2b0956b7e7635)) + +## [1.61.2](https://github.com/Zephyruso/zashboard/compare/v1.61.1...v1.61.2) (2025-02-08) + + +### Bug Fixes + +* connection details style ([acbcfb2](https://github.com/Zephyruso/zashboard/commit/acbcfb2e2c22460c00147d73be610b539e988670)) +* missing proxies ctrl in some cases ([375a0d4](https://github.com/Zephyruso/zashboard/commit/375a0d4e8b86c87ecb25481cbf8dbeae9cae68ee)) +* show hidden group -> manage hidden group ([4e8fb42](https://github.com/Zephyruso/zashboard/commit/4e8fb42977a1513ef4cb28b0cb09bafb7e04c6c6)) + +## [1.61.1](https://github.com/Zephyruso/zashboard/compare/v1.61.0...v1.61.1) (2025-02-07) + + +### Bug Fixes + +* filter log by time ([0af9828](https://github.com/Zephyruso/zashboard/commit/0af9828c8110340f13dfdd29871adbf8cd90717d)) +* tip position & timeout ([fe8730b](https://github.com/Zephyruso/zashboard/commit/fe8730bb51f780285f2c0c18381e1925ad32410a)) +* two columns style for md screen ([3561fdf](https://github.com/Zephyruso/zashboard/commit/3561fdfb74a2a3ac43ab0fc99d2ed9695d7249aa)) + +## [1.61.0](https://github.com/Zephyruso/zashboard/compare/v1.60.3...v1.61.0) (2025-02-07) + + +### Features + +* display ip asn info in connection details ([bcb96ec](https://github.com/Zephyruso/zashboard/commit/bcb96ecc42d90b89c1b1d4847b4c8dc1315b5b25)) +* update geo button ([144aecc](https://github.com/Zephyruso/zashboard/commit/144aecc922318b9c312dd55cfd1614b551b10889)) + + +### Bug Fixes + +* hide group btn style ([b10804b](https://github.com/Zephyruso/zashboard/commit/b10804b74f3716ee450e2bb180d4bc22908dd234)) + +## [1.60.3](https://github.com/Zephyruso/zashboard/compare/v1.60.2...v1.60.3) (2025-02-06) + + +### Bug Fixes + +* hide proxy group btn position ([bce1642](https://github.com/Zephyruso/zashboard/commit/bce1642cbe4ff5a093ee9ffa192a4ec245e1761f)) + +## [1.60.2](https://github.com/Zephyruso/zashboard/compare/v1.60.1...v1.60.2) (2025-02-05) + + +### Bug Fixes + +* autocomplete for setup ([18417b5](https://github.com/Zephyruso/zashboard/commit/18417b5e844ba3bac40f8c592f62371927c16826)) +* hide connections ([8065ebd](https://github.com/Zephyruso/zashboard/commit/8065ebdbd5d4da1b23e2caa8d215c6ea57a0ede7)) +* regex search for logs ([3d1d1b9](https://github.com/Zephyruso/zashboard/commit/3d1d1b98a4119b1c5e600e04ab5e9dcd1a899363)) +* rule card style for sing-box 1.11.0 ([669cf5a](https://github.com/Zephyruso/zashboard/commit/669cf5abf270e69ff59ef66aa237812d0aa3c819)) + +## [1.60.1](https://github.com/Zephyruso/zashboard/compare/v1.60.0...v1.60.1) (2025-01-26) + + +### Bug Fixes + +* connections count ([2f36311](https://github.com/Zephyruso/zashboard/commit/2f36311e216b47ee844860b753dbd2c1e2f0724c)) +* rule card & input style ([5498644](https://github.com/Zephyruso/zashboard/commit/549864497bc43ea9ef60a4508a6536b48397db22)) + +## [1.60.0](https://github.com/Zephyruso/zashboard/compare/v1.59.0...v1.60.0) (2025-01-25) + + +### Features + +* label for backend ([7655df3](https://github.com/Zephyruso/zashboard/commit/7655df34f82d722139b8e96e54b7ca602210fe86)) +* make group hidden by manual ([e903d7b](https://github.com/Zephyruso/zashboard/commit/e903d7b838e6d20da14566bf8a54f6f5a74c1ebe)) +* setting for swipe in tabs ([e003184](https://github.com/Zephyruso/zashboard/commit/e003184ea9095679226183f9ab181eb4cd7d8fd0)) + + +### Bug Fixes + +* icon incorrect ([19db7c8](https://github.com/Zephyruso/zashboard/commit/19db7c85eab1cb9c4f8b5aff6db2f3e75a781648)) +* move proxies settings to modal ([2bdc79c](https://github.com/Zephyruso/zashboard/commit/2bdc79ce105bcdeb0e8b848a6ef239d68f1cad5e)) +* style ([e7a97c1](https://github.com/Zephyruso/zashboard/commit/e7a97c15e688a1fd9de3d4b998116974e2cb9421)) +* style for loadbalance ([51d34f7](https://github.com/Zephyruso/zashboard/commit/51d34f7e6db8d9e9bb946bacbcc84a7f43d1b59c)) + +## [1.59.0](https://github.com/Zephyruso/zashboard/compare/v1.58.0...v1.59.0) (2025-01-24) + + +### Features + +* swipe for tabs ([5ebcf75](https://github.com/Zephyruso/zashboard/commit/5ebcf759ee0403874a84db5502977843487477a5)) + + +### Bug Fixes + +* ctrls style ([36653fc](https://github.com/Zephyruso/zashboard/commit/36653fc91a17068010754bd3317311e809e6d567)) +* import settings for ios ([a376f3f](https://github.com/Zephyruso/zashboard/commit/a376f3f6672060c8018e5e7ee57b3ca7f0538374)) +* rename quick filter -> hide connections ([477fdef](https://github.com/Zephyruso/zashboard/commit/477fdef1f4142a046cdfa67f8b274cd0e1c5a2b6)) +* truncate connection card ([feda74b](https://github.com/Zephyruso/zashboard/commit/feda74bb58348b3d78deb4485fdffc478d9bfd2d)) + +## [1.58.0](https://github.com/Zephyruso/zashboard/compare/v1.57.0...v1.58.0) (2025-01-23) + + +### Features + +* search for proxies ([f28cc86](https://github.com/Zephyruso/zashboard/commit/f28cc86fa45f48fef339dacbd05ab58755089199)) +* unify style for ctrl component ([740a9f7](https://github.com/Zephyruso/zashboard/commit/740a9f7cc8dbe3fdc7f77d93677593139bd8ff37)) + + +### Bug Fixes + +* import settings failed ([6757609](https://github.com/Zephyruso/zashboard/commit/6757609295831e06ed91fb178220e5202c817ea2)) + +## [1.57.0](https://github.com/Zephyruso/zashboard/compare/v1.56.2...v1.57.0) (2025-01-22) + + +### Features + +* toggle collapse for all ([652f1da](https://github.com/Zephyruso/zashboard/commit/652f1da0bdb654f4b312ea59a92259e9b5d04d4c)) + + +### Bug Fixes + +* connection card style ([16c1d88](https://github.com/Zephyruso/zashboard/commit/16c1d882cec3b4eb8ccec89caf9e03dfa87239ef)) +* disable swipe when selection and dialog ([7e4b4d1](https://github.com/Zephyruso/zashboard/commit/7e4b4d142e9ecc533917d3a795686a45368cb7c9)) + +## [1.56.2](https://github.com/Zephyruso/zashboard/compare/v1.56.1...v1.56.2) (2025-01-21) + + +### Bug Fixes + +* rules not display & selector latency test ([6f35379](https://github.com/Zephyruso/zashboard/commit/6f35379c011d0b375cc8f823abcce4f3b6f24b36)) + +## [1.56.1](https://github.com/Zephyruso/zashboard/compare/v1.56.0...v1.56.1) (2025-01-20) + + +### Bug Fixes + +* get latency from now ([4268255](https://github.com/Zephyruso/zashboard/commit/4268255f2cfa1f0b64804397804d07cac5d301e4)) + +## [1.56.0](https://github.com/Zephyruso/zashboard/compare/v1.55.2...v1.56.0) (2025-01-20) + + +### Features + +* independent latency test ([635a168](https://github.com/Zephyruso/zashboard/commit/635a168c04eb2f906c1199015faf1e2b858e9702)) + + +### Bug Fixes + +* ip check privacy ([2d66a61](https://github.com/Zephyruso/zashboard/commit/2d66a61e58b2182e6120919686946fabb02b8c7b)) + +## [1.55.2](https://github.com/Zephyruso/zashboard/compare/v1.55.1...v1.55.2) (2025-01-20) + + +### Bug Fixes + +* disable swipe when dnd ([b8c3eb9](https://github.com/Zephyruso/zashboard/commit/b8c3eb979e4acb5d577b70532abf92af4dd68205)) +* proxies ctrl style in mobile ([56c69ce](https://github.com/Zephyruso/zashboard/commit/56c69ce2b1a5690cb9a7b0ce0bed84db7b0755f5)) + +## [1.55.1](https://github.com/Zephyruso/zashboard/compare/v1.55.0...v1.55.1) (2025-01-19) + + +### Bug Fixes + +* proxies ctrl style ([90cafdf](https://github.com/Zephyruso/zashboard/commit/90cafdfd2812bd62d92d33b075dc133aaa1bbc03)) +* rule provider & notification style ([c8bb270](https://github.com/Zephyruso/zashboard/commit/c8bb2703b1d102e8a4b33adc277d53b9df2f31ef)) + +## [1.55.0](https://github.com/Zephyruso/zashboard/compare/v1.54.0...v1.55.0) (2025-01-19) + + +### Features + +* latency test for all proxies ([43b04b3](https://github.com/Zephyruso/zashboard/commit/43b04b3944b7a220a1a7616ec79fe5a8ad70ba43)) +* tip for large group latency test ([be7db20](https://github.com/Zephyruso/zashboard/commit/be7db20a9efe35ccbcdfe86e48e0b06ada0f6f05)) + + +### Bug Fixes + +* catch cors icon ([c15bedb](https://github.com/Zephyruso/zashboard/commit/c15bedba41b1ae2467b69ff1ea3670f96fed5c41)) +* icon shrink for macos ([3a0646f](https://github.com/Zephyruso/zashboard/commit/3a0646f662e450c4812e1b1aa62b230617a439ba)) +* loading with rollup && latency test ([a0aa905](https://github.com/Zephyruso/zashboard/commit/a0aa905b2e16cc0967e9890b90ce76ce8c9c54b5)) +* ru.ts for Total Connections Overview ([#195](https://github.com/Zephyruso/zashboard/issues/195)) ([f9f2bcf](https://github.com/Zephyruso/zashboard/commit/f9f2bcf6332cf24b70cef10d429ae1a9bddd9231)) +* swipe threshold ([9933c6e](https://github.com/Zephyruso/zashboard/commit/9933c6e3f40aa4a7482d3fe866a0cb3874735855)) + +## [1.54.0](https://github.com/Zephyruso/zashboard/compare/v1.53.1...v1.54.0) (2025-01-18) + + +### Features + +* latency test animation ([207a139](https://github.com/Zephyruso/zashboard/commit/207a139154af485ba2cb1d5c656ac484da8504f1)) +* swipe page animation ([dd7fe3d](https://github.com/Zephyruso/zashboard/commit/dd7fe3d7c50fed87338c1bbfb0bf691208fdef86)) +* swipe to switch pages in mobile ([dbf7306](https://github.com/Zephyruso/zashboard/commit/dbf7306a788fcb3d98876dd9253dd57af77044d1)) + + +### Bug Fixes + +* scroll to top for details modal ([da8f837](https://github.com/Zephyruso/zashboard/commit/da8f83730417155e0d9949d873fa390a6fba4b66)) +* unable to switch backend ([9fe51e5](https://github.com/Zephyruso/zashboard/commit/9fe51e58705a4aa0e644186203fc59b8a517a479)) + +## [1.53.1](https://github.com/Zephyruso/zashboard/compare/v1.53.0...v1.53.1) (2025-01-17) + + +### Bug Fixes + +* break all for proxy name ([26281c1](https://github.com/Zephyruso/zashboard/commit/26281c1542f953bdeea611c667370fd932364ce8)) + +## [1.53.0](https://github.com/Zephyruso/zashboard/compare/v1.52.1...v1.53.0) (2025-01-17) + + +### Features + +* resizeable table columns ([c781105](https://github.com/Zephyruso/zashboard/commit/c781105118cd33cecaed5fda0a4ad015a5439956)) +* sortable backend list ([f556aa4](https://github.com/Zephyruso/zashboard/commit/f556aa47a4e35a0fc84f12030bc72515440d5c01)) + + +### Bug Fixes + +* connections ctrl style ([4743daf](https://github.com/Zephyruso/zashboard/commit/4743daf06a0952afa4b77237ab0d7b3c82dccda5)) +* connections settings style ([03e002a](https://github.com/Zephyruso/zashboard/commit/03e002a4e9efe2e94c826caaf3f7ea52803e8446)) +* proxy name shrink ([300c92c](https://github.com/Zephyruso/zashboard/commit/300c92cb0758df1377468c8d3850a5683a5fa3a8)) + +## [1.52.1](https://github.com/Zephyruso/zashboard/compare/v1.52.0...v1.52.1) (2025-01-16) + + +### Bug Fixes + +* details modal & icon style ([ab67366](https://github.com/Zephyruso/zashboard/commit/ab673667cb5f091808635eb7afbbd92a14aacba1)) + +## [1.52.0](https://github.com/Zephyruso/zashboard/compare/v1.51.0...v1.52.0) (2025-01-16) + + +### Features + +* connection history in overview page ([8c0bfb0](https://github.com/Zephyruso/zashboard/commit/8c0bfb033b2436c298326296de8cbe1843e443c2)) +* display icon in connections ([d6dba90](https://github.com/Zephyruso/zashboard/commit/d6dba903e8e9e07bec226b017803658f1f93758d)) + + +### Bug Fixes + +* ctrls and other style ([55d4a46](https://github.com/Zephyruso/zashboard/commit/55d4a46aa0c17b49e3627db8cd950c4878e759bd)) +* details modal style ([d9f786b](https://github.com/Zephyruso/zashboard/commit/d9f786b95068663dc607238542c54d4647972855)) +* ip check style ([d2f84c5](https://github.com/Zephyruso/zashboard/commit/d2f84c594b55090991ba3fc48252edf4049afe3d)) + +## [1.51.0](https://github.com/Zephyruso/zashboard/compare/v1.50.0...v1.51.0) (2025-01-15) + + +### Features + +* one by one latency test for large group ([6a5aebc](https://github.com/Zephyruso/zashboard/commit/6a5aebce6a67df6afe3c56dc002402d5f58ec687)) +* override speedtesturl with config ([5662cfe](https://github.com/Zephyruso/zashboard/commit/5662cfedd5deaeec962f53a7f3738798127af12b)) +* setting for display statistics in collapsed sidebar ([ddbc273](https://github.com/Zephyruso/zashboard/commit/ddbc273b75830175a2114aab4cbcb49d334fd4c2)) + + +### Bug Fixes + +* dayjs locale ([b806e9d](https://github.com/Zephyruso/zashboard/commit/b806e9dfe8361c7a90b0bf3fb0c9006c7705ebca)) +* details modals & proxies ctrl style ([0ca01e5](https://github.com/Zephyruso/zashboard/commit/0ca01e52bd903842c54c25ea189c8419ef014224)) +* grouped row click ([089861c](https://github.com/Zephyruso/zashboard/commit/089861cee8753ee50ac5cd205c4d05d28af809ee)) +* latency test for proxy node card ([1f94549](https://github.com/Zephyruso/zashboard/commit/1f94549d3c9e640a3c3d1bd88ff640b9925fc6e9)) +* missing destinationPort in connection card ([a7c461a](https://github.com/Zephyruso/zashboard/commit/a7c461ad260f56b1ab9dc6b4e993071d2d3b8502)) +* number of proxies node ([785d56c](https://github.com/Zephyruso/zashboard/commit/785d56cc67b63d8737b0d65273b30e33c5fbb9e7)) +* tip for sidebar collapsed ([5f90287](https://github.com/Zephyruso/zashboard/commit/5f90287178fc2ae70bc05d98d05114ecc3009668)) + +## [1.50.0](https://github.com/Zephyruso/zashboard/compare/v1.49.1...v1.50.0) (2025-01-14) + + +### Features + +* display details modal for table & card click ([e9b3210](https://github.com/Zephyruso/zashboard/commit/e9b3210e61235cf18582e4e81b6a29ba30a33b53)) +* latency test when right click ([f801e41](https://github.com/Zephyruso/zashboard/commit/f801e4111768ca4503925f1fe08df03bf5bdc421)) + + +### Bug Fixes + +* btm-nav & ipcheck style ([238ca13](https://github.com/Zephyruso/zashboard/commit/238ca1396b542343e5b72c05371b71176ab0ca27)) +* display sniffhost for host ([c7096cc](https://github.com/Zephyruso/zashboard/commit/c7096ccccd8bc0e022dc31aec92b8f3eff9be10b)) +* ip filter ([0df5b68](https://github.com/Zephyruso/zashboard/commit/0df5b68fb0120f51fb685109f4a7f9be3a0def0f)) +* latency test timeout ([b80f07e](https://github.com/Zephyruso/zashboard/commit/b80f07e753b4b6bd94f74df03a5f9868d8c27c2b)) +* style for unavailable proxy ([2091ccf](https://github.com/Zephyruso/zashboard/commit/2091ccf815b1b203ea4edb080ac4b6f10eecff87)) + +## [1.49.1](https://github.com/Zephyruso/zashboard/compare/v1.49.0...v1.49.1) (2025-01-12) + + +### Bug Fixes + +* latency test failed ([8d1e31b](https://github.com/Zephyruso/zashboard/commit/8d1e31b54ba537561b7a09ccdcf1b3b23b8f0033)) +* parse of ipip.net ([711fef9](https://github.com/Zephyruso/zashboard/commit/711fef9b279563ad2b92fee863a4dd398dca046d)) +* select blink in firefox ([040b792](https://github.com/Zephyruso/zashboard/commit/040b7925880c09e3aa2c4b521572e3d62264543c)) +* tooltip style ([034b18d](https://github.com/Zephyruso/zashboard/commit/034b18db6d3da7bf50bed030f46b8726db6b20c0)) + +## [1.49.0](https://github.com/Zephyruso/zashboard/compare/v1.48.1...v1.49.0) (2025-01-11) + + +### Features + +* ipip.net ([4816cd8](https://github.com/Zephyruso/zashboard/commit/4816cd8a0f28a1fbc9671e02433cc32ba6137f0a)) +* sniff host ([6a4b091](https://github.com/Zephyruso/zashboard/commit/6a4b09144a12aca14e2a5aaaf5e48a8436bb5e1a)) + + +### Bug Fixes + +* ws performance ([ac5ad2e](https://github.com/Zephyruso/zashboard/commit/ac5ad2e26eb7b9ff2f5b367a6b3a44c0ac33a62b)) + +## [1.48.1](https://github.com/Zephyruso/zashboard/compare/v1.48.0...v1.48.1) (2025-01-10) + + +### Bug Fixes + +* group test timeout ([b52f83f](https://github.com/Zephyruso/zashboard/commit/b52f83fe7264057c6bdad803822a822f309a1a0f)) +* tip font ([c48c315](https://github.com/Zephyruso/zashboard/commit/c48c3158d6f4e9a680ab2727d8fbe2ec3824514d)) +* virtual scroller estimate size ([034fe13](https://github.com/Zephyruso/zashboard/commit/034fe132d10752c6fc4181b30c312b7441ee8432)) + +## [1.48.0](https://github.com/Zephyruso/zashboard/compare/v1.47.0...v1.48.0) (2025-01-08) + + +### Features + +* nav for mobile & network quic ([a38ac31](https://github.com/Zephyruso/zashboard/commit/a38ac310c92250bd1e5143f22d3af4301d9b7f36)) + + +### Bug Fixes + +* transparent & network type ([dec2173](https://github.com/Zephyruso/zashboard/commit/dec21733eb18510c0e68adf623a28aec75241848)) + +## [1.47.0](https://github.com/Zephyruso/zashboard/compare/v1.46.2...v1.47.0) (2025-01-07) + + +### Features + +* add icon caching and clear cache functionality ([#146](https://github.com/Zephyruso/zashboard/issues/146)) ([1226600](https://github.com/Zephyruso/zashboard/commit/122660054151c0eb58dff9ed17f4e3aac4e354fd)) +* dns query detail ([1bdcd15](https://github.com/Zephyruso/zashboard/commit/1bdcd153d9c903e00f569a28b587d23b9a02301e)) +* proxy chain direction ([ab5a621](https://github.com/Zephyruso/zashboard/commit/ab5a62186792e838358604f13b260a0a34a0a972)) + + +### Bug Fixes + +* dots tooltip style ([6e32a16](https://github.com/Zephyruso/zashboard/commit/6e32a1693db62e5917cfd6b339ff82d5a2056eb0)) +* opacity style ([321df18](https://github.com/Zephyruso/zashboard/commit/321df188df3129be88246560bac7404653dd8ed9)) +* replace -> ([845d015](https://github.com/Zephyruso/zashboard/commit/845d015a7d5f51fcff3b1c12eb9e41985a2f11f3)) +* style for rule ([201dcf5](https://github.com/Zephyruso/zashboard/commit/201dcf5ec1f5b1edc36b42f572ca9585f9cd3771)) + +## [1.46.2](https://github.com/Zephyruso/zashboard/compare/v1.46.1...v1.46.2) (2025-01-06) + + +### Bug Fixes + +* proxy group & rule style ([72e17ad](https://github.com/Zephyruso/zashboard/commit/72e17ade6819ac21010d42811fa126898f8b8d8d)) + +## [1.46.1](https://github.com/Zephyruso/zashboard/compare/v1.46.0...v1.46.1) (2025-01-06) + + +### Bug Fixes + +* rule style ([8a64aeb](https://github.com/Zephyruso/zashboard/commit/8a64aebdb4f0feb8b7a851f19f6ebd811b835bf8)) + +## [1.46.0](https://github.com/Zephyruso/zashboard/compare/v1.45.1...v1.46.0) (2025-01-06) + + +### Features + +* allow lan & auto upgrade core ([1a9c86c](https://github.com/Zephyruso/zashboard/commit/1a9c86cd424b798292ab06980b5276dc44706d25)) + + +### Bug Fixes + +* pwa style ([3faea83](https://github.com/Zephyruso/zashboard/commit/3faea836cb26f3903b4b3dd501adf98d9376f267)) +* style for icon ([05eab9d](https://github.com/Zephyruso/zashboard/commit/05eab9df61391df085847820d99dc5916399761b)) + +## [1.45.1](https://github.com/Zephyruso/zashboard/compare/v1.45.0...v1.45.1) (2025-01-06) + + +### Bug Fixes + +* rule & proxy group style ([a052679](https://github.com/Zephyruso/zashboard/commit/a052679e72f7cb5e0f5526531857fc379a271c66)) +* tooltip style ([f90ba9a](https://github.com/Zephyruso/zashboard/commit/f90ba9a853249597c1fa0f022101ae25e7a129ce)) + +## [1.45.0](https://github.com/Zephyruso/zashboard/compare/v1.44.0...v1.45.0) (2025-01-06) + + +### Features + +* select proxy by click dot ([296e959](https://github.com/Zephyruso/zashboard/commit/296e959ceda68b803701840de10390e13c88c28e)) + + +### Bug Fixes + +* connections card type ([a1627fa](https://github.com/Zephyruso/zashboard/commit/a1627fab815d4b0854d1546b52f2e23d916194d5)) +* ctrl style ([2d22041](https://github.com/Zephyruso/zashboard/commit/2d22041ebd4013b70547dec0d78f7569dcfb7670)) +* rules ctrl style ([9522770](https://github.com/Zephyruso/zashboard/commit/9522770a260b7369fabd8ddacb63f5dfb07cc05b)) + +## [1.44.0](https://github.com/Zephyruso/zashboard/compare/v1.43.0...v1.44.0) (2025-01-03) + + +### Features + +* upload image for bg ([966d5e9](https://github.com/Zephyruso/zashboard/commit/966d5e9c869a0e044ad31489b08ebf5334761c5f)) + + +### Bug Fixes + +* ru.ts ([#143](https://github.com/Zephyruso/zashboard/issues/143)) ([cae45f4](https://github.com/Zephyruso/zashboard/commit/cae45f46ac87dec5308b08d3e49417c75d1a599f)) + +## [1.43.0](https://github.com/Zephyruso/zashboard/compare/v1.42.0...v1.43.0) (2025-01-02) + + +### Features + +* range input for transparent ([5cd7ed9](https://github.com/Zephyruso/zashboard/commit/5cd7ed9bbdb504d52ccea9c934a0e68af5cbe88f)) +* size for icon ([0b7c54e](https://github.com/Zephyruso/zashboard/commit/0b7c54ed603d94cb076b919410b940568caafa34)) + + +### Bug Fixes + +* close all btn ([fa4f31c](https://github.com/Zephyruso/zashboard/commit/fa4f31ce6cf6fb4b230a085c04ac1d75d39595ab)) +* style for transparent ([8f9ba4b](https://github.com/Zephyruso/zashboard/commit/8f9ba4b58c71f6cba204cff15df3aaabd838d360)) + +## [1.42.0](https://github.com/Zephyruso/zashboard/compare/v1.41.0...v1.42.0) (2024-12-31) + + +### Features + +* options for transparent ([3c4b986](https://github.com/Zephyruso/zashboard/commit/3c4b986ffbf207a94b0c1e787fb177e47c1dcb9a)) +* search for rules ([8f7c67e](https://github.com/Zephyruso/zashboard/commit/8f7c67e3b1fa09c2bdcf1ac4dbdfb7d58568d129)) + + +### Bug Fixes + +* connections card & overview style ([690edfc](https://github.com/Zephyruso/zashboard/commit/690edfc677a23fd4f81377ac7de906cb8dafdca1)) +* desitination ([21a5990](https://github.com/Zephyruso/zashboard/commit/21a5990738f9987c3c7fa62b6f37408d0a90cc21)) +* rule size ([917ecc2](https://github.com/Zephyruso/zashboard/commit/917ecc203b26987779fc94541cdf006e240859ae)) +* style ([c969408](https://github.com/Zephyruso/zashboard/commit/c969408173e9fea57eab884f67bbb08580b4b344)) + +## [1.41.0](https://github.com/Zephyruso/zashboard/compare/v1.40.0...v1.41.0) (2024-12-31) + + +### Features + +* option for hidden groups ([4da005e](https://github.com/Zephyruso/zashboard/commit/4da005ed8f85dc3453c41ffffa6dec6c7b28d2fc)) + + +### Bug Fixes + +* charts style ([5ca830e](https://github.com/Zephyruso/zashboard/commit/5ca830ed0d0c7cb67bf0b360048728927e602600)) +* connections ctrl style ([9f5cd96](https://github.com/Zephyruso/zashboard/commit/9f5cd965558ffb11ccfe8afa606d38b3c5253b9c)) +* overview style ([7c33b18](https://github.com/Zephyruso/zashboard/commit/7c33b18f6ff3a8fdacdec1e2d7e25ae96d55041d)) + +## [1.40.0](https://github.com/Zephyruso/zashboard/compare/v1.39.2...v1.40.0) (2024-12-31) + + +### Features + +* option for split overview page ([5076f4e](https://github.com/Zephyruso/zashboard/commit/5076f4e7b0478f94f374ad52098cab98e7dcee8d)) +* sort direction for card ([17e3c43](https://github.com/Zephyruso/zashboard/commit/17e3c43de8b14558f152f0eda0caebd34dcb2da9)) + + +### Bug Fixes + +* default pages ([fffa435](https://github.com/Zephyruso/zashboard/commit/fffa435ee8475864964701256b8ac55d436543c7)) +* inner sourceip filter ([215031d](https://github.com/Zephyruso/zashboard/commit/215031d4688a8689e4bb4ad495e774e62101cb6f)) + +## [1.39.2](https://github.com/Zephyruso/zashboard/compare/v1.39.1...v1.39.2) (2024-12-31) + + +### Bug Fixes + +* remove default img ([668c00a](https://github.com/Zephyruso/zashboard/commit/668c00ad0d38df2c37a0f858b5a7e54abdf194d7)) + +## [1.39.1](https://github.com/Zephyruso/zashboard/compare/v1.39.0...v1.39.1) (2024-12-31) + +### Bug Fixes + +* close conn for card ([bcc7e82](https://github.com/Zephyruso/zashboard/commit/bcc7e82b1709eda1a30ed1f6ac13baf53d280dee)) + +## [1.39.0](https://github.com/Zephyruso/zashboard/compare/v1.38.0...v1.39.0) (2024-12-31) + + +### Features + +* bing bg ([7b09ab1](https://github.com/Zephyruso/zashboard/commit/7b09ab17732b9065178dba8de6157a5d777daffa)) + + +### Bug Fixes + +* default theme -> auto switch theme ([0e19ac6](https://github.com/Zephyruso/zashboard/commit/0e19ac6a18f28ea407c3f594e0a87175d4785fe8)) +* settings for overview & sourceip is null ([539de88](https://github.com/Zephyruso/zashboard/commit/539de88fad71a33c5ed62971d0057ed51e687aed)) +* style ([56289dc](https://github.com/Zephyruso/zashboard/commit/56289dc3b1962557e6158d1d967bef9287a86edc)) + +## [1.38.0](https://github.com/Zephyruso/zashboard/compare/v1.37.0...v1.38.0) (2024-12-30) + + +### Features + +* customizable connection card ([b7a67bd](https://github.com/Zephyruso/zashboard/commit/b7a67bdd8a43979a4ccbe4eac51d5bdd2c99b82c)) +* latency of cloudflare ([0da167d](https://github.com/Zephyruso/zashboard/commit/0da167def4a0c2967b67d9bb004d381ab6ad1e46)) +* overview page ([f1eaf27](https://github.com/Zephyruso/zashboard/commit/f1eaf2778975c0bf9bddde91f22a96327d363b09)) + + +### Bug Fixes + +* connections card style ([a11823e](https://github.com/Zephyruso/zashboard/commit/a11823ec50926e25e9d602bc4dc3afd0de93632c)) +* rules icon ([03ef6e0](https://github.com/Zephyruso/zashboard/commit/03ef6e0b535b02b0751ac16903bf3cf3a969be9d)) +* subscription info is null & overview page ([1f17bba](https://github.com/Zephyruso/zashboard/commit/1f17bba66a289974aaacba443f003df478ba7156)) + +## [1.37.0](https://github.com/Zephyruso/zashboard/compare/v1.36.0...v1.37.0) (2024-12-30) + + +### Features + +* carousel ([b8689c6](https://github.com/Zephyruso/zashboard/commit/b8689c648cab18da6aab68e66139f208bcfc4ae1)) +* netease -> openai ([3521796](https://github.com/Zephyruso/zashboard/commit/352179690a3e19ea8e32ba26b8d5c5d168e96fad)) +* overview ([eba9dff](https://github.com/Zephyruso/zashboard/commit/eba9dff740e1c75aff5e467c14b962bb8d88e166)) +* sticky thead ([0f91378](https://github.com/Zephyruso/zashboard/commit/0f91378f177d9fbb7736f64bb0898b54e9c3809f)) + +## [1.36.0](https://github.com/Zephyruso/zashboard/compare/v1.35.0...v1.36.0) (2024-12-29) + + +### Features + +* connection status ([f9a05b0](https://github.com/Zephyruso/zashboard/commit/f9a05b0ded6d022ce6a26fd6f89d098e719b4187)) +* ipcheck ([7c62958](https://github.com/Zephyruso/zashboard/commit/7c629584a96989adde91cdb209b2f4139f2a85ef)) + +## [1.35.0](https://github.com/Zephyruso/zashboard/compare/v1.34.0...v1.35.0) (2024-12-27) + + +### Features + +* settings for proxy card size ([0e19b2d](https://github.com/Zephyruso/zashboard/commit/0e19b2dd04b3f6fd10aee4833d1f2b5798eac10e)) + + +### Bug Fixes + +* remove reload cfgs for sing-box ([4bad656](https://github.com/Zephyruso/zashboard/commit/4bad656eb0e35fa254bcebf6638eab88ea65c2be)) + +## [1.34.0](https://github.com/Zephyruso/zashboard/compare/v1.33.1...v1.34.0) (2024-12-27) + + +### Features + +* settings for table size ([61ffe06](https://github.com/Zephyruso/zashboard/commit/61ffe0648392060ae4572935c019085fced7b0b6)) + + +### Bug Fixes + +* bigger proxy card ([af238be](https://github.com/Zephyruso/zashboard/commit/af238becf57553a09c66e505be8c94d1b29dbbc0)) +* chart style ([d036723](https://github.com/Zephyruso/zashboard/commit/d036723c692f77b2eadcd927c239c885684d9d99)) +* metacubex logo ([73c56f9](https://github.com/Zephyruso/zashboard/commit/73c56f9b49fa7d85dc2377ef919f31489ac0f35f)) + +## [1.33.1](https://github.com/Zephyruso/zashboard/compare/v1.33.0...v1.33.1) (2024-12-26) + + +### Bug Fixes + +* rule provider style ([50dcc16](https://github.com/Zephyruso/zashboard/commit/50dcc16c3059c3e5c9a1ea39ecdd61d4941e3bc1)) +* settings style ([ba9483d](https://github.com/Zephyruso/zashboard/commit/ba9483d4d144ac51176169f96a5c2e3a21fafcf0)) +* sort by latency for proxy group ([641d324](https://github.com/Zephyruso/zashboard/commit/641d324f88d9a02317c5ae0466b5c205712aab7d)) + +## [1.33.0](https://github.com/Zephyruso/zashboard/compare/v1.32.1...v1.33.0) (2024-12-26) + + +### Features + +* ports setting for mihomo ([eedcf06](https://github.com/Zephyruso/zashboard/commit/eedcf06383b3ce84097e3a270c4a5ef4967bf8aa)) + + +### Bug Fixes + +* api for reloadcfg & icon style ([eb17dae](https://github.com/Zephyruso/zashboard/commit/eb17daeca6b6af4b82aa2b50beaf5d6ad81a3063)) +* bigger latency tag ([452243f](https://github.com/Zephyruso/zashboard/commit/452243f4bd6772fa1d4f9c75ff3277598ee70378)) +* connections ctrl style ([f28d404](https://github.com/Zephyruso/zashboard/commit/f28d404d7ea57661f498b0cffdbaad251664919a)) +* missing translate for russian ([7a03b7b](https://github.com/Zephyruso/zashboard/commit/7a03b7b62c4f46ea663f4ffc4f04b39eb80dc294)) +* settings style ([16e893a](https://github.com/Zephyruso/zashboard/commit/16e893a6c67c0a81636bbd8c3285716251544683)) + +## [1.32.1](https://github.com/Zephyruso/zashboard/compare/v1.32.0...v1.32.1) (2024-12-25) + + +### Bug Fixes + +* style ([50f9324](https://github.com/Zephyruso/zashboard/commit/50f932464f3d50e53cf1969a6f1918114efa0e88)) + +## [1.32.0](https://github.com/Zephyruso/zashboard/compare/v1.31.0...v1.32.0) (2024-12-25) + + +### Features + +* add ru lang ([#108](https://github.com/Zephyruso/zashboard/issues/108)) ([e28c9b1](https://github.com/Zephyruso/zashboard/commit/e28c9b19805c0ca27d4bfbe9d4c54690b2f69d89)) +* two line proxy card ([f942cd3](https://github.com/Zephyruso/zashboard/commit/f942cd36108913ca2c67a34cc56bba7bd5ce5e18)) + + +### Bug Fixes + +* ru.ts ([#109](https://github.com/Zephyruso/zashboard/issues/109)) ([f4d4e68](https://github.com/Zephyruso/zashboard/commit/f4d4e68b963de7229f3a9150501e59178a50ead3)) +* style ([3479ab6](https://github.com/Zephyruso/zashboard/commit/3479ab65b565011f593074938c650c3ab163e994)) +* update quick-filter-regex ([#110](https://github.com/Zephyruso/zashboard/issues/110)) ([8efb37a](https://github.com/Zephyruso/zashboard/commit/8efb37a6da61b5b158d062ddb31a04c29d718efb)) +* xs element -> sm element ([d0a160d](https://github.com/Zephyruso/zashboard/commit/d0a160d90d5053aad5be1e38f4ab2cf8282472b8)) + +## [1.31.0](https://github.com/Zephyruso/zashboard/compare/v1.30.0...v1.31.0) (2024-12-24) + + +### Features + +* auto switch backend ([b153fd0](https://github.com/Zephyruso/zashboard/commit/b153fd021a1b1b376b76bd11e4cac740aa4fe0bc)) +* dual mode for cdn fonts ([ba0fdc7](https://github.com/Zephyruso/zashboard/commit/ba0fdc7ba8b72cef28502cc072422f12ffc1d3a4)) +* ipv6 test ([a654645](https://github.com/Zephyruso/zashboard/commit/a654645321ec89a131c9571d26ab3bea153bcfaa)) +* tip for auto switch ([573f11f](https://github.com/Zephyruso/zashboard/commit/573f11f6c186c90a0566d537c731dc8cb9a409ff)) + + +### Bug Fixes + +* //version ([50a269d](https://github.com/Zephyruso/zashboard/commit/50a269ddeaa2802f8c976428738a4e6f14e41931)) +* backend available timeout ([756f99f](https://github.com/Zephyruso/zashboard/commit/756f99f62ab896b1e7d3bf4b0bd5e53a0ed7de4f)) +* rule card & ctrls style ([529fe0e](https://github.com/Zephyruso/zashboard/commit/529fe0e478c699cd328d96f49e574d73412978bf)) + +## [1.30.0](https://github.com/Zephyruso/zashboard/compare/v1.29.4...v1.30.0) (2024-12-19) + + +### Features + +* backend version in settings ([04dea21](https://github.com/Zephyruso/zashboard/commit/04dea213af06e5cd52a786c57e4404a5b5b7b80a)) +* import configs in setup page & rule style ([1e816e4](https://github.com/Zephyruso/zashboard/commit/1e816e45879f749c7467e8408e87a1f62a9cdaad)) +* index for rules provider ([a54d48f](https://github.com/Zephyruso/zashboard/commit/a54d48f01bec8b89058a5c60972cf80983acf5b8)) +* new rule card style ([6313cc8](https://github.com/Zephyruso/zashboard/commit/6313cc82751ae46e148d27452f3782ad785963ce)) + + +### Bug Fixes + +* backend version check & provider collapse title ([151872a](https://github.com/Zephyruso/zashboard/commit/151872a28b4d741f3c623a7cb09a0b3dc64fd13d)) +* options label for old safari ([8631a8c](https://github.com/Zephyruso/zashboard/commit/8631a8cd65e1828150a82c36479157c6d675fbf5)) +* tooltip & font style ([fa36b55](https://github.com/Zephyruso/zashboard/commit/fa36b55b619433edfd4b3b647da643210002631c)) + +## [1.29.4](https://github.com/Zephyruso/zashboard/compare/v1.29.3...v1.29.4) (2024-12-18) + + +### Bug Fixes + +* backend settings layout ([a0febbb](https://github.com/Zephyruso/zashboard/commit/a0febbb181e9f81f0a92410766e4e7815ce600ce)) +* transition for preview ([6dfa379](https://github.com/Zephyruso/zashboard/commit/6dfa3796f27b24f42519b88aef36e89b6fc7ab0e)) + +## [1.29.3](https://github.com/Zephyruso/zashboard/compare/v1.29.2...v1.29.3) (2024-12-17) + + +### Bug Fixes + +* core version check ([947df26](https://github.com/Zephyruso/zashboard/commit/947df26c9c009637283a75b500a794e686ba379d)) +* dns query & collpase style ([4b00b29](https://github.com/Zephyruso/zashboard/commit/4b00b29f23d909771463dd83c7f9bf4d58186572)) + +## [1.29.2](https://github.com/Zephyruso/zashboard/compare/v1.29.1...v1.29.2) (2024-12-17) + + +### Bug Fixes + +* card style for mobile in two columns ([cef601b](https://github.com/Zephyruso/zashboard/commit/cef601b212b226ddaa31b288302bf679531e552b)) +* latency tag & collapse style' ([e041f7b](https://github.com/Zephyruso/zashboard/commit/e041f7b66aa933a671f3b019419b89893a7a6840)) + +## [1.29.1](https://github.com/Zephyruso/zashboard/compare/v1.29.0...v1.29.1) (2024-12-17) + + +### Bug Fixes + +* performance ([964a733](https://github.com/Zephyruso/zashboard/commit/964a7330aa6aa8f9c01c8b228afc23315e4d59f0)) + +## [1.29.0](https://github.com/Zephyruso/zashboard/compare/v1.28.3...v1.29.0) (2024-12-17) + + +### Features + +* core update check & fix ([8694a3a](https://github.com/Zephyruso/zashboard/commit/8694a3ae85cf6a7ad81fd30bbc1f37ccdd96c7b1)) + + +### Bug Fixes + +* render block by cdn ([a00a071](https://github.com/Zephyruso/zashboard/commit/a00a07152f2d05ca1019c96bb92d767524d7ffe9)) + +## [1.28.3](https://github.com/Zephyruso/zashboard/compare/v1.28.2...v1.28.3) (2024-12-16) + + +### Bug Fixes + +* proxies ctrl style ([f437696](https://github.com/Zephyruso/zashboard/commit/f4376960b330c372ac286c1be19c2ece23dc537b)) +* proxy group style ([cd0291d](https://github.com/Zephyruso/zashboard/commit/cd0291d7d9148322ac3f2b3a145a07ea1bab0771)) + +## [1.28.2](https://github.com/Zephyruso/zashboard/compare/v1.28.1...v1.28.2) (2024-12-16) + + +### Bug Fixes + +* performance ([710a4cb](https://github.com/Zephyruso/zashboard/commit/710a4cb0f24a8a110a3a818f25f717a0f84bfa15)) + +## [1.28.1](https://github.com/Zephyruso/zashboard/compare/v1.28.0...v1.28.1) (2024-12-16) + + +### Bug Fixes + +* proxy group style & regex ip label ([ddb8dfe](https://github.com/Zephyruso/zashboard/commit/ddb8dfe9c55f9c6176898518bdf561ddd54f8779)) + +## [1.28.0](https://github.com/Zephyruso/zashboard/compare/v1.27.2...v1.28.0) (2024-12-16) + + +### Features + +* add `/setup` route for SetupPage ([#88](https://github.com/Zephyruso/zashboard/issues/88)) ([684ee20](https://github.com/Zephyruso/zashboard/commit/684ee2062b2ba1dff8b18af1f5fc2234b024c23c)) +* CNAME ([f4d87c3](https://github.com/Zephyruso/zashboard/commit/f4d87c3e4b93292f0c2de1262ab63d1edda65852)) +* font & charts & proxy group style ([ec438ed](https://github.com/Zephyruso/zashboard/commit/ec438edb112ee074aaab3c53f89bf335754a745b)) + + +### Bug Fixes + +* lazy new countup ([96de66b](https://github.com/Zephyruso/zashboard/commit/96de66b2fedc668a41a89d6ecf850a920d22bb4f)) +* router ([a9a0202](https://github.com/Zephyruso/zashboard/commit/a9a0202b1fa054e199b4ca7273a3bff942460a53)) +* start time sort ([7cf335a](https://github.com/Zephyruso/zashboard/commit/7cf335a8f979e6eb22c753b226911fcd569272ad)) + +## [1.27.2](https://github.com/Zephyruso/zashboard/compare/v1.27.1...v1.27.2) (2024-12-14) + + +### Bug Fixes + +* cdn fonts ([c8fb7b1](https://github.com/Zephyruso/zashboard/commit/c8fb7b17a49740ba2c0797fcdb2091ead98985a7)) +* cfg for rolling effect ([4ff2987](https://github.com/Zephyruso/zashboard/commit/4ff2987d5bd9bdd02f9eb337d35e132c354f156f)) +* style ([b788a8e](https://github.com/Zephyruso/zashboard/commit/b788a8e709e2292f1613ab147c7f2cc7fd64b032)) +* tippy -> tooltip ([9d5f882](https://github.com/Zephyruso/zashboard/commit/9d5f882cc1479bb38b816ce7612f8d288fba1d37)) +* tooltip ([f900fe3](https://github.com/Zephyruso/zashboard/commit/f900fe378bc31edc6ce2b801dd3600d7c6667654)) + +## [1.27.1](https://github.com/Zephyruso/zashboard/compare/v1.27.0...v1.27.1) (2024-12-13) + + +### Bug Fixes + +* fuck you safari ([3a73475](https://github.com/Zephyruso/zashboard/commit/3a7347594a6160c807c2631d4bb02b85944fadf1)) +* log performonce ([f189598](https://github.com/Zephyruso/zashboard/commit/f18959802471a418df1c66f3c26ad9a956271451)) +* performance ([e35e1db](https://github.com/Zephyruso/zashboard/commit/e35e1dbf2bfbcfef2ea3a363594be9ad837ed0a4)) + +## [1.27.0](https://github.com/Zephyruso/zashboard/compare/v1.26.2...v1.27.0) (2024-12-13) + + +### Features + +* DNS query & latency rolling effect cfg ([f7896c5](https://github.com/Zephyruso/zashboard/commit/f7896c57df6e74948c24e08c5c5fca6cc715d1be)) + + +### Bug Fixes + +* latency countup performance ([86ea651](https://github.com/Zephyruso/zashboard/commit/86ea65191f628bd04000fbe8f13118e3026e1651)) + +## [1.26.2](https://github.com/Zephyruso/zashboard/compare/v1.26.1...v1.26.2) (2024-12-12) + + +### Bug Fixes + +* height for pwa ([e848e94](https://github.com/Zephyruso/zashboard/commit/e848e947fc41698361b42eedf27263ef067341d7)) +* padding ([8443931](https://github.com/Zephyruso/zashboard/commit/8443931c15be60308c6aa2924c90e44ff3b0f096)) +* reduce dom ([9799232](https://github.com/Zephyruso/zashboard/commit/9799232745e586d4dc02c5d83cc6bf1bd0f8dec8)) +* reduce dom for proxy group ([86cf36c](https://github.com/Zephyruso/zashboard/commit/86cf36c0b72de30fa3dd58ad79d75280bb361b0c)) +* sidebar collapse btn & input clear btn ([b19128d](https://github.com/Zephyruso/zashboard/commit/b19128d8f34151c036d95b791196d19ee3685dc0)) +* virtual scroller for rule ([0a35b87](https://github.com/Zephyruso/zashboard/commit/0a35b8713415c49b2de3b3e783112998080d82aa)) + +## [1.26.1](https://github.com/Zephyruso/zashboard/compare/v1.26.0...v1.26.1) (2024-12-12) + + +### Bug Fixes + +* scroll for logs & style ([17170bd](https://github.com/Zephyruso/zashboard/commit/17170bd325d71a54eebd97fdc276f969cc159a01)) +* virtual scroller for connections card ([7549eb3](https://github.com/Zephyruso/zashboard/commit/7549eb34171bbb04be965df3cf08ba2be841462b)) + +## [1.26.0](https://github.com/Zephyruso/zashboard/compare/v1.25.0...v1.26.0) (2024-12-12) + + +### Features + +* log retention limit ([267df17](https://github.com/Zephyruso/zashboard/commit/267df17abd4bbf92131b9243dcfcadf9f75a860b)) +* swap for navbar ([030f6e9](https://github.com/Zephyruso/zashboard/commit/030f6e9e97cc953caa22d1283ac52cf63851483b)) +* virtual scroller for connections ([8239b83](https://github.com/Zephyruso/zashboard/commit/8239b83d0b470fc171612a1f09c62ccd22b4ec28)) +* virtual scroller for logs ([22ed83e](https://github.com/Zephyruso/zashboard/commit/22ed83eadc90cafea8b221bd1e42c70827b6765c)) + + +### Bug Fixes + +* **autoUpgrade:** upgradeCoreAPI->upgradeUIAPI ([#79](https://github.com/Zephyruso/zashboard/issues/79)) ([eb6fb92](https://github.com/Zephyruso/zashboard/commit/eb6fb921bdedd12de9f11a2d3fdeaeae61efc02d)) +* reduce dom usage ([dabeee7](https://github.com/Zephyruso/zashboard/commit/dabeee7d95d31bcfc52c4cf167a974ff2fcc9463)) + +## [1.25.0](https://github.com/Zephyruso/zashboard/compare/v1.24.0...v1.25.0) (2024-12-11) + + +### Features + +* connections chart ([eb779f6](https://github.com/Zephyruso/zashboard/commit/eb779f648cf6528e14ae9ef24fad2479f0a2b282)) +* ip label in logs ([26b721e](https://github.com/Zephyruso/zashboard/commit/26b721e05fbc246317fcc9a5e9df6e27bb001e9b)) + + +### Bug Fixes + +* handle empty secondaryPath in SetupPage ([#78](https://github.com/Zephyruso/zashboard/issues/78)) ([0446297](https://github.com/Zephyruso/zashboard/commit/04462972404ed93fb6026f8e8b8096cbf649d819)) +* login ([80bc57a](https://github.com/Zephyruso/zashboard/commit/80bc57a452968073c8fce41251cff2d461bfced2)) + +## [1.24.0](https://github.com/Zephyruso/zashboard/compare/v1.23.0...v1.24.0) (2024-12-11) + + +### Features + +* pauseable chart ([eb92569](https://github.com/Zephyruso/zashboard/commit/eb925694d13886a55ef78746341360b9b7dff95d)) +* secondary path ([1867177](https://github.com/Zephyruso/zashboard/commit/18671772058a18130a86783ae27ca332c13457ad)) + + +### Bug Fixes + +* style ([9760297](https://github.com/Zephyruso/zashboard/commit/9760297b012c3ae01bc4e2ce5eca753714b1627f)) +* tooltip for charts ([4f44be4](https://github.com/Zephyruso/zashboard/commit/4f44be424302065935c84da04ed7b67937fda573)) + +## [1.23.0](https://github.com/Zephyruso/zashboard/compare/v1.22.2...v1.23.0) (2024-12-10) + + +### Features + +* auto update ([1ffc2fb](https://github.com/Zephyruso/zashboard/commit/1ffc2fb93177df314d4112f9b217c06015d724f8)) +* countup style for latency ([8ed0b92](https://github.com/Zephyruso/zashboard/commit/8ed0b92a281e979c1a1bca172d797d80d89f758d)) + + +### Bug Fixes + +* auto update -> auto upgrade ([6afe9b6](https://github.com/Zephyruso/zashboard/commit/6afe9b640fd7a44ee9fa284da6d49c512ce016a7)) +* charts font ([574ef6b](https://github.com/Zephyruso/zashboard/commit/574ef6bb8b9e38ce185f81d852d966cfce6f32de)) +* proxy card style ([5803d86](https://github.com/Zephyruso/zashboard/commit/5803d8696280b1051c4b957a6a8b7ee91aabe216)) +* proxy node style ([72bef99](https://github.com/Zephyruso/zashboard/commit/72bef9980263adbf63687b89e5f87dba3a047ca2)) +* rulePayload filter ([1ff658b](https://github.com/Zephyruso/zashboard/commit/1ff658bf1a4d4a94c718ebe26c993392d4d3822c)) +* sourceip filter opts ([9d7d984](https://github.com/Zephyruso/zashboard/commit/9d7d984fafdc0041280c7c76e1390e4dc7ce4fc8)) + +## [1.22.2](https://github.com/Zephyruso/zashboard/compare/v1.22.1...v1.22.2) (2024-12-09) + + +### Bug Fixes + +* is proxy group ([bf6665e](https://github.com/Zephyruso/zashboard/commit/bf6665eb5e9e2b4f6b426456bd10583608924702)) +* proxy ctrl style ([827e626](https://github.com/Zephyruso/zashboard/commit/827e626aa4f10fd5ba40a1e32f7c8f2d7b77d337)) +* proxy node grid ([2d77b57](https://github.com/Zephyruso/zashboard/commit/2d77b57ddb506119d2d8208f511c7487b17db9a0)) +* small shadow ([88c8849](https://github.com/Zephyruso/zashboard/commit/88c88493176900a57119888814877f5a683d5e8f)) + +## [1.22.1](https://github.com/Zephyruso/zashboard/compare/v1.22.0...v1.22.1) (2024-12-09) + + +### Bug Fixes + +* chart style ([3fcfdc0](https://github.com/Zephyruso/zashboard/commit/3fcfdc07af6dcf7d824ff16818769afadd026770)) +* latency color ([8db460a](https://github.com/Zephyruso/zashboard/commit/8db460aeebc9cfd97f7085049b3835b17273deed)) +* preview color & restart core ([a9f55d9](https://github.com/Zephyruso/zashboard/commit/a9f55d99e76cd6fe7be1be767e550f9531a2e8ad)) +* provider style ([36fb61e](https://github.com/Zephyruso/zashboard/commit/36fb61e7cd9dfd9e8060877dcbc99946bb46f64a)) + +## [1.22.0](https://github.com/Zephyruso/zashboard/compare/v1.21.0...v1.22.0) (2024-12-09) + + +### Features + +* allow setting protocol in params-based setup ([#70](https://github.com/Zephyruso/zashboard/issues/70)) ([1c26b96](https://github.com/Zephyruso/zashboard/commit/1c26b960579f502834ae045697ed970e79adb0f2)) +* jump to settings page when request status's code is `401` ([#69](https://github.com/Zephyruso/zashboard/issues/69)) ([dd0a044](https://github.com/Zephyruso/zashboard/commit/dd0a04441f6010d225337779f0616603a96545d8)) + + +### Bug Fixes + +* host save for setup & style ([cd24cd9](https://github.com/Zephyruso/zashboard/commit/cd24cd9a24bed5c43ffcf50b2ba2d014304e8f9c)) +* icon can not be seen clearly due to [#66](https://github.com/Zephyruso/zashboard/issues/66) ([#67](https://github.com/Zephyruso/zashboard/issues/67)) ([f8c8810](https://github.com/Zephyruso/zashboard/commit/f8c8810eb5f74c6cc88ff96fce4d165f530604ab)) +* respect the system's language settings ([7e31034](https://github.com/Zephyruso/zashboard/commit/7e31034e244e107533f2a09925dd13a76a8e4145)) +* unify shadow & rounded ([3f560ac](https://github.com/Zephyruso/zashboard/commit/3f560ac3b3969f025defdb5bee022125d4b5e69d)) + +## [1.21.0](https://github.com/Zephyruso/zashboard/compare/v1.20.1...v1.21.0) (2024-12-08) + + +### Features + +* add ProxyIcon component to ProxyNodeCard ([#66](https://github.com/Zephyruso/zashboard/issues/66)) ([a797085](https://github.com/Zephyruso/zashboard/commit/a7970852d54b6d35763646423313b52ab6192c17)) +* charts in settings & fix websocket reactive ([2a28021](https://github.com/Zephyruso/zashboard/commit/2a28021d949cfb6c34d864fb68e14500937de15d)) + + +### Bug Fixes + +* chart style ([9e299b7](https://github.com/Zephyruso/zashboard/commit/9e299b745fb5c5b1dde7833198b0dc42167efbb1)) +* make fira sans great again ([9d8d71c](https://github.com/Zephyruso/zashboard/commit/9d8d71ceaa2d6fdebbb75e786b04ada8a905dc6a)) +* set theme color when theme chagne for pwa ([6add135](https://github.com/Zephyruso/zashboard/commit/6add1356f3e1a9c3567976bad80879b62918456b)) +* system ui ([94e2433](https://github.com/Zephyruso/zashboard/commit/94e243324da3a4810d81542656a8cf033d749fce)) + +## [1.20.1](https://github.com/Zephyruso/zashboard/compare/v1.20.0...v1.20.1) (2024-12-07) + + +### Bug Fixes + +* count for conns ([466c090](https://github.com/Zephyruso/zashboard/commit/466c090b02212a7135775bfa27df457fd58c495c)) + +## [1.20.0](https://github.com/Zephyruso/zashboard/compare/v1.19.0...v1.20.0) (2024-12-07) + + +### Features + +* connections count ([2a5440e](https://github.com/Zephyruso/zashboard/commit/2a5440e259520708077342dc2a40b32a9a33ea92)) +* select of fonts ([6a38eb4](https://github.com/Zephyruso/zashboard/commit/6a38eb4b5b2e216b762bb542407595068fc38861)) +* try MiSans ([#60](https://github.com/Zephyruso/zashboard/issues/60)) ([4c7a001](https://github.com/Zephyruso/zashboard/commit/4c7a001cfb300c8f6641a803a8bd205b2c56edc8)) + + +### Bug Fixes + +* make side collapsed default ([16d1c51](https://github.com/Zephyruso/zashboard/commit/16d1c51d45b4d89bb1785558b945685d97074010)) +* tree shake & misans from npm ([6bebec6](https://github.com/Zephyruso/zashboard/commit/6bebec6725b3f69c89c7c73275d38fcab83d5ec7)) + +## [1.19.0](https://github.com/Zephyruso/zashboard/compare/v1.18.0...v1.19.0) (2024-12-06) + + +### Features + +* config for preview threshold ([af7a1e9](https://github.com/Zephyruso/zashboard/commit/af7a1e9e7a6c3381c7eb4fa245840a8ab0b5086f)) + + +### Bug Fixes + +* font fira sans ([dc69219](https://github.com/Zephyruso/zashboard/commit/dc69219463e39909c955e3efbe42b143f3442de8)) +* fuck twemoji ([ace9d13](https://github.com/Zephyruso/zashboard/commit/ace9d13e44245c4459469be8deb36770b78a0f13)) +* latency status ([c1db2ad](https://github.com/Zephyruso/zashboard/commit/c1db2ad01fd2daecf5a86bc1c4fba04c3b3d71ef)) +* node card udp ([1f5e457](https://github.com/Zephyruso/zashboard/commit/1f5e45773581c6562f878c6aa979112d2b2c3d0e)) +* tip for quick filter ([461d2ce](https://github.com/Zephyruso/zashboard/commit/461d2ce489ab8d068ab6eb122512374da36b82c4)) + +## [1.18.0](https://github.com/Zephyruso/zashboard/compare/v1.17.0...v1.18.0) (2024-12-06) + + +### Features + +* check update for ui ([#57](https://github.com/Zephyruso/zashboard/issues/57)) ([ba16c7e](https://github.com/Zephyruso/zashboard/commit/ba16c7e5cb3a001a898f0a837a0a1487fa69c78e)) + + +### Bug Fixes + +* font load ([977f743](https://github.com/Zephyruso/zashboard/commit/977f743e2207121e8d023048c9b20d14b192f81f)) +* ttf -> woff2 ([6f2a6f6](https://github.com/Zephyruso/zashboard/commit/6f2a6f6ffb178591d99a116fa99c321ed47a8a7e)) +* update tip ([ca49591](https://github.com/Zephyruso/zashboard/commit/ca4959167bec442abdd7b26464d97c062d90971b)) + +## [1.17.0](https://github.com/Zephyruso/zashboard/compare/v1.16.0...v1.17.0) (2024-12-06) + + +### Features + +* release build zip ([e60cd00](https://github.com/Zephyruso/zashboard/commit/e60cd00e8fdf8aba1201f025ffd6b19acd0536c5)) + + +### Bug Fixes + +* i18n for mode ([500dbf8](https://github.com/Zephyruso/zashboard/commit/500dbf8ff816520bf1aec3a1e58bf5ec2d4fba8e)) + +## [1.16.0](https://github.com/Zephyruso/zashboard/compare/v1.15.1...v1.16.0) (2024-12-06) + + +### Features + +* font ([2ae072a](https://github.com/Zephyruso/zashboard/commit/2ae072a0f285731f1a789e3edc7e430e586bfc40)) +* tip for https ([0eae3b3](https://github.com/Zephyruso/zashboard/commit/0eae3b308a25316943a690d5f6c586585b5a1bd4)) + +## [1.15.1](https://github.com/Zephyruso/zashboard/compare/v1.15.0...v1.15.1) (2024-12-05) + + +### Bug Fixes + +* proxy group style ([80ebdfc](https://github.com/Zephyruso/zashboard/commit/80ebdfcee3b9ef525192dd6287a0bf4e6f9aed5b)) + +## [1.15.0](https://github.com/Zephyruso/zashboard/compare/v1.14.0...v1.15.0) (2024-12-05) + + +### Features + +* node card style for mobile in dual columns ([3a6aa67](https://github.com/Zephyruso/zashboard/commit/3a6aa67b71526ca76a5d0dc96a6b09e949e6cf95)) + + +### Bug Fixes + +* proxies ctrl style ([eaed72f](https://github.com/Zephyruso/zashboard/commit/eaed72fc0f62f3464277a5964ed6d76c71143e5d)) +* ui layout for mobile ([df9f48c](https://github.com/Zephyruso/zashboard/commit/df9f48c3ad38f02e3297c720196dad0779d18e12)) + +## [1.14.0](https://github.com/Zephyruso/zashboard/compare/v1.13.1...v1.14.0) (2024-12-05) + + +### Features + +* charts animation ([e7e555d](https://github.com/Zephyruso/zashboard/commit/e7e555db258f34487ff35eaf6fb467c1f3b42bab)) +* hide unavailable proxy ([cd5c03d](https://github.com/Zephyruso/zashboard/commit/cd5c03d198e4304ced77952b16e25efb208ed095)) + + +### Bug Fixes + +* node card style ([2152529](https://github.com/Zephyruso/zashboard/commit/2152529d4361919ffa65b8ef19dfa058475ef799)) + +## [1.13.1](https://github.com/Zephyruso/zashboard/compare/v1.13.0...v1.13.1) (2024-12-04) + + +### Bug Fixes + +* md ([a190a8c](https://github.com/Zephyruso/zashboard/commit/a190a8cbcc733b23d7ee942eadf025720e0e9503)) + +## [1.13.0](https://github.com/Zephyruso/zashboard/compare/v1.12.0...v1.13.0) (2024-12-04) + + +### Features + +Docker +## [1.12.0](https://github.com/Zephyruso/zashboard/compare/v1.11.0...v1.12.0) (2024-12-04) + + +### Features + +* docker ([667069f](https://github.com/Zephyruso/zashboard/commit/667069fa4e73f01d188ad401a31591c6c2d9e9eb)) +* ghcr ([dd3dbf2](https://github.com/Zephyruso/zashboard/commit/dd3dbf20ad3ead30ad1187d7508dc6ee14076192)) +* hide `GLOBAL` proxy group when `mode` is not `GLOBAL` in `mihomo` ,otherwise it will be shown ([#38](https://github.com/Zephyruso/zashboard/issues/38)) ([2390b28](https://github.com/Zephyruso/zashboard/commit/2390b28b6680383a69d4e13d2fa6db43f3f002a6)) + + +### Bug Fixes + +* hide proxy group whose `hidden` is true in `mihomo` ([#40](https://github.com/Zephyruso/zashboard/issues/40)) ([76fbda9](https://github.com/Zephyruso/zashboard/commit/76fbda94ed78f9a7f2585e55b44a9d5c997fa31f)) +* process ([7ec9087](https://github.com/Zephyruso/zashboard/commit/7ec90877685c944498ab15e83f55f787c0235341)) +* proxy grid style ([d57007c](https://github.com/Zephyruso/zashboard/commit/d57007c5995dbad9fdb950542bbe019355dcade6)) +* settings style ([88f12c7](https://github.com/Zephyruso/zashboard/commit/88f12c7f3f9556adc4274d318594ce630fdcb906)) + +## [1.11.0](https://github.com/Zephyruso/zashboard/compare/v1.10.0...v1.11.0) (2024-12-03) + + +### Features + +* export & import settings ([3617a07](https://github.com/Zephyruso/zashboard/commit/3617a07124925764fa05d532373bc2ad0dec796c)) +* logos for version ([feb0e14](https://github.com/Zephyruso/zashboard/commit/feb0e14be50a5c9fec51d90c6c7bf9e3768fe7d4)) +* support to set icon color to the theme color when proxy icon starts with `data:image/svg+xml` ([520c44a](https://github.com/Zephyruso/zashboard/commit/520c44ae0d74c538d7a335127698797cead22086)) + + +### Bug Fixes + +* bigger menu for mobile ([fa74af5](https://github.com/Zephyruso/zashboard/commit/fa74af5b0e98409dec880273af7d3b0a64eb6790)) +* export on ios ([430cc2d](https://github.com/Zephyruso/zashboard/commit/430cc2d52ac0100ba27395e9cac15e29b5049685)) +* ip label match ends ([8ffe0c4](https://github.com/Zephyruso/zashboard/commit/8ffe0c4051a6cdda43b04584aabb6d91dbba0303)) +* remove ambiguity ([273da00](https://github.com/Zephyruso/zashboard/commit/273da002be4d7e2987bb7efb2da6c15c764e73bd)) +* settings input style ([1586c75](https://github.com/Zephyruso/zashboard/commit/1586c752b86cd5401bf10eae9f250f69febaf417)) +* style for statistics infos ([eae3bf8](https://github.com/Zephyruso/zashboard/commit/eae3bf8ccdb310bda124e3e9840dadb4e0218610)) +* truncate long host ([3cdb783](https://github.com/Zephyruso/zashboard/commit/3cdb783c449e72d8ffa7d811dcfb88fa05ed6f3a)) + +## [1.10.0](https://github.com/Zephyruso/zashboard/compare/v1.9.1...v1.10.0) (2024-12-03) + + +### Features + +* config for proxy preview type ([ffec0c5](https://github.com/Zephyruso/zashboard/commit/ffec0c5d8f8f8a645d3ca6cd9afdea6b2520953b)) +* pwa ([dd054a7](https://github.com/Zephyruso/zashboard/commit/dd054a7145c0f16ecf4f48110a35568cbdd0f533)) +* remove backend ([f26ba61](https://github.com/Zephyruso/zashboard/commit/f26ba61758364e62fa1b6370db00f334eda74c2c)) + + +### Bug Fixes + +* chain filter ([bc8655c](https://github.com/Zephyruso/zashboard/commit/bc8655ce0c1d68c9de52feb5b0312243a70a464c)) +* default regex ([b2e71e6](https://github.com/Zephyruso/zashboard/commit/b2e71e69c442aa22bab3e7c420d6a0db80cf869a)) +* proxy group title ([53471c9](https://github.com/Zephyruso/zashboard/commit/53471c99e490ebfb30437e7ffc45db03006e8589)) +* rule card style ([240296e](https://github.com/Zephyruso/zashboard/commit/240296e69d2de40ff4a61179d79afad1e06199e1)) +* rule card style ([35cbea0](https://github.com/Zephyruso/zashboard/commit/35cbea089649c59d1bf58e3bd6b2a5f9818885cd)) + +## [1.9.1](https://github.com/Zephyruso/zashboard/compare/v1.9.0...v1.9.1) (2024-12-02) + + +### Bug Fixes + +* log style ([99c4f77](https://github.com/Zephyruso/zashboard/commit/99c4f778139fdfd64c35c12ba006f574f82155b1)) +* sourceip label for card ([80cb1c8](https://github.com/Zephyruso/zashboard/commit/80cb1c82db772b363ed8f692d6258a08720cf556)) +* truncate proxy chain in card ([7aaeaec](https://github.com/Zephyruso/zashboard/commit/7aaeaec8046348ff2e1f97e46d79504729b598a6)) + +## [1.9.0](https://github.com/Zephyruso/zashboard/compare/v1.8.0...v1.9.0) (2024-12-02) + + +### Features + +* host label map & log style ([35a8dc9](https://github.com/Zephyruso/zashboard/commit/35a8dc99d1483522eaffc6284d490cb3f1ded01f)) +* truncate proxy name ([7dbeb31](https://github.com/Zephyruso/zashboard/commit/7dbeb31790772bf7612cfc95055f39eae6ababda)) +* tun mode switch ([9619aae](https://github.com/Zephyruso/zashboard/commit/9619aaeaf84756eb772e842eb902e3b1b6390e10)) + + +### Bug Fixes + +* element observer ([ad329a2](https://github.com/Zephyruso/zashboard/commit/ad329a2e3bd21a497ff48b00ee40d3aa090087f4)) +* latency test for url-test ([f9113d6](https://github.com/Zephyruso/zashboard/commit/f9113d681021ecc2ca8f94ce7b0bd7e832fdb517)) +* show tip when truncate ([a160824](https://github.com/Zephyruso/zashboard/commit/a160824f7759c719a203c92ebe46720f05b3c0c1)) +* translate ([b8ab4d9](https://github.com/Zephyruso/zashboard/commit/b8ab4d9e3790e3ef420a990e89522c1b54e86a59)) +* update all providers ([c88b1e1](https://github.com/Zephyruso/zashboard/commit/c88b1e1c3d569d77bd2248abfe53298fb4a42e57)) + +## [1.8.0](https://github.com/Zephyruso/zashboard/compare/v1.7.2...v1.8.0) (2024-12-02) + + +### Features + +* proxy group hide & config for automatic disconnection ([8b48f45](https://github.com/Zephyruso/zashboard/commit/8b48f4535c13d02114fc589c7bc78e37cb83d2ac)) +* source ip filter ([4a09d25](https://github.com/Zephyruso/zashboard/commit/4a09d25586b281b11f3b7d2cae397ee79e46ec2c)) +* upgrade core for mihomo ([cd0f65b](https://github.com/Zephyruso/zashboard/commit/cd0f65b6f8ee0e07efa880bd8f162b8413be88b9)) + + +### Bug Fixes + +* dl speed ([0d09a78](https://github.com/Zephyruso/zashboard/commit/0d09a78b8c1f8ea03f59bd780fa7165b2bcc6288)) +* proxy group style ([7c2841c](https://github.com/Zephyruso/zashboard/commit/7c2841c8436b68023b89ffe3ca2af6756d912b0e)) +* source ip filter ([4ca1a89](https://github.com/Zephyruso/zashboard/commit/4ca1a8992915774f761fa493e135b890312f10fa)) +* stable sourceips opts ([8836dd8](https://github.com/Zephyruso/zashboard/commit/8836dd81d1caccf240a9ee9c591d1ee01a0b851c)) +* style fix & config for mobile ([a59fac2](https://github.com/Zephyruso/zashboard/commit/a59fac257e2f19a8022972bc1c245c4314881a5c)) + +## [1.7.2](https://github.com/Zephyruso/zashboard/compare/v1.7.1...v1.7.2) (2024-11-29) + + +### Bug Fixes + +* modes for sing-box-p & version error & icon height ([793fdf4](https://github.com/Zephyruso/zashboard/commit/793fdf4c9221adf1108d93fafeacda54bf445804)) + +## [1.7.1](https://github.com/Zephyruso/zashboard/compare/v1.7.0...v1.7.1) (2024-11-29) + + +### Bug Fixes + +* collapse for mobile ([4561d33](https://github.com/Zephyruso/zashboard/commit/4561d33c73aa628e8b9f12b65362f47817392854)) +* md & logs color ([75b02f5](https://github.com/Zephyruso/zashboard/commit/75b02f50fc5438504053eb5a03bf9a4ba06d9e4e)) +* node card style ([9b1d391](https://github.com/Zephyruso/zashboard/commit/9b1d3914bae838c0bcc36cca11aa17becf0c8830)) +* statistics when switch backend ([b18c8b8](https://github.com/Zephyruso/zashboard/commit/b18c8b866551f580a9e0759683aed9b5af729170)) + +## [1.7.0](https://github.com/Zephyruso/zashboard/compare/v1.6.2...v1.7.0) (2024-11-29) + + +### Features + +* time for log & proxy icon ([097e936](https://github.com/Zephyruso/zashboard/commit/097e9366d4f2041d76fb2249960f8ce6952a3e29)) + + +### Bug Fixes + +* rule card ([9697156](https://github.com/Zephyruso/zashboard/commit/969715641f4979b0f3b1fa808a119d860fb26f17)) +* style ([95232e0](https://github.com/Zephyruso/zashboard/commit/95232e0d2457a5423b8156d056945b5209cdc62c)) + +## [1.6.2](https://github.com/Zephyruso/zashboard/compare/v1.6.1...v1.6.2) (2024-11-28) + + +### Bug Fixes + +* btn click & logs layout ([c8f7a38](https://github.com/Zephyruso/zashboard/commit/c8f7a38fde8c980c6d3769f8556868b41297ba7e)) +* proxy node grid ([4e3458b](https://github.com/Zephyruso/zashboard/commit/4e3458bfe8b3bc9333f804a8a55beb51fd203302)) + +## [1.6.1](https://github.com/Zephyruso/zashboard/compare/v1.6.0...v1.6.1) (2024-11-28) + + +### Bug Fixes + +* align fix & href link ([8c7678f](https://github.com/Zephyruso/zashboard/commit/8c7678f850ee4fa2618437c5366ebd2f03703f4d)) +* remove dynamic import & connections ([54eacb4](https://github.com/Zephyruso/zashboard/commit/54eacb453906d3916022fa2da46f7f24265461ff)) +* speed info ([54d3364](https://github.com/Zephyruso/zashboard/commit/54d33646f419c92a2bdc6849354d63b1bfac9581)) + +## [1.6.0](https://github.com/Zephyruso/zashboard/compare/v1.5.5...v1.6.0) (2024-11-28) + + +### Features + +* sidebar collapse & bars preview ([059c64f](https://github.com/Zephyruso/zashboard/commit/059c64f61f55f8b6ef09637d52e25985b9a4e2cd)) + +## [1.5.5](https://github.com/Zephyruso/zashboard/compare/v1.5.4...v1.5.5) (2024-11-27) + + +### Bug Fixes + +* catch errors ([58754f7](https://github.com/Zephyruso/zashboard/commit/58754f75638d05541b43a4a1979d702928300da2)) +* tab translate & home bg ([0bcffce](https://github.com/Zephyruso/zashboard/commit/0bcffce17edca247f6771e2db53656529da3765b)) + +## [1.5.4](https://github.com/Zephyruso/zashboard/compare/v1.5.3...v1.5.4) (2024-11-27) + + +### Bug Fixes + +* chart style & home bg ([827f9df](https://github.com/Zephyruso/zashboard/commit/827f9df5fbd22c128ce33b68e65fb27954df7b82)) + +## [1.5.3](https://github.com/Zephyruso/zashboard/compare/v1.5.2...v1.5.3) (2024-11-26) + + +### Bug Fixes + +* chart style ([02feffd](https://github.com/Zephyruso/zashboard/commit/02feffdcf6b32c0e8b2d18282780170894025612)) + +## [1.5.2](https://github.com/Zephyruso/zashboard/compare/v1.5.1...v1.5.2) (2024-11-26) + + +### Bug Fixes + +* menu width ([7249ffe](https://github.com/Zephyruso/zashboard/commit/7249ffe0cdcc055d76b32b1fb4d230c0c80b1591)) + +## [1.5.1](https://github.com/Zephyruso/zashboard/compare/v1.5.0...v1.5.1) (2024-11-26) + + +### Bug Fixes + +* tab for connections ([60cb6fe](https://github.com/Zephyruso/zashboard/commit/60cb6fe0dcd5cdc21c5bc8dbe4b5e4f377d81c11)) +* unify card style & pretty bytes ([77bb99b](https://github.com/Zephyruso/zashboard/commit/77bb99b88cc3c9ce05f3381641ec77ca8b728cec)) + +## [1.5.0](https://github.com/Zephyruso/zashboard/compare/v1.4.0...v1.5.0) (2024-11-26) + + +### Features + +* zashboard version ([f08c584](https://github.com/Zephyruso/zashboard/commit/f08c5848c23605db0cc8a240e7a64c27e0be57cd)) + + +### Bug Fixes + +* build ([a7d51f3](https://github.com/Zephyruso/zashboard/commit/a7d51f32512ae2c87e7c285f235ae6d299b71f6c)) +* charts units ([f4dc1c7](https://github.com/Zephyruso/zashboard/commit/f4dc1c76b85e2af3da2c32595fbf07866b1f4a52)) +* pretty bytes ([2563b02](https://github.com/Zephyruso/zashboard/commit/2563b02ca54a11a7fc4975b802b3a513d11a7ceb)) + +## [1.4.0](https://github.com/Zephyruso/zashboard/compare/v1.3.0...v1.4.0) (2024-11-26) + + +### Features + +* speed charts ([244ed8b](https://github.com/Zephyruso/zashboard/commit/244ed8bfec1544bd03b160769fad645334f38629)) + + +### Bug Fixes + +* card style with long name ([ae77d8b](https://github.com/Zephyruso/zashboard/commit/ae77d8b9726a76959ca80a6df8a27c0d6398aa1d)) + +## [1.3.0](https://github.com/Zephyruso/zashboard/compare/v1.2.0...v1.3.0) (2024-11-26) + + +### Features + +* custom sort proxy ([b38c154](https://github.com/Zephyruso/zashboard/commit/b38c1543dfb577598efdc451461f001e699de265)) + +## [1.2.0](https://github.com/Zephyruso/zashboard/compare/v1.1.0...v1.2.0) (2024-11-26) + + +### Features + +* save sorting to storage ([e52950b](https://github.com/Zephyruso/zashboard/commit/e52950b40aa01223d812a8d280199fbac8b94117)) +* upgrade all providers ([32465fe](https://github.com/Zephyruso/zashboard/commit/32465fe7f4e1a7e7d7e381fef4ff99f31ec98c10)) +* upgrade ui ([796be32](https://github.com/Zephyruso/zashboard/commit/796be32f1869ebf79bcc35b4424b1f19f4528163)) + + +### Bug Fixes + +* provider preview ([b583847](https://github.com/Zephyruso/zashboard/commit/b583847d42345e7666f49e63874d003489fb00ac)) + +## [1.1.0](https://github.com/Zephyruso/zashboard/compare/v1.0.0...v1.1.0) (2024-11-26) + + +### Features + +* providers ([b64119b](https://github.com/Zephyruso/zashboard/commit/b64119bbce9c8aa10cca56068d7459aaaa88b083)) +* subscription info ([09c71be](https://github.com/Zephyruso/zashboard/commit/09c71bebb27555b176bc4e7cb2c9b56393c998da)) + +## 1.0.0 (2024-11-25) + + +### Features + +* auto release ([47a2c6c](https://github.com/Zephyruso/zashboard/commit/47a2c6c7b9b5520ecd75b9c32f534a520b3e2095)) +* cfg for two cols ([23ffa48](https://github.com/Zephyruso/zashboard/commit/23ffa480ecd29fdb8e1e5662ba473da0ae380acd)) +* connection & logs ([fa7cc15](https://github.com/Zephyruso/zashboard/commit/fa7cc15a9b55a4dfa98a8fe0f749493f376d601d)) +* connection tables ([a27ec0b](https://github.com/Zephyruso/zashboard/commit/a27ec0bc095fb80a3114093f8ab00c03aea38014)) +* mode list ([1156911](https://github.com/Zephyruso/zashboard/commit/1156911ecf7f242dc3c1c83b904ff80386a404d6)) +* quick filter & flush fakeip & backend switch ([186ccbd](https://github.com/Zephyruso/zashboard/commit/186ccbdbd8884111a40af5282dadc9e6fef22fed)) +* version 1.0.0 ([2e429d1](https://github.com/Zephyruso/zashboard/commit/2e429d17f59a758d68727b8c27f3aa6e35e16d57)) + + +### Bug Fixes + +* pkg name ([550bd67](https://github.com/Zephyruso/zashboard/commit/550bd67366fb98bc24e38c05bc32b4c7314e832b)) diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..4351a05 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,7 @@ +:80 { + file_server + + root * . + + try_files {path} /index.html +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2916cb1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM --platform=linux/amd64 docker.io/guergeiro/pnpm:lts-latest AS builder + +WORKDIR /build + +COPY . . + +RUN pnpm install +RUN pnpm build + +FROM docker.io/caddy:alpine + +EXPOSE 80 + +WORKDIR /srv + +COPY --from=builder /build/dist/. . +COPY Caddyfile . + +CMD ["caddy", "run"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4e20e41 --- /dev/null +++ b/LICENSE @@ -0,0 +1,8 @@ +Copyright 2024 Zephyruso + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..efdb38e --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# zashboard + +

+ + +

+ +## **Requirement** + +Browser support + +- Chrome 111 (released March 2023) +- Firefox 128 (released July 2024) +- Safari 16.4 (released March 2023) +- Not supported on iOS 16.4 jailbroken version. + +## **Online** + +You can access the online zashboard at the following link: + +- [Online zashboard](http://board.zash.run.place) + +## **Download** + +You can download the zashboard files here: + +release: + +- [dist.zip (7.81 MB)](https://github.com/Zephyruso/zashboard/releases/latest/download/dist.zip) – Includes better font-loading experience. +- [dist-no-fonts.zip (1.44 MB)](https://github.com/Zephyruso/zashboard/releases/latest/download/dist-no-fonts.zip) – No fonts included, uses system fonts only. +- [dist-cdn-fonts.zip (1.44 MB)](https://github.com/Zephyruso/zashboard/releases/latest/download/dist-cdn-fonts.zip) – Fonts loaded from unpkg.com, If you have trouble connecting to unpkg.com, **you may experience slow page loading**. +- [dist-firasans-only.zip (1.67 MB)](https://github.com/Zephyruso/zashboard/releases/latest/download/dist-firasans-only.zip) – Only with FiraSans Font +- [dist-misans-only.zip (3.54 MB)](https://github.com/Zephyruso/zashboard/releases/latest/download/dist-misans-only.zip) – Only with MiSans Font +- [dist-pingfang-only.zip (3.25 MB)](https://github.com/Zephyruso/zashboard/releases/latest/download/dist-pingfang-only.zip) – Only with PingFang Font +- [dist-sarasa-only.zip (3.67 MB)](https://github.com/Zephyruso/zashboard/releases/latest/download/dist-sarasa-only.zip) – Only with Sarasa Font + +dev: + +- [gh-pages.zip (7.81 MB)](https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages.zip) +- [gh-pages-no-fonts.zip (1.44 MB)](https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages-no-fonts.zip) +- [gh-pages-cdn-fonts.zip (1.44 MB)](https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages-cdn-fonts.zip) +- [gh-pages-firasans-only.zip (1.67 MB)](https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages-firasans-only.zip) +- [gh-pages-misans-only.zip (3.54 MB)](https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages-misans-only.zip) +- [gh-pages-pingfang-only.zip (3.25 MB)](https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages-pingfang-only.zip) +- [gh-pages-sarasa-only.zip (3.67 MB)](https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages-sarasa-only.zip) + +## **Docker Setup** + +To run zashboard via Docker, use the following command: + +``` +docker run -d -p 80:80 ghcr.io/zephyruso/zashboard:latest +``` + +## Tips + +1. The connection table can be dragged with the left mouse button, and right-clicking can copy cell content. +2. Right-clicking on a node / node group card will perform a speedtest for the node / node group. +3. The proxy group sorting is based on the node order in the GLOBAL group. In Mihomo, it follows the configuration file order, while in sing-box, route.final is placed first, with the rest following the configuration file order. If you need custom ordering, you can specify the order by overriding the GLOBAL group. +4. The dashboard supports PWA (Progressive Web App), which can provide a native app-like experience on mobile devices through "Add to Home Screen". +5. The dashboard's upgrade button and auto-upgrade functionality require proper configuration of the core's UI download path ([mihomo](https://wiki.metacubex.one/config/general/#_9) | [sing-box](https://sing-box.sagernet.org/configuration/experimental/clash-api/#external_ui_download_url)), otherwise clicking update may result in updating to the core's default panel. + +## 提示 + +1. 连接表格可被鼠标左键拖动,右键可复制单元格内容。 +2. 右键点击节点/节点组卡片可对节点/节点组进行测速。 +3. 面板的节点组排序是根据GLOBAL组中的节点顺序排序的,在Mihomo中会是按配置文件的顺序,在sing-box中会把route.final放到第一位,其余按照配置文件顺序,如果你需要自定义顺序,可通过覆盖GLOBAL组指定顺序 +4. 面板支持PWA(Progressive Web App),可以在移动设备上通过"添加到主屏幕"获得类原生app的体验 +5. 面板的更新按钮和自动更新功能需要正确的配置核心的ui下载路径 ([mihomo](https://wiki.metacubex.one/config/general/#_9) | [sing-box](https://sing-box.sagernet.org/configuration/experimental/clash-api/#external_ui_download_url)), 否则可能会在点击更新后更新为核心默认面板 + +## URL params format + +#### basic example + +http://host:port/#/setup?hostname=ipordomain&port=9090&secret=123456 + +1. **`http` / `https`** + - Determines the protocol (`http` or `https`). + - Default: current page protocol + +2. **`hostname`** + - The Clash API's IP or domain. + +3. **`port`** + - The Clash API port. + +4. **`secondaryPath`** + - Optional path appended to the base URL. + - Default: An empty string. + +5. **`secret`** + - Password for authentication. + +6. **`disableUpgradeCore`** + - Set '1' or 'true' to hide upgrade core button + +### I code just for fun, not for money. If you really want to donate, please consider donating to [UNICEF](https://www.unicef.org/) to help hungry children. diff --git a/env.d.ts b/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..abe5c77 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,31 @@ +import pluginVue from 'eslint-plugin-vue' +import vueTsEslintConfig from '@vue/eslint-config-typescript' +import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' + +export default [ + { + 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({ + rules: { + 'vue/singleline-html-element-content-newline': 'off', + 'vue/multiline-html-element-content-newline': 'off', + 'vue/max-attributes-per-line': [ + 'error', + { + 'singleline': 1, + 'multiline': 1 + } + ] + } + }), + skipFormatting, +] diff --git a/index.html b/index.html new file mode 100644 index 0000000..6a688b8 --- /dev/null +++ b/index.html @@ -0,0 +1,56 @@ + + + + + zashboard + + + + + + + + +
+ + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..de62833 --- /dev/null +++ b/package.json @@ -0,0 +1,80 @@ +{ + "name": "zashboard", + "version": "2.6.0", + "description": "A Dashboard Using Clash API", + "license": "MIT", + "type": "module", + "scripts": { + "build": "vite build", + "build:cdn-fonts": "vite build --mode cdn-fonts", + "build:firasans-only": "vite build --mode FiraSans", + "build:misans-only": "vite build --mode MiSans", + "build:no-fonts": "vite build --mode SystemUI", + "build:pingfang-only": "vite build --mode PingFang", + "build:sarasa-only": "vite build --mode SarasaUi", + "dev": "vite", + "format": "prettier --write src/", + "lint": "eslint . --fix", + "prepare": "husky", + "preview": "vite preview", + "type-check": "vue-tsc --build --force" + }, + "dependencies": { + "@eslint/plugin-kit": "^0.5.0", + "@fontsource/fira-sans": "^5.2.7", + "@heroicons/vue": "^2.2.0", + "@tanstack/vue-table": "^8.21.3", + "@tanstack/vue-virtual": "^3.13.13", + "@types/reconnectingwebsocket": "^1.0.10", + "@vueuse/core": "^14.1.0", + "axios": "^1.13.2", + "countup.js": "^2.9.0", + "dayjs": "^1.11.19", + "dompurify": "^3.3.1", + "echarts": "^6.0.0", + "ipaddr.js": "^2.3.0", + "lodash": "^4.17.21", + "misans": "^4.1.0", + "p-limit": "^7.2.0", + "prettier-plugin-organize-imports": "^4.3.0", + "prettier-plugin-tailwindcss": "^0.7.2", + "pretty-bytes": "^7.1.0", + "reconnectingwebsocket": "^1.0.0", + "sort-package-json": "^3.6.0", + "subsetted-fonts": "^1.0.4", + "tippy.js": "^6.3.7", + "uuid": "^13.0.0", + "vite-plugin-pwa": "^1.2.0", + "vue": "^3.5.26", + "vue-i18n": "^11.2.7", + "vue-json-pretty": "^2.6.0", + "vue-router": "^4.6.4", + "vuedraggable": "^4.1.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.18", + "@tsconfig/node22": "^22.0.5", + "@types/lodash": "^4.17.21", + "@types/node": "^25.0.3", + "@vitejs/plugin-vue": "^6.0.3", + "@vitejs/plugin-vue-jsx": "^5.1.2", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.6.0", + "@vue/tsconfig": "^0.8.1", + "daisyui": "^5.5.14", + "eslint": "^9.39.2", + "eslint-plugin-vue": "^10.6.2", + "husky": "^9.1.7", + "lint-staged": "^16.2.7", + "postcss": "^8.5.6", + "postcss-conditionals": "^2.1.0", + "postcss-for": "^2.1.1", + "prettier": "^3.7.4", + "tailwind-merge": "^3.4.0", + "tailwindcss": "^4.1.18", + "typescript": "~5.9.3", + "vite": "^7.3.0", + "vue-tsc": "^3.2.1" + }, + "packageManager": "pnpm@10.15.0" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..203296c --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,8576 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + '@eslint/plugin-kit': + specifier: ^0.5.0 + version: 0.5.0 + '@fontsource/fira-sans': + specifier: ^5.2.7 + version: 5.2.7 + '@heroicons/vue': + specifier: ^2.2.0 + version: 2.2.0(vue@3.5.26(typescript@5.9.3)) + '@tanstack/vue-table': + specifier: ^8.21.3 + version: 8.21.3(vue@3.5.26(typescript@5.9.3)) + '@tanstack/vue-virtual': + specifier: ^3.13.13 + version: 3.13.13(vue@3.5.26(typescript@5.9.3)) + '@types/reconnectingwebsocket': + specifier: ^1.0.10 + version: 1.0.10 + '@vueuse/core': + specifier: ^14.1.0 + version: 14.1.0(vue@3.5.26(typescript@5.9.3)) + axios: + specifier: ^1.13.2 + version: 1.13.2 + countup.js: + specifier: ^2.9.0 + version: 2.9.0 + dayjs: + specifier: ^1.11.19 + version: 1.11.19 + dompurify: + specifier: ^3.3.1 + version: 3.3.1 + echarts: + specifier: ^6.0.0 + version: 6.0.0 + ipaddr.js: + specifier: ^2.3.0 + version: 2.3.0 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + misans: + specifier: ^4.1.0 + version: 4.1.0 + p-limit: + specifier: ^7.2.0 + version: 7.2.0 + prettier-plugin-organize-imports: + specifier: ^4.3.0 + version: 4.3.0(prettier@3.7.4)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3)) + prettier-plugin-tailwindcss: + specifier: ^0.7.2 + version: 0.7.2(prettier-plugin-organize-imports@4.3.0(prettier@3.7.4)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3)))(prettier@3.7.4) + pretty-bytes: + specifier: ^7.1.0 + version: 7.1.0 + reconnectingwebsocket: + specifier: ^1.0.0 + version: 1.0.0 + sort-package-json: + specifier: ^3.6.0 + version: 3.6.0 + subsetted-fonts: + specifier: ^1.0.4 + version: 1.0.4 + tippy.js: + specifier: ^6.3.7 + version: 6.3.7 + uuid: + specifier: ^13.0.0 + version: 13.0.0 + vite-plugin-pwa: + specifier: ^1.2.0 + version: 1.2.0(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(workbox-build@7.3.0)(workbox-window@7.3.0) + vue: + specifier: ^3.5.26 + version: 3.5.26(typescript@5.9.3) + vue-i18n: + specifier: ^11.2.7 + version: 11.2.7(vue@3.5.26(typescript@5.9.3)) + vue-json-pretty: + specifier: ^2.6.0 + version: 2.6.0(vue@3.5.26(typescript@5.9.3)) + vue-router: + specifier: ^4.6.4 + version: 4.6.4(vue@3.5.26(typescript@5.9.3)) + vuedraggable: + specifier: ^4.1.0 + version: 4.1.0(vue@3.5.26(typescript@5.9.3)) + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.1.18 + version: 4.1.18 + '@tsconfig/node22': + specifier: ^22.0.5 + version: 22.0.5 + '@types/lodash': + specifier: ^4.17.21 + version: 4.17.21 + '@types/node': + specifier: ^25.0.3 + version: 25.0.3 + '@vitejs/plugin-vue': + specifier: ^6.0.3 + version: 6.0.3(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': + specifier: ^5.1.2 + version: 5.1.2(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3)) + '@vue/eslint-config-prettier': + specifier: ^10.2.0 + version: 10.2.0(eslint@9.39.2(jiti@2.6.1))(prettier@3.7.4) + '@vue/eslint-config-typescript': + specifier: ^14.6.0 + version: 14.6.0(eslint-plugin-vue@10.6.2(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@vue/tsconfig': + specifier: ^0.8.1 + version: 0.8.1(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3)) + daisyui: + specifier: ^5.5.14 + version: 5.5.14 + eslint: + specifier: ^9.39.2 + version: 9.39.2(jiti@2.6.1) + eslint-plugin-vue: + specifier: ^10.6.2 + version: 10.6.2(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))) + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^16.2.7 + version: 16.2.7 + postcss: + specifier: ^8.5.6 + version: 8.5.6 + postcss-conditionals: + specifier: ^2.1.0 + version: 2.1.0 + postcss-for: + specifier: ^2.1.1 + version: 2.1.1 + prettier: + specifier: ^3.7.4 + version: 3.7.4 + tailwind-merge: + specifier: ^3.4.0 + version: 3.4.0 + tailwindcss: + specifier: ^4.1.18 + version: 4.1.18 + typescript: + specifier: ~5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.0 + version: 7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vue-tsc: + specifier: ^3.2.1 + version: 3.2.1(typescript@5.9.3) + +packages: + '@alloc/quick-lru@5.2.0': + resolution: + { + integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==, + } + engines: { node: '>=10' } + + '@apideck/better-ajv-errors@0.3.6': + resolution: + { + integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==, + } + engines: { node: '>=10' } + peerDependencies: + ajv: '>=8' + + '@babel/code-frame@7.27.1': + resolution: + { + integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==, + } + engines: { node: '>=6.9.0' } + + '@babel/compat-data@7.28.5': + resolution: + { + integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==, + } + engines: { node: '>=6.9.0' } + + '@babel/core@7.28.5': + resolution: + { + integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==, + } + engines: { node: '>=6.9.0' } + + '@babel/generator@7.28.5': + resolution: + { + integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: + { + integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-compilation-targets@7.27.2': + resolution: + { + integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-create-class-features-plugin@7.28.5': + resolution: + { + integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: + { + integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.5': + resolution: + { + integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==, + } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.28.0': + resolution: + { + integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: + { + integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-module-imports@7.27.1': + resolution: + { + integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-module-transforms@7.28.3': + resolution: + { + integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: + { + integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-plugin-utils@7.27.1': + resolution: + { + integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: + { + integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.27.1': + resolution: + { + integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: + { + integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-string-parser@7.27.1': + resolution: + { + integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-validator-identifier@7.28.5': + resolution: + { + integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-validator-option@7.27.1': + resolution: + { + integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==, + } + engines: { node: '>=6.9.0' } + + '@babel/helper-wrap-function@7.28.3': + resolution: + { + integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==, + } + engines: { node: '>=6.9.0' } + + '@babel/helpers@7.28.4': + resolution: + { + integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==, + } + engines: { node: '>=6.9.0' } + + '@babel/parser@7.28.5': + resolution: + { + integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==, + } + engines: { node: '>=6.0.0' } + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: + { + integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: + { + integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: + { + integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: + { + integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3': + resolution: + { + integrity: sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: + { + integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.27.1': + resolution: + { + integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: + { + integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: + { + integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: + { + integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: + { + integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: + { + integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.28.0': + resolution: + { + integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.27.1': + resolution: + { + integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: + { + integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.5': + resolution: + { + integrity: sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.27.1': + resolution: + { + integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.28.3': + resolution: + { + integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.28.4': + resolution: + { + integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.27.1': + resolution: + { + integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.5': + resolution: + { + integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.27.1': + resolution: + { + integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: + { + integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': + resolution: + { + integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: + { + integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.0': + resolution: + { + integrity: sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.28.5': + resolution: + { + integrity: sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: + { + integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: + { + integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: + { + integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.27.1': + resolution: + { + integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: + { + integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.28.5': + resolution: + { + integrity: sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: + { + integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: + { + integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: + { + integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.28.5': + resolution: + { + integrity: sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: + { + integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': + resolution: + { + integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: + { + integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: + { + integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.27.1': + resolution: + { + integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.4': + resolution: + { + integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: + { + integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.27.1': + resolution: + { + integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.5': + resolution: + { + integrity: sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: + { + integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.27.1': + resolution: + { + integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.27.1': + resolution: + { + integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: + { + integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.28.4': + resolution: + { + integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.27.1': + resolution: + { + integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: + { + integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: + { + integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.27.1': + resolution: + { + integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: + { + integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: + { + integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: + { + integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.5': + resolution: + { + integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: + { + integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.27.1': + resolution: + { + integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: + { + integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1': + resolution: + { + integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.28.5': + resolution: + { + integrity: sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==, + } + engines: { node: '>=6.9.0' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: + { + integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/runtime@7.28.4': + resolution: + { + integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==, + } + engines: { node: '>=6.9.0' } + + '@babel/template@7.27.2': + resolution: + { + integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==, + } + engines: { node: '>=6.9.0' } + + '@babel/traverse@7.28.5': + resolution: + { + integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==, + } + engines: { node: '>=6.9.0' } + + '@babel/types@7.28.5': + resolution: + { + integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==, + } + engines: { node: '>=6.9.0' } + + '@esbuild/aix-ppc64@0.27.2': + resolution: + { + integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: + { + integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: + { + integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==, + } + engines: { node: '>=18' } + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: + { + integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: + { + integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: + { + integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: + { + integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: + { + integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: + { + integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: + { + integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==, + } + engines: { node: '>=18' } + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: + { + integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: + { + integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==, + } + engines: { node: '>=18' } + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: + { + integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==, + } + engines: { node: '>=18' } + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: + { + integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==, + } + engines: { node: '>=18' } + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: + { + integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==, + } + engines: { node: '>=18' } + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: + { + integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==, + } + engines: { node: '>=18' } + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: + { + integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: + { + integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: + { + integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: + { + integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: + { + integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: + { + integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: + { + integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: + { + integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==, + } + engines: { node: '>=18' } + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: + { + integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==, + } + engines: { node: '>=18' } + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: + { + integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==, + } + engines: { node: '>=18' } + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: + { + integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: + { + integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + '@eslint/config-array@0.21.1': + resolution: + { + integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/config-helpers@0.4.2': + resolution: + { + integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/core@0.17.0': + resolution: + { + integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/core@1.0.0': + resolution: + { + integrity: sha512-PRfWP+8FOldvbApr6xL7mNCw4cJcSTq4GA7tYbgq15mRb0kWKO/wEB2jr+uwjFH3sZvEZneZyCUGTxsv4Sahyw==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + '@eslint/eslintrc@3.3.3': + resolution: + { + integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/js@9.39.2': + resolution: + { + integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/object-schema@2.1.7': + resolution: + { + integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/plugin-kit@0.4.1': + resolution: + { + integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@eslint/plugin-kit@0.5.0': + resolution: + { + integrity: sha512-rSXBsAcmx80jI9OUevyNBU0f5pZRQJkNmk4bLX6hCbm1qKe5Z/TcU7vwXc2nR8814mhRlgbZIHL1+HSiYS0VkQ==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + '@fontsource/fira-sans@5.2.7': + resolution: + { + integrity: sha512-5DE4AealD/VnbwdzMgnpWfCttMQBbteNiK9DCJE7cVwZEbDTPLUoFDMMvxNQ498nZc5in7Mta9c/s+3Ehh0BAg==, + } + + '@heroicons/vue@2.2.0': + resolution: + { + integrity: sha512-G3dbSxoeEKqbi/DFalhRxJU4mTXJn7GwZ7ae8NuEQzd1bqdd0jAbdaBZlHPcvPD2xI1iGzNVB4k20Un2AguYPw==, + } + peerDependencies: + vue: '>= 3' + + '@humanfs/core@0.19.1': + resolution: + { + integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, + } + engines: { node: '>=18.18.0' } + + '@humanfs/node@0.16.7': + resolution: + { + integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==, + } + engines: { node: '>=18.18.0' } + + '@humanwhocodes/module-importer@1.0.1': + resolution: + { + integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, + } + engines: { node: '>=12.22' } + + '@humanwhocodes/retry@0.4.3': + resolution: + { + integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, + } + engines: { node: '>=18.18' } + + '@intlify/core-base@11.2.7': + resolution: + { + integrity: sha512-+Ra9I/LAzXDnmv/IrTO03WMCiLya7pHRmGJvNl9fKwx/W4REJ0xaMk2PxCRqnxcBsX443amEMdebQ3R1geiuIw==, + } + engines: { node: '>= 16' } + + '@intlify/message-compiler@11.2.7': + resolution: + { + integrity: sha512-TFamC+GzJAotAFwUNvbtRVBgvuSn2nCwKNresmPUHv3IIVMmXJt7QQJj/DORI1h8hs46ZF6L0Fs2xBohSOE4iQ==, + } + engines: { node: '>= 16' } + + '@intlify/shared@11.2.7': + resolution: + { + integrity: sha512-uvlkvc/0uQ4FDlHQZccpUnmcOwNcaI3i+69ck2YJ+GqM35AoVbuS63b+YfirV4G0SZh64Ij2UMcFRMmB4nr95w==, + } + engines: { node: '>= 16' } + + '@jridgewell/gen-mapping@0.3.13': + resolution: + { + integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, + } + + '@jridgewell/remapping@2.3.5': + resolution: + { + integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, + } + + '@jridgewell/resolve-uri@3.1.2': + resolution: + { + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, + } + engines: { node: '>=6.0.0' } + + '@jridgewell/source-map@0.3.11': + resolution: + { + integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==, + } + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: + { + integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, + } + + '@jridgewell/trace-mapping@0.3.31': + resolution: + { + integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, + } + + '@nodelib/fs.scandir@2.1.5': + resolution: + { + integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, + } + engines: { node: '>= 8' } + + '@nodelib/fs.stat@2.0.5': + resolution: + { + integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, + } + engines: { node: '>= 8' } + + '@nodelib/fs.walk@1.2.8': + resolution: + { + integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, + } + engines: { node: '>= 8' } + + '@pkgr/core@0.2.9': + resolution: + { + integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==, + } + engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + + '@popperjs/core@2.11.8': + resolution: + { + integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==, + } + + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: + { + integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==, + } + + '@rolldown/pluginutils@1.0.0-beta.56': + resolution: + { + integrity: sha512-cw9jwAgCs024Nic4OB8PeFDLBHLD1Athcv3bRvyYATIVD9B/gL5X5cJkezT94Y7m7Dk9HXaUMcvb7ypvSX46sA==, + } + + '@rollup/plugin-babel@5.3.1': + resolution: + { + integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==, + } + engines: { node: '>= 10.0.0' } + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + + '@rollup/plugin-node-resolve@15.3.1': + resolution: + { + integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==, + } + engines: { node: '>=14.0.0' } + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-replace@2.4.2': + resolution: + { + integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==, + } + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + + '@rollup/plugin-terser@0.4.4': + resolution: + { + integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==, + } + engines: { node: '>=14.0.0' } + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@3.1.0': + resolution: + { + integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==, + } + engines: { node: '>= 8.0.0' } + peerDependencies: + rollup: ^1.20.0||^2.0.0 + + '@rollup/pluginutils@5.3.0': + resolution: + { + integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==, + } + engines: { node: '>=14.0.0' } + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.54.0': + resolution: + { + integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==, + } + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.54.0': + resolution: + { + integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==, + } + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.54.0': + resolution: + { + integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==, + } + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.54.0': + resolution: + { + integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==, + } + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.54.0': + resolution: + { + integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==, + } + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.54.0': + resolution: + { + integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==, + } + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + resolution: + { + integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==, + } + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + resolution: + { + integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==, + } + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.54.0': + resolution: + { + integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==, + } + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.54.0': + resolution: + { + integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==, + } + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.54.0': + resolution: + { + integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==, + } + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + resolution: + { + integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==, + } + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + resolution: + { + integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==, + } + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.54.0': + resolution: + { + integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==, + } + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.54.0': + resolution: + { + integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==, + } + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.54.0': + resolution: + { + integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==, + } + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.54.0': + resolution: + { + integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==, + } + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.54.0': + resolution: + { + integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==, + } + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.54.0': + resolution: + { + integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==, + } + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.54.0': + resolution: + { + integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==, + } + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.54.0': + resolution: + { + integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==, + } + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.54.0': + resolution: + { + integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==, + } + cpu: [x64] + os: [win32] + + '@surma/rollup-plugin-off-main-thread@2.2.3': + resolution: + { + integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==, + } + + '@tailwindcss/node@4.1.18': + resolution: + { + integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==, + } + + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: + { + integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==, + } + engines: { node: '>= 10' } + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + resolution: + { + integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==, + } + engines: { node: '>= 10' } + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.18': + resolution: + { + integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==, + } + engines: { node: '>= 10' } + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + resolution: + { + integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==, + } + engines: { node: '>= 10' } + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + resolution: + { + integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==, + } + engines: { node: '>= 10' } + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + resolution: + { + integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==, + } + engines: { node: '>= 10' } + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + resolution: + { + integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==, + } + engines: { node: '>= 10' } + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + resolution: + { + integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==, + } + engines: { node: '>= 10' } + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + resolution: + { + integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==, + } + engines: { node: '>= 10' } + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + resolution: + { + integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==, + } + engines: { node: '>=14.0.0' } + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + resolution: + { + integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==, + } + engines: { node: '>= 10' } + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + resolution: + { + integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==, + } + engines: { node: '>= 10' } + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.18': + resolution: + { + integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==, + } + engines: { node: '>= 10' } + + '@tailwindcss/postcss@4.1.18': + resolution: + { + integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==, + } + + '@tanstack/table-core@8.21.3': + resolution: + { + integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==, + } + engines: { node: '>=12' } + + '@tanstack/virtual-core@3.13.13': + resolution: + { + integrity: sha512-uQFoSdKKf5S8k51W5t7b2qpfkyIbdHMzAn+AMQvHPxKUPeo1SsGaA4JRISQT87jm28b7z8OEqPcg1IOZagQHcA==, + } + + '@tanstack/vue-table@8.21.3': + resolution: + { + integrity: sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==, + } + engines: { node: '>=12' } + peerDependencies: + vue: '>=3.2' + + '@tanstack/vue-virtual@3.13.13': + resolution: + { + integrity: sha512-Cf2xIEE8nWAfsX0N5nihkPYMeQRT+pHt4NEkuP8rNCn6lVnLDiV8rC8IeIxbKmQC0yPnj4SIBLwXYVf86xxKTQ==, + } + peerDependencies: + vue: ^2.7.0 || ^3.0.0 + + '@tsconfig/node22@22.0.5': + resolution: + { + integrity: sha512-hLf2ld+sYN/BtOJjHUWOk568dvjFQkHnLNa6zce25GIH+vxKfvTgm3qpaH6ToF5tu/NN0IH66s+Bb5wElHrLcw==, + } + + '@types/estree@0.0.39': + resolution: + { + integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==, + } + + '@types/estree@1.0.8': + resolution: + { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, + } + + '@types/json-schema@7.0.15': + resolution: + { + integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, + } + + '@types/lodash@4.17.21': + resolution: + { + integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==, + } + + '@types/node@25.0.3': + resolution: + { + integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==, + } + + '@types/reconnectingwebsocket@1.0.10': + resolution: + { + integrity: sha512-30Pq4D3o8BKcdY53dzr0elGFyB/ChYpGrHiRH/GuaZKXXGWq/CsD1QBEu1b8IgdHReOKpo9tjk80UaxSbuXoTQ==, + } + + '@types/resolve@1.20.2': + resolution: + { + integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==, + } + + '@types/trusted-types@2.0.7': + resolution: + { + integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==, + } + + '@types/web-bluetooth@0.0.21': + resolution: + { + integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==, + } + + '@typescript-eslint/eslint-plugin@8.50.1': + resolution: + { + integrity: sha512-PKhLGDq3JAg0Jk/aK890knnqduuI/Qj+udH7wCf0217IGi4gt+acgCyPVe79qoT+qKUvHMDQkwJeKW9fwl8Cyw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + '@typescript-eslint/parser': ^8.50.1 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.50.1': + resolution: + { + integrity: sha512-hM5faZwg7aVNa819m/5r7D0h0c9yC4DUlWAOvHAtISdFTc8xB86VmX5Xqabrama3wIPJ/q9RbGS1worb6JfnMg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.50.1': + resolution: + { + integrity: sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.50.1': + resolution: + { + integrity: sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/tsconfig-utils@8.50.1': + resolution: + { + integrity: sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.50.1': + resolution: + { + integrity: sha512-7J3bf022QZE42tYMO6SL+6lTPKFk/WphhRPe9Tw/el+cEwzLz1Jjz2PX3GtGQVxooLDKeMVmMt7fWpYRdG5Etg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.50.1': + resolution: + { + integrity: sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/typescript-estree@8.50.1': + resolution: + { + integrity: sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.50.1': + resolution: + { + integrity: sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.50.1': + resolution: + { + integrity: sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@vitejs/plugin-vue-jsx@5.1.2': + resolution: + { + integrity: sha512-3a2BOryRjG/Iih87x87YXz5c8nw27eSlHytvSKYfp8ZIsp5+FgFQoKeA7k2PnqWpjJrv6AoVTMnvmuKUXb771A==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vue: ^3.0.0 + + '@vitejs/plugin-vue@6.0.3': + resolution: + { + integrity: sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vue: ^3.2.25 + + '@volar/language-core@2.4.27': + resolution: + { + integrity: sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==, + } + + '@volar/source-map@2.4.27': + resolution: + { + integrity: sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg==, + } + + '@volar/typescript@2.4.27': + resolution: + { + integrity: sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==, + } + + '@vue/babel-helper-vue-transform-on@2.0.1': + resolution: + { + integrity: sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==, + } + + '@vue/babel-plugin-jsx@2.0.1': + resolution: + { + integrity: sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + + '@vue/babel-plugin-resolve-type@2.0.1': + resolution: + { + integrity: sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==, + } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/compiler-core@3.5.26': + resolution: + { + integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==, + } + + '@vue/compiler-dom@3.5.26': + resolution: + { + integrity: sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==, + } + + '@vue/compiler-sfc@3.5.26': + resolution: + { + integrity: sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==, + } + + '@vue/compiler-ssr@3.5.26': + resolution: + { + integrity: sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==, + } + + '@vue/devtools-api@6.6.4': + resolution: + { + integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==, + } + + '@vue/eslint-config-prettier@10.2.0': + resolution: + { + integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==, + } + peerDependencies: + eslint: '>= 8.21.0' + prettier: '>= 3.0.0' + + '@vue/eslint-config-typescript@14.6.0': + resolution: + { + integrity: sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^9.10.0 + eslint-plugin-vue: ^9.28.0 || ^10.0.0 + typescript: '>=4.8.4' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/language-core@3.2.1': + resolution: + { + integrity: sha512-g6oSenpnGMtpxHGAwKuu7HJJkNZpemK/zg3vZzZbJ6cnnXq1ssxuNrXSsAHYM3NvH8p4IkTw+NLmuxyeYz4r8A==, + } + + '@vue/reactivity@3.5.26': + resolution: + { + integrity: sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==, + } + + '@vue/runtime-core@3.5.26': + resolution: + { + integrity: sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==, + } + + '@vue/runtime-dom@3.5.26': + resolution: + { + integrity: sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==, + } + + '@vue/server-renderer@3.5.26': + resolution: + { + integrity: sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==, + } + peerDependencies: + vue: 3.5.26 + + '@vue/shared@3.5.26': + resolution: + { + integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==, + } + + '@vue/tsconfig@0.8.1': + resolution: + { + integrity: sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==, + } + peerDependencies: + typescript: 5.x + vue: ^3.4.0 + peerDependenciesMeta: + typescript: + optional: true + vue: + optional: true + + '@vueuse/core@14.1.0': + resolution: + { + integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==, + } + peerDependencies: + vue: ^3.5.0 + + '@vueuse/metadata@14.1.0': + resolution: + { + integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==, + } + + '@vueuse/shared@14.1.0': + resolution: + { + integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==, + } + peerDependencies: + vue: ^3.5.0 + + acorn-jsx@5.3.2: + resolution: + { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, + } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: + { + integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==, + } + engines: { node: '>=0.4.0' } + hasBin: true + + ajv@6.12.6: + resolution: + { + integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, + } + + ajv@8.17.1: + resolution: + { + integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==, + } + + alien-signals@3.1.1: + resolution: + { + integrity: sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==, + } + + ansi-escapes@7.2.0: + resolution: + { + integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==, + } + engines: { node: '>=18' } + + ansi-regex@2.1.1: + resolution: + { + integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==, + } + engines: { node: '>=0.10.0' } + + ansi-regex@6.2.2: + resolution: + { + integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==, + } + engines: { node: '>=12' } + + ansi-styles@2.2.1: + resolution: + { + integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==, + } + engines: { node: '>=0.10.0' } + + ansi-styles@4.3.0: + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: '>=8' } + + ansi-styles@6.2.3: + resolution: + { + integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==, + } + engines: { node: '>=12' } + + argparse@2.0.1: + resolution: + { + integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, + } + + array-buffer-byte-length@1.0.2: + resolution: + { + integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==, + } + engines: { node: '>= 0.4' } + + arraybuffer.prototype.slice@1.0.4: + resolution: + { + integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==, + } + engines: { node: '>= 0.4' } + + async-function@1.0.0: + resolution: + { + integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==, + } + engines: { node: '>= 0.4' } + + async@3.2.6: + resolution: + { + integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==, + } + + asynckit@0.4.0: + resolution: + { + integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, + } + + at-least-node@1.0.0: + resolution: + { + integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==, + } + engines: { node: '>= 4.0.0' } + + available-typed-arrays@1.0.7: + resolution: + { + integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==, + } + engines: { node: '>= 0.4' } + + axios@1.13.2: + resolution: + { + integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==, + } + + babel-plugin-polyfill-corejs2@0.4.14: + resolution: + { + integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==, + } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: + { + integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==, + } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.5: + resolution: + { + integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==, + } + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + balanced-match@1.0.2: + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, + } + + baseline-browser-mapping@2.9.11: + resolution: + { + integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==, + } + hasBin: true + + boolbase@1.0.0: + resolution: + { + integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, + } + + brace-expansion@1.1.12: + resolution: + { + integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, + } + + brace-expansion@2.0.2: + resolution: + { + integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==, + } + + braces@3.0.3: + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, + } + engines: { node: '>=8' } + + browserslist@4.28.1: + resolution: + { + integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==, + } + engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + hasBin: true + + buffer-from@1.1.2: + resolution: + { + integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, + } + + call-bind-apply-helpers@1.0.2: + resolution: + { + integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, + } + engines: { node: '>= 0.4' } + + call-bind@1.0.8: + resolution: + { + integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==, + } + engines: { node: '>= 0.4' } + + call-bound@1.0.4: + resolution: + { + integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==, + } + engines: { node: '>= 0.4' } + + callsites@3.1.0: + resolution: + { + integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, + } + engines: { node: '>=6' } + + caniuse-lite@1.0.30001761: + resolution: + { + integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==, + } + + chalk@1.1.3: + resolution: + { + integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==, + } + engines: { node: '>=0.10.0' } + + chalk@4.1.2: + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, + } + engines: { node: '>=10' } + + cli-cursor@5.0.0: + resolution: + { + integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==, + } + engines: { node: '>=18' } + + cli-truncate@5.1.1: + resolution: + { + integrity: sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==, + } + engines: { node: '>=20' } + + color-convert@0.5.3: + resolution: + { + integrity: sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling==, + } + + color-convert@2.0.1: + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, + } + engines: { node: '>=7.0.0' } + + color-name@1.1.4: + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, + } + + colorette@2.0.20: + resolution: + { + integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==, + } + + combined-stream@1.0.8: + resolution: + { + integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, + } + engines: { node: '>= 0.8' } + + commander@14.0.2: + resolution: + { + integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==, + } + engines: { node: '>=20' } + + commander@2.20.3: + resolution: + { + integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, + } + + common-tags@1.8.2: + resolution: + { + integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==, + } + engines: { node: '>=4.0.0' } + + concat-map@0.0.1: + resolution: + { + integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, + } + + convert-source-map@2.0.0: + resolution: + { + integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, + } + + core-js-compat@3.47.0: + resolution: + { + integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==, + } + + countup.js@2.9.0: + resolution: + { + integrity: sha512-llqrvyXztRFPp6+i8jx25phHWcVWhrHO4Nlt0uAOSKHB8778zzQswa4MU3qKBvkXfJKftRYFJuVHez67lyKdHg==, + } + + cross-spawn@7.0.6: + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, + } + engines: { node: '>= 8' } + + crypto-random-string@2.0.0: + resolution: + { + integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==, + } + engines: { node: '>=8' } + + css-color-converter@1.1.1: + resolution: + { + integrity: sha512-Fx7twabisBA+FQQ72VY/gzoSXL8RfzqKtldotbpeJjwFUYwUPBd5fm6ut/mK2T8JMPWph2CY1TpVIhy4I0ko+Q==, + } + + css-unit-converter@1.1.2: + resolution: + { + integrity: sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==, + } + + cssesc@3.0.0: + resolution: + { + integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, + } + engines: { node: '>=4' } + hasBin: true + + csstype@3.2.3: + resolution: + { + integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, + } + + daisyui@5.5.14: + resolution: + { + integrity: sha512-L47rvw7I7hK68TA97VB8Ee0woHew+/ohR6Lx6Ah/krfISOqcG4My7poNpX5Mo5/ytMxiR40fEaz6njzDi7cuSg==, + } + + data-view-buffer@1.0.2: + resolution: + { + integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==, + } + engines: { node: '>= 0.4' } + + data-view-byte-length@1.0.2: + resolution: + { + integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==, + } + engines: { node: '>= 0.4' } + + data-view-byte-offset@1.0.1: + resolution: + { + integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==, + } + engines: { node: '>= 0.4' } + + dayjs@1.11.19: + resolution: + { + integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==, + } + + debug@4.4.3: + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, + } + engines: { node: '>=6.0' } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: + { + integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, + } + + deepmerge@4.3.1: + resolution: + { + integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==, + } + engines: { node: '>=0.10.0' } + + define-data-property@1.1.4: + resolution: + { + integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==, + } + engines: { node: '>= 0.4' } + + define-properties@1.2.1: + resolution: + { + integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==, + } + engines: { node: '>= 0.4' } + + delayed-stream@1.0.0: + resolution: + { + integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, + } + engines: { node: '>=0.4.0' } + + detect-indent@7.0.2: + resolution: + { + integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==, + } + engines: { node: '>=12.20' } + + detect-libc@2.1.2: + resolution: + { + integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, + } + engines: { node: '>=8' } + + detect-newline@4.0.1: + resolution: + { + integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==, + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + dompurify@3.3.1: + resolution: + { + integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==, + } + + dunder-proto@1.0.1: + resolution: + { + integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, + } + engines: { node: '>= 0.4' } + + echarts@6.0.0: + resolution: + { + integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==, + } + + ejs@3.1.10: + resolution: + { + integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==, + } + engines: { node: '>=0.10.0' } + hasBin: true + + electron-to-chromium@1.5.267: + resolution: + { + integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==, + } + + emoji-regex@10.6.0: + resolution: + { + integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==, + } + + enhanced-resolve@5.18.4: + resolution: + { + integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==, + } + engines: { node: '>=10.13.0' } + + entities@7.0.0: + resolution: + { + integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==, + } + engines: { node: '>=0.12' } + + environment@1.1.0: + resolution: + { + integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==, + } + engines: { node: '>=18' } + + es-abstract@1.24.1: + resolution: + { + integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==, + } + engines: { node: '>= 0.4' } + + es-define-property@1.0.1: + resolution: + { + integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, + } + engines: { node: '>= 0.4' } + + es-errors@1.3.0: + resolution: + { + integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, + } + engines: { node: '>= 0.4' } + + es-object-atoms@1.1.1: + resolution: + { + integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==, + } + engines: { node: '>= 0.4' } + + es-set-tostringtag@2.1.0: + resolution: + { + integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==, + } + engines: { node: '>= 0.4' } + + es-to-primitive@1.3.0: + resolution: + { + integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==, + } + engines: { node: '>= 0.4' } + + esbuild@0.27.2: + resolution: + { + integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==, + } + engines: { node: '>=18' } + hasBin: true + + escalade@3.2.0: + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, + } + engines: { node: '>=6' } + + escape-string-regexp@1.0.5: + resolution: + { + integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, + } + engines: { node: '>=0.8.0' } + + escape-string-regexp@4.0.0: + resolution: + { + integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, + } + engines: { node: '>=10' } + + eslint-config-prettier@10.1.8: + resolution: + { + integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==, + } + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prettier@5.5.4: + resolution: + { + integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==, + } + engines: { node: ^14.18.0 || >=16.0.0 } + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-vue@10.6.2: + resolution: + { + integrity: sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 + vue-eslint-parser: ^10.0.0 + peerDependenciesMeta: + '@stylistic/eslint-plugin': + optional: true + '@typescript-eslint/parser': + optional: true + + eslint-scope@8.4.0: + resolution: + { + integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@3.4.3: + resolution: + { + integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@4.2.1: + resolution: + { + integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint@9.39.2: + resolution: + { + integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: + { + integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + esquery@1.6.0: + resolution: + { + integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==, + } + engines: { node: '>=0.10' } + + esrecurse@4.3.0: + resolution: + { + integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, + } + engines: { node: '>=4.0' } + + estraverse@5.3.0: + resolution: + { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, + } + engines: { node: '>=4.0' } + + estree-walker@1.0.1: + resolution: + { + integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==, + } + + estree-walker@2.0.2: + resolution: + { + integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==, + } + + esutils@2.0.3: + resolution: + { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, + } + engines: { node: '>=0.10.0' } + + eventemitter3@5.0.1: + resolution: + { + integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==, + } + + fast-deep-equal@3.1.3: + resolution: + { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, + } + + fast-diff@1.3.0: + resolution: + { + integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==, + } + + fast-glob@3.3.3: + resolution: + { + integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, + } + engines: { node: '>=8.6.0' } + + fast-json-stable-stringify@2.1.0: + resolution: + { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, + } + + fast-levenshtein@2.0.6: + resolution: + { + integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, + } + + fast-uri@3.1.0: + resolution: + { + integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==, + } + + fastq@1.19.1: + resolution: + { + integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==, + } + + fdir@6.5.0: + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: '>=12.0.0' } + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: + { + integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, + } + engines: { node: '>=16.0.0' } + + filelist@1.0.4: + resolution: + { + integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==, + } + + fill-range@7.1.1: + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, + } + engines: { node: '>=8' } + + find-up@5.0.0: + resolution: + { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, + } + engines: { node: '>=10' } + + flat-cache@4.0.1: + resolution: + { + integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, + } + engines: { node: '>=16' } + + flatted@3.3.3: + resolution: + { + integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==, + } + + follow-redirects@1.15.11: + resolution: + { + integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==, + } + engines: { node: '>=4.0' } + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: + { + integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==, + } + engines: { node: '>= 0.4' } + + form-data@4.0.5: + resolution: + { + integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==, + } + engines: { node: '>= 6' } + + fs-extra@9.1.0: + resolution: + { + integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==, + } + engines: { node: '>=10' } + + fs.realpath@1.0.0: + resolution: + { + integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, + } + + fsevents@2.3.3: + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + + function-bind@1.1.2: + resolution: + { + integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, + } + + function.prototype.name@1.1.8: + resolution: + { + integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==, + } + engines: { node: '>= 0.4' } + + functions-have-names@1.2.3: + resolution: + { + integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==, + } + + generator-function@2.0.1: + resolution: + { + integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==, + } + engines: { node: '>= 0.4' } + + gensync@1.0.0-beta.2: + resolution: + { + integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, + } + engines: { node: '>=6.9.0' } + + get-east-asian-width@1.4.0: + resolution: + { + integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==, + } + engines: { node: '>=18' } + + get-intrinsic@1.3.0: + resolution: + { + integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, + } + engines: { node: '>= 0.4' } + + get-own-enumerable-property-symbols@3.0.2: + resolution: + { + integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==, + } + + get-proto@1.0.1: + resolution: + { + integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, + } + engines: { node: '>= 0.4' } + + get-symbol-description@1.1.0: + resolution: + { + integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==, + } + engines: { node: '>= 0.4' } + + git-hooks-list@4.1.1: + resolution: + { + integrity: sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==, + } + + glob-parent@5.1.2: + resolution: + { + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, + } + engines: { node: '>= 6' } + + glob-parent@6.0.2: + resolution: + { + integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, + } + engines: { node: '>=10.13.0' } + + glob@7.2.3: + resolution: + { + integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==, + } + deprecated: Glob versions prior to v9 are no longer supported + + globals@14.0.0: + resolution: + { + integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, + } + engines: { node: '>=18' } + + globalthis@1.0.4: + resolution: + { + integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==, + } + engines: { node: '>= 0.4' } + + gopd@1.2.0: + resolution: + { + integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, + } + engines: { node: '>= 0.4' } + + graceful-fs@4.2.11: + resolution: + { + integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, + } + + has-ansi@2.0.0: + resolution: + { + integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==, + } + engines: { node: '>=0.10.0' } + + has-bigints@1.1.0: + resolution: + { + integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==, + } + engines: { node: '>= 0.4' } + + has-flag@1.0.0: + resolution: + { + integrity: sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==, + } + engines: { node: '>=0.10.0' } + + has-flag@4.0.0: + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, + } + engines: { node: '>=8' } + + has-property-descriptors@1.0.2: + resolution: + { + integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==, + } + + has-proto@1.2.0: + resolution: + { + integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==, + } + engines: { node: '>= 0.4' } + + has-symbols@1.1.0: + resolution: + { + integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, + } + engines: { node: '>= 0.4' } + + has-tostringtag@1.0.2: + resolution: + { + integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==, + } + engines: { node: '>= 0.4' } + + hasown@2.0.2: + resolution: + { + integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, + } + engines: { node: '>= 0.4' } + + husky@9.1.7: + resolution: + { + integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==, + } + engines: { node: '>=18' } + hasBin: true + + idb@7.1.1: + resolution: + { + integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==, + } + + ignore@5.3.2: + resolution: + { + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, + } + engines: { node: '>= 4' } + + ignore@7.0.5: + resolution: + { + integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, + } + engines: { node: '>= 4' } + + import-fresh@3.3.1: + resolution: + { + integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, + } + engines: { node: '>=6' } + + imurmurhash@0.1.4: + resolution: + { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, + } + engines: { node: '>=0.8.19' } + + inflight@1.0.6: + resolution: + { + integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==, + } + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: + { + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, + } + + internal-slot@1.1.0: + resolution: + { + integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==, + } + engines: { node: '>= 0.4' } + + ipaddr.js@2.3.0: + resolution: + { + integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==, + } + engines: { node: '>= 10' } + + is-array-buffer@3.0.5: + resolution: + { + integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==, + } + engines: { node: '>= 0.4' } + + is-async-function@2.1.1: + resolution: + { + integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==, + } + engines: { node: '>= 0.4' } + + is-bigint@1.1.0: + resolution: + { + integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==, + } + engines: { node: '>= 0.4' } + + is-boolean-object@1.2.2: + resolution: + { + integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==, + } + engines: { node: '>= 0.4' } + + is-callable@1.2.7: + resolution: + { + integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==, + } + engines: { node: '>= 0.4' } + + is-core-module@2.16.1: + resolution: + { + integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==, + } + engines: { node: '>= 0.4' } + + is-data-view@1.0.2: + resolution: + { + integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==, + } + engines: { node: '>= 0.4' } + + is-date-object@1.1.0: + resolution: + { + integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==, + } + engines: { node: '>= 0.4' } + + is-extglob@2.1.1: + resolution: + { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, + } + engines: { node: '>=0.10.0' } + + is-finalizationregistry@1.1.1: + resolution: + { + integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==, + } + engines: { node: '>= 0.4' } + + is-fullwidth-code-point@5.1.0: + resolution: + { + integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==, + } + engines: { node: '>=18' } + + is-generator-function@1.1.2: + resolution: + { + integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==, + } + engines: { node: '>= 0.4' } + + is-glob@4.0.3: + resolution: + { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, + } + engines: { node: '>=0.10.0' } + + is-map@2.0.3: + resolution: + { + integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==, + } + engines: { node: '>= 0.4' } + + is-module@1.0.0: + resolution: + { + integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==, + } + + is-negative-zero@2.0.3: + resolution: + { + integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==, + } + engines: { node: '>= 0.4' } + + is-number-object@1.1.1: + resolution: + { + integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==, + } + engines: { node: '>= 0.4' } + + is-number@7.0.0: + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, + } + engines: { node: '>=0.12.0' } + + is-obj@1.0.1: + resolution: + { + integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==, + } + engines: { node: '>=0.10.0' } + + is-plain-obj@4.1.0: + resolution: + { + integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==, + } + engines: { node: '>=12' } + + is-regex@1.2.1: + resolution: + { + integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==, + } + engines: { node: '>= 0.4' } + + is-regexp@1.0.0: + resolution: + { + integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==, + } + engines: { node: '>=0.10.0' } + + is-set@2.0.3: + resolution: + { + integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==, + } + engines: { node: '>= 0.4' } + + is-shared-array-buffer@1.0.4: + resolution: + { + integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==, + } + engines: { node: '>= 0.4' } + + is-stream@2.0.1: + resolution: + { + integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==, + } + engines: { node: '>=8' } + + is-string@1.1.1: + resolution: + { + integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==, + } + engines: { node: '>= 0.4' } + + is-symbol@1.1.1: + resolution: + { + integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==, + } + engines: { node: '>= 0.4' } + + is-typed-array@1.1.15: + resolution: + { + integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==, + } + engines: { node: '>= 0.4' } + + is-weakmap@2.0.2: + resolution: + { + integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==, + } + engines: { node: '>= 0.4' } + + is-weakref@1.1.1: + resolution: + { + integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==, + } + engines: { node: '>= 0.4' } + + is-weakset@2.0.4: + resolution: + { + integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==, + } + engines: { node: '>= 0.4' } + + isarray@2.0.5: + resolution: + { + integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==, + } + + isexe@2.0.0: + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, + } + + jake@10.9.4: + resolution: + { + integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==, + } + engines: { node: '>=10' } + hasBin: true + + jiti@2.6.1: + resolution: + { + integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==, + } + hasBin: true + + js-base64@2.6.4: + resolution: + { + integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==, + } + + js-tokens@4.0.0: + resolution: + { + integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, + } + + js-yaml@4.1.1: + resolution: + { + integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==, + } + hasBin: true + + jsesc@3.1.0: + resolution: + { + integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, + } + engines: { node: '>=6' } + hasBin: true + + json-buffer@3.0.1: + resolution: + { + integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, + } + + json-schema-traverse@0.4.1: + resolution: + { + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, + } + + json-schema-traverse@1.0.0: + resolution: + { + integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, + } + + json-schema@0.4.0: + resolution: + { + integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==, + } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { + integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, + } + + json5@2.2.3: + resolution: + { + integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, + } + engines: { node: '>=6' } + hasBin: true + + jsonfile@6.2.0: + resolution: + { + integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==, + } + + jsonpointer@5.0.1: + resolution: + { + integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==, + } + engines: { node: '>=0.10.0' } + + keyv@4.5.4: + resolution: + { + integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, + } + + leven@3.1.0: + resolution: + { + integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==, + } + engines: { node: '>=6' } + + levn@0.4.1: + resolution: + { + integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, + } + engines: { node: '>= 0.8.0' } + + lightningcss-android-arm64@1.30.2: + resolution: + { + integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: + { + integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: + { + integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: + { + integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: + { + integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==, + } + engines: { node: '>= 12.0.0' } + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: + { + integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: + { + integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: + { + integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: + { + integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: + { + integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==, + } + engines: { node: '>= 12.0.0' } + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: + { + integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==, + } + engines: { node: '>= 12.0.0' } + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: + { + integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==, + } + engines: { node: '>= 12.0.0' } + + lint-staged@16.2.7: + resolution: + { + integrity: sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==, + } + engines: { node: '>=20.17' } + hasBin: true + + listr2@9.0.5: + resolution: + { + integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==, + } + engines: { node: '>=20.0.0' } + + locate-path@6.0.0: + resolution: + { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, + } + engines: { node: '>=10' } + + lodash.debounce@4.0.8: + resolution: + { + integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==, + } + + lodash.merge@4.6.2: + resolution: + { + integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, + } + + lodash.sortby@4.7.0: + resolution: + { + integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==, + } + + lodash@4.17.21: + resolution: + { + integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, + } + + log-update@6.1.0: + resolution: + { + integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==, + } + engines: { node: '>=18' } + + lru-cache@5.1.1: + resolution: + { + integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, + } + + magic-string@0.25.9: + resolution: + { + integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==, + } + + magic-string@0.30.21: + resolution: + { + integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, + } + + math-intrinsics@1.1.0: + resolution: + { + integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, + } + engines: { node: '>= 0.4' } + + merge2@1.4.1: + resolution: + { + integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, + } + engines: { node: '>= 8' } + + micromatch@4.0.8: + resolution: + { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, + } + engines: { node: '>=8.6' } + + mime-db@1.52.0: + resolution: + { + integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, + } + engines: { node: '>= 0.6' } + + mime-types@2.1.35: + resolution: + { + integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, + } + engines: { node: '>= 0.6' } + + mimic-function@5.0.1: + resolution: + { + integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==, + } + engines: { node: '>=18' } + + minimatch@3.1.2: + resolution: + { + integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, + } + + minimatch@5.1.6: + resolution: + { + integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==, + } + engines: { node: '>=10' } + + minimatch@9.0.5: + resolution: + { + integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==, + } + engines: { node: '>=16 || 14 >=14.17' } + + misans@4.1.0: + resolution: + { + integrity: sha512-CcIRrIVhnt+OpGXvw1Q8llGBVAy5P2mdov/kJ0gGa81sJ0RY7mZp2fNAt2ySTCeZos+wo7ZnzDZxl1In//7FdA==, + } + + ms@2.1.3: + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } + + muggle-string@0.4.1: + resolution: + { + integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==, + } + + nano-spawn@2.0.0: + resolution: + { + integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==, + } + engines: { node: '>=20.17' } + + nanoid@3.3.11: + resolution: + { + integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + hasBin: true + + natural-compare@1.4.0: + resolution: + { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, + } + + node-releases@2.0.27: + resolution: + { + integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==, + } + + nth-check@2.1.1: + resolution: + { + integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==, + } + + object-inspect@1.13.4: + resolution: + { + integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==, + } + engines: { node: '>= 0.4' } + + object-keys@1.1.1: + resolution: + { + integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==, + } + engines: { node: '>= 0.4' } + + object.assign@4.1.7: + resolution: + { + integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==, + } + engines: { node: '>= 0.4' } + + once@1.4.0: + resolution: + { + integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, + } + + onetime@7.0.0: + resolution: + { + integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==, + } + engines: { node: '>=18' } + + optionator@0.9.4: + resolution: + { + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, + } + engines: { node: '>= 0.8.0' } + + own-keys@1.0.1: + resolution: + { + integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==, + } + engines: { node: '>= 0.4' } + + p-limit@3.1.0: + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, + } + engines: { node: '>=10' } + + p-limit@7.2.0: + resolution: + { + integrity: sha512-ATHLtwoTNDloHRFFxFJdHnG6n2WUeFjaR8XQMFdKIv0xkXjrER8/iG9iu265jOM95zXHAfv9oTkqhrfbIzosrQ==, + } + engines: { node: '>=20' } + + p-locate@5.0.0: + resolution: + { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, + } + engines: { node: '>=10' } + + parent-module@1.0.1: + resolution: + { + integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, + } + engines: { node: '>=6' } + + path-browserify@1.0.1: + resolution: + { + integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==, + } + + path-exists@4.0.0: + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: '>=8' } + + path-is-absolute@1.0.1: + resolution: + { + integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==, + } + engines: { node: '>=0.10.0' } + + path-key@3.1.1: + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, + } + engines: { node: '>=8' } + + path-parse@1.0.7: + resolution: + { + integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, + } + + picocolors@1.1.1: + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } + + picomatch@2.3.1: + resolution: + { + integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, + } + engines: { node: '>=8.6' } + + picomatch@4.0.3: + resolution: + { + integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, + } + engines: { node: '>=12' } + + pidtree@0.6.0: + resolution: + { + integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==, + } + engines: { node: '>=0.10' } + hasBin: true + + possible-typed-array-names@1.1.0: + resolution: + { + integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==, + } + engines: { node: '>= 0.4' } + + postcss-conditionals@2.1.0: + resolution: + { + integrity: sha512-PjYipbEJKz3S+m5xlbyxNvrA68jnrEdFOK1q2+VCffMVbut6dN7mDp1WGfP0umnRczyrNYdttVviWvPAjRseLw==, + } + + postcss-for@2.1.1: + resolution: + { + integrity: sha512-X0R84FCyr5cqzW4+/g4Dvz2OUe1iwC3G/atIrwEpiRstZlBBpknV+ETlIneSTnw/iXgUnEoTRaO2qXY62YWLhQ==, + } + + postcss-selector-parser@7.1.1: + resolution: + { + integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==, + } + engines: { node: '>=4' } + + postcss-simple-vars@2.0.0: + resolution: + { + integrity: sha512-HllLaKKCBOdKudyzqrw/ve5rWouM9cDL+WHaSF9q4CkBEPjdTdiKNw1xF2dAz5rUKrxVmnUmOYxamwy37dnq2Q==, + } + + postcss@5.2.18: + resolution: + { + integrity: sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==, + } + engines: { node: '>=0.12' } + + postcss@8.5.6: + resolution: + { + integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, + } + engines: { node: ^10 || ^12 || >=14 } + + prelude-ls@1.2.1: + resolution: + { + integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, + } + engines: { node: '>= 0.8.0' } + + prettier-linter-helpers@1.0.0: + resolution: + { + integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==, + } + engines: { node: '>=6.0.0' } + + prettier-plugin-organize-imports@4.3.0: + resolution: + { + integrity: sha512-FxFz0qFhyBsGdIsb697f/EkvHzi5SZOhWAjxcx2dLt+Q532bAlhswcXGYB1yzjZ69kW8UoadFBw7TyNwlq96Iw==, + } + peerDependencies: + prettier: '>=2.0' + typescript: '>=2.9' + vue-tsc: ^2.1.0 || 3 + peerDependenciesMeta: + vue-tsc: + optional: true + + prettier-plugin-tailwindcss@0.7.2: + resolution: + { + integrity: sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==, + } + engines: { node: '>=20.19' } + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.7.4: + resolution: + { + integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==, + } + engines: { node: '>=14' } + hasBin: true + + pretty-bytes@5.6.0: + resolution: + { + integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==, + } + engines: { node: '>=6' } + + pretty-bytes@6.1.1: + resolution: + { + integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==, + } + engines: { node: ^14.13.1 || >=16.0.0 } + + pretty-bytes@7.1.0: + resolution: + { + integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==, + } + engines: { node: '>=20' } + + proxy-from-env@1.1.0: + resolution: + { + integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==, + } + + punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: '>=6' } + + queue-microtask@1.2.3: + resolution: + { + integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, + } + + randombytes@2.1.0: + resolution: + { + integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==, + } + + reconnectingwebsocket@1.0.0: + resolution: + { + integrity: sha512-r7H/dwkkfBu9x5eMGIt8td5WLqNbqy675x8Xg0+SoXaUS3xzniVlmfO7t7HSYmN/ZGzYjOKa9G2W4xCgCo7Zlg==, + } + + reflect.getprototypeof@1.0.10: + resolution: + { + integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==, + } + engines: { node: '>= 0.4' } + + regenerate-unicode-properties@10.2.2: + resolution: + { + integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==, + } + engines: { node: '>=4' } + + regenerate@1.4.2: + resolution: + { + integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==, + } + + regexp.prototype.flags@1.5.4: + resolution: + { + integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==, + } + engines: { node: '>= 0.4' } + + regexpu-core@6.4.0: + resolution: + { + integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==, + } + engines: { node: '>=4' } + + regjsgen@0.8.0: + resolution: + { + integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==, + } + + regjsparser@0.13.0: + resolution: + { + integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==, + } + hasBin: true + + require-from-string@2.0.2: + resolution: + { + integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, + } + engines: { node: '>=0.10.0' } + + resolve-from@4.0.0: + resolution: + { + integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, + } + engines: { node: '>=4' } + + resolve@1.22.11: + resolution: + { + integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==, + } + engines: { node: '>= 0.4' } + hasBin: true + + restore-cursor@5.1.0: + resolution: + { + integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==, + } + engines: { node: '>=18' } + + reusify@1.1.0: + resolution: + { + integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, + } + engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + + rfdc@1.4.1: + resolution: + { + integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==, + } + + rollup@2.79.2: + resolution: + { + integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==, + } + engines: { node: '>=10.0.0' } + hasBin: true + + rollup@4.54.0: + resolution: + { + integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==, + } + engines: { node: '>=18.0.0', npm: '>=8.0.0' } + hasBin: true + + run-parallel@1.2.0: + resolution: + { + integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, + } + + safe-array-concat@1.1.3: + resolution: + { + integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==, + } + engines: { node: '>=0.4' } + + safe-buffer@5.2.1: + resolution: + { + integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, + } + + safe-push-apply@1.0.0: + resolution: + { + integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==, + } + engines: { node: '>= 0.4' } + + safe-regex-test@1.1.0: + resolution: + { + integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==, + } + engines: { node: '>= 0.4' } + + semver@6.3.1: + resolution: + { + integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, + } + hasBin: true + + semver@7.7.3: + resolution: + { + integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==, + } + engines: { node: '>=10' } + hasBin: true + + serialize-javascript@6.0.2: + resolution: + { + integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==, + } + + set-function-length@1.2.2: + resolution: + { + integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==, + } + engines: { node: '>= 0.4' } + + set-function-name@2.0.2: + resolution: + { + integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==, + } + engines: { node: '>= 0.4' } + + set-proto@1.0.0: + resolution: + { + integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==, + } + engines: { node: '>= 0.4' } + + shebang-command@2.0.0: + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, + } + engines: { node: '>=8' } + + shebang-regex@3.0.0: + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, + } + engines: { node: '>=8' } + + side-channel-list@1.0.0: + resolution: + { + integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==, + } + engines: { node: '>= 0.4' } + + side-channel-map@1.0.1: + resolution: + { + integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==, + } + engines: { node: '>= 0.4' } + + side-channel-weakmap@1.0.2: + resolution: + { + integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==, + } + engines: { node: '>= 0.4' } + + side-channel@1.1.0: + resolution: + { + integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==, + } + engines: { node: '>= 0.4' } + + signal-exit@4.1.0: + resolution: + { + integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, + } + engines: { node: '>=14' } + + slice-ansi@7.1.2: + resolution: + { + integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==, + } + engines: { node: '>=18' } + + smob@1.5.0: + resolution: + { + integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==, + } + + sort-object-keys@2.0.1: + resolution: + { + integrity: sha512-R89fO+z3x7hiKPXX5P0qim+ge6Y60AjtlW+QQpRozrrNcR1lw9Pkpm5MLB56HoNvdcLHL4wbpq16OcvGpEDJIg==, + } + + sort-package-json@3.6.0: + resolution: + { + integrity: sha512-fyJsPLhWvY7u2KsKPZn1PixbXp+1m7V8NWqU8CvgFRbMEX41Ffw1kD8n0CfJiGoaSfoAvbrqRRl/DcHO8omQOQ==, + } + engines: { node: '>=20' } + hasBin: true + + sortablejs@1.14.0: + resolution: + { + integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==, + } + + source-map-js@1.2.1: + resolution: + { + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, + } + engines: { node: '>=0.10.0' } + + source-map-support@0.5.21: + resolution: + { + integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==, + } + + source-map@0.5.7: + resolution: + { + integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==, + } + engines: { node: '>=0.10.0' } + + source-map@0.6.1: + resolution: + { + integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, + } + engines: { node: '>=0.10.0' } + + source-map@0.8.0-beta.0: + resolution: + { + integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==, + } + engines: { node: '>= 8' } + deprecated: The work that was done in this beta branch won't be included in future versions + + sourcemap-codec@1.4.8: + resolution: + { + integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==, + } + deprecated: Please use @jridgewell/sourcemap-codec instead + + stop-iteration-iterator@1.1.0: + resolution: + { + integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==, + } + engines: { node: '>= 0.4' } + + string-argv@0.3.2: + resolution: + { + integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==, + } + engines: { node: '>=0.6.19' } + + string-width@7.2.0: + resolution: + { + integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==, + } + engines: { node: '>=18' } + + string-width@8.1.0: + resolution: + { + integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==, + } + engines: { node: '>=20' } + + string.prototype.matchall@4.0.12: + resolution: + { + integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==, + } + engines: { node: '>= 0.4' } + + string.prototype.trim@1.2.10: + resolution: + { + integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==, + } + engines: { node: '>= 0.4' } + + string.prototype.trimend@1.0.9: + resolution: + { + integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==, + } + engines: { node: '>= 0.4' } + + string.prototype.trimstart@1.0.8: + resolution: + { + integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==, + } + engines: { node: '>= 0.4' } + + stringify-object@3.3.0: + resolution: + { + integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==, + } + engines: { node: '>=4' } + + strip-ansi@3.0.1: + resolution: + { + integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==, + } + engines: { node: '>=0.10.0' } + + strip-ansi@7.1.2: + resolution: + { + integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==, + } + engines: { node: '>=12' } + + strip-comments@2.0.1: + resolution: + { + integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==, + } + engines: { node: '>=10' } + + strip-json-comments@3.1.1: + resolution: + { + integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, + } + engines: { node: '>=8' } + + subsetted-fonts@1.0.4: + resolution: + { + integrity: sha512-d/z+2OruazabDwJ6mHCDQhOQ/8BJW7r1VXeVvw9xBP49DH1pSGPgpNbGcG7tQc6EMsZOfFQnXwFdEakwV4CuIw==, + } + + supports-color@2.0.0: + resolution: + { + integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==, + } + engines: { node: '>=0.8.0' } + + supports-color@3.2.3: + resolution: + { + integrity: sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==, + } + engines: { node: '>=0.8.0' } + + supports-color@7.2.0: + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, + } + engines: { node: '>=8' } + + supports-preserve-symlinks-flag@1.0.0: + resolution: + { + integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, + } + engines: { node: '>= 0.4' } + + synckit@0.11.11: + resolution: + { + integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==, + } + engines: { node: ^14.18.0 || >=16.0.0 } + + tailwind-merge@3.4.0: + resolution: + { + integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==, + } + + tailwindcss@4.1.18: + resolution: + { + integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==, + } + + tapable@2.3.0: + resolution: + { + integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==, + } + engines: { node: '>=6' } + + temp-dir@2.0.0: + resolution: + { + integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==, + } + engines: { node: '>=8' } + + tempy@0.6.0: + resolution: + { + integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==, + } + engines: { node: '>=10' } + + terser@5.44.1: + resolution: + { + integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==, + } + engines: { node: '>=10' } + hasBin: true + + tinyglobby@0.2.15: + resolution: + { + integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, + } + engines: { node: '>=12.0.0' } + + tippy.js@6.3.7: + resolution: + { + integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==, + } + + to-regex-range@5.0.1: + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, + } + engines: { node: '>=8.0' } + + tr46@1.0.1: + resolution: + { + integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==, + } + + ts-api-utils@2.1.0: + resolution: + { + integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==, + } + engines: { node: '>=18.12' } + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.3.0: + resolution: + { + integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==, + } + + type-check@0.4.0: + resolution: + { + integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, + } + engines: { node: '>= 0.8.0' } + + type-fest@0.16.0: + resolution: + { + integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==, + } + engines: { node: '>=10' } + + typed-array-buffer@1.0.3: + resolution: + { + integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==, + } + engines: { node: '>= 0.4' } + + typed-array-byte-length@1.0.3: + resolution: + { + integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==, + } + engines: { node: '>= 0.4' } + + typed-array-byte-offset@1.0.4: + resolution: + { + integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==, + } + engines: { node: '>= 0.4' } + + typed-array-length@1.0.7: + resolution: + { + integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==, + } + engines: { node: '>= 0.4' } + + typescript-eslint@8.50.1: + resolution: + { + integrity: sha512-ytTHO+SoYSbhAH9CrYnMhiLx8To6PSSvqnvXyPUgPETCvB6eBKmTI9w6XMPS3HsBRGkwTVBX+urA8dYQx6bHfQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.9.3: + resolution: + { + integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, + } + engines: { node: '>=14.17' } + hasBin: true + + unbox-primitive@1.1.0: + resolution: + { + integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==, + } + engines: { node: '>= 0.4' } + + undici-types@7.16.0: + resolution: + { + integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==, + } + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: + { + integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==, + } + engines: { node: '>=4' } + + unicode-match-property-ecmascript@2.0.0: + resolution: + { + integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==, + } + engines: { node: '>=4' } + + unicode-match-property-value-ecmascript@2.2.1: + resolution: + { + integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==, + } + engines: { node: '>=4' } + + unicode-property-aliases-ecmascript@2.2.0: + resolution: + { + integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==, + } + engines: { node: '>=4' } + + unique-string@2.0.0: + resolution: + { + integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==, + } + engines: { node: '>=8' } + + universalify@2.0.1: + resolution: + { + integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==, + } + engines: { node: '>= 10.0.0' } + + upath@1.2.0: + resolution: + { + integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==, + } + engines: { node: '>=4' } + + update-browserslist-db@1.2.3: + resolution: + { + integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==, + } + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, + } + + util-deprecate@1.0.2: + resolution: + { + integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, + } + + uuid@13.0.0: + resolution: + { + integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==, + } + hasBin: true + + vite-plugin-pwa@1.2.0: + resolution: + { + integrity: sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw==, + } + engines: { node: '>=16.0.0' } + peerDependencies: + '@vite-pwa/assets-generator': ^1.0.0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + workbox-build: ^7.4.0 + workbox-window: ^7.4.0 + peerDependenciesMeta: + '@vite-pwa/assets-generator': + optional: true + + vite@7.3.0: + resolution: + { + integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-uri@3.1.0: + resolution: + { + integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==, + } + + vue-eslint-parser@10.2.0: + resolution: + { + integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + vue-i18n@11.2.7: + resolution: + { + integrity: sha512-LPv8bAY5OA0UvFEXl4vBQOBqJzRrlExy92tWgRuwW7tbykHf7CH71G2Y4TM2OwGcIS4+hyqKHS2EVBqaYwPY9Q==, + } + engines: { node: '>= 16' } + peerDependencies: + vue: ^3.0.0 + + vue-json-pretty@2.6.0: + resolution: + { + integrity: sha512-glz1aBVS35EO8+S9agIl3WOQaW2cJZW192UVKTuGmryx01ZvOVWc4pR3t+5UcyY4jdOfBUgVHjcpRpcnjRhCAg==, + } + engines: { node: '>= 10.0.0', npm: '>= 5.0.0' } + peerDependencies: + vue: '>=3.0.0' + + vue-router@4.6.4: + resolution: + { + integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==, + } + peerDependencies: + vue: ^3.5.0 + + vue-tsc@3.2.1: + resolution: + { + integrity: sha512-I23Rk8dkQfmcSbxDO0dmg9ioMLjKA1pjlU3Lz6Jfk2pMGu3Uryu9810XkcZH24IzPbhzPCnkKo2rEMRX0skSrw==, + } + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.26: + resolution: + { + integrity: sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==, + } + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vuedraggable@4.1.0: + resolution: + { + integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==, + } + peerDependencies: + vue: ^3.0.1 + + webidl-conversions@4.0.2: + resolution: + { + integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==, + } + + whatwg-url@7.1.0: + resolution: + { + integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==, + } + + which-boxed-primitive@1.1.1: + resolution: + { + integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==, + } + engines: { node: '>= 0.4' } + + which-builtin-type@1.2.1: + resolution: + { + integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==, + } + engines: { node: '>= 0.4' } + + which-collection@1.0.2: + resolution: + { + integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==, + } + engines: { node: '>= 0.4' } + + which-typed-array@1.1.19: + resolution: + { + integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==, + } + engines: { node: '>= 0.4' } + + which@2.0.2: + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, + } + engines: { node: '>= 8' } + hasBin: true + + word-wrap@1.2.5: + resolution: + { + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, + } + engines: { node: '>=0.10.0' } + + workbox-background-sync@7.3.0: + resolution: + { + integrity: sha512-PCSk3eK7Mxeuyatb22pcSx9dlgWNv3+M8PqPaYDokks8Y5/FX4soaOqj3yhAZr5k6Q5JWTOMYgaJBpbw11G9Eg==, + } + + workbox-broadcast-update@7.3.0: + resolution: + { + integrity: sha512-T9/F5VEdJVhwmrIAE+E/kq5at2OY6+OXXgOWQevnubal6sO92Gjo24v6dCVwQiclAF5NS3hlmsifRrpQzZCdUA==, + } + + workbox-build@7.3.0: + resolution: + { + integrity: sha512-JGL6vZTPlxnlqZRhR/K/msqg3wKP+m0wfEUVosK7gsYzSgeIxvZLi1ViJJzVL7CEeI8r7rGFV973RiEqkP3lWQ==, + } + engines: { node: '>=16.0.0' } + + workbox-cacheable-response@7.3.0: + resolution: + { + integrity: sha512-eAFERIg6J2LuyELhLlmeRcJFa5e16Mj8kL2yCDbhWE+HUun9skRQrGIFVUagqWj4DMaaPSMWfAolM7XZZxNmxA==, + } + + workbox-core@7.3.0: + resolution: + { + integrity: sha512-Z+mYrErfh4t3zi7NVTvOuACB0A/jA3bgxUN3PwtAVHvfEsZxV9Iju580VEETug3zYJRc0Dmii/aixI/Uxj8fmw==, + } + + workbox-expiration@7.3.0: + resolution: + { + integrity: sha512-lpnSSLp2BM+K6bgFCWc5bS1LR5pAwDWbcKt1iL87/eTSJRdLdAwGQznZE+1czLgn/X05YChsrEegTNxjM067vQ==, + } + + workbox-google-analytics@7.3.0: + resolution: + { + integrity: sha512-ii/tSfFdhjLHZ2BrYgFNTrb/yk04pw2hasgbM70jpZfLk0vdJAXgaiMAWsoE+wfJDNWoZmBYY0hMVI0v5wWDbg==, + } + + workbox-navigation-preload@7.3.0: + resolution: + { + integrity: sha512-fTJzogmFaTv4bShZ6aA7Bfj4Cewaq5rp30qcxl2iYM45YD79rKIhvzNHiFj1P+u5ZZldroqhASXwwoyusnr2cg==, + } + + workbox-precaching@7.3.0: + resolution: + { + integrity: sha512-ckp/3t0msgXclVAYaNndAGeAoWQUv7Rwc4fdhWL69CCAb2UHo3Cef0KIUctqfQj1p8h6aGyz3w8Cy3Ihq9OmIw==, + } + + workbox-range-requests@7.3.0: + resolution: + { + integrity: sha512-EyFmM1KpDzzAouNF3+EWa15yDEenwxoeXu9bgxOEYnFfCxns7eAxA9WSSaVd8kujFFt3eIbShNqa4hLQNFvmVQ==, + } + + workbox-recipes@7.3.0: + resolution: + { + integrity: sha512-BJro/MpuW35I/zjZQBcoxsctgeB+kyb2JAP5EB3EYzePg8wDGoQuUdyYQS+CheTb+GhqJeWmVs3QxLI8EBP1sg==, + } + + workbox-routing@7.3.0: + resolution: + { + integrity: sha512-ZUlysUVn5ZUzMOmQN3bqu+gK98vNfgX/gSTZ127izJg/pMMy4LryAthnYtjuqcjkN4HEAx1mdgxNiKJMZQM76A==, + } + + workbox-strategies@7.3.0: + resolution: + { + integrity: sha512-tmZydug+qzDFATwX7QiEL5Hdf7FrkhjaF9db1CbB39sDmEZJg3l9ayDvPxy8Y18C3Y66Nrr9kkN1f/RlkDgllg==, + } + + workbox-streams@7.3.0: + resolution: + { + integrity: sha512-SZnXucyg8x2Y61VGtDjKPO5EgPUG5NDn/v86WYHX+9ZqvAsGOytP0Jxp1bl663YUuMoXSAtsGLL+byHzEuMRpw==, + } + + workbox-sw@7.3.0: + resolution: + { + integrity: sha512-aCUyoAZU9IZtH05mn0ACUpyHzPs0lMeJimAYkQkBsOWiqaJLgusfDCR+yllkPkFRxWpZKF8vSvgHYeG7LwhlmA==, + } + + workbox-window@7.3.0: + resolution: + { + integrity: sha512-qW8PDy16OV1UBaUNGlTVcepzrlzyzNW/ZJvFQQs2j2TzGsg6IKjcpZC1RSquqQnTOafl5pCj5bGfAHlCjOOjdA==, + } + + wrap-ansi@9.0.2: + resolution: + { + integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==, + } + engines: { node: '>=18' } + + wrappy@1.0.2: + resolution: + { + integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, + } + + xml-name-validator@4.0.0: + resolution: + { + integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==, + } + engines: { node: '>=12' } + + yallist@3.1.1: + resolution: + { + integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, + } + + yaml@2.8.2: + resolution: + { + integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==, + } + engines: { node: '>= 14.6' } + hasBin: true + + yocto-queue@0.1.0: + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, + } + engines: { node: '>=10' } + + yocto-queue@1.2.2: + resolution: + { + integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==, + } + engines: { node: '>=12.20' } + + zrender@6.0.0: + resolution: + { + integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==, + } + +snapshots: + '@alloc/quick-lru@5.2.0': {} + + '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': + dependencies: + ajv: 8.17.1 + json-schema: 0.4.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.5 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.3 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.3': + dependencies: + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-block-scoping@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-logical-assignment-operators@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-optional-chaining@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/preset-env@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.5) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.5) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoping': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.5) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-exponentiation-operator': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-logical-assignment-operators': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-systemjs': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.5) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.5) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.5) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.5) + core-js-compat: 3.47.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.28.5 + esutils: 2.0.3 + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2(jiti@2.6.1))': + dependencies: + eslint: 9.39.2(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@1.0.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@eslint/plugin-kit@0.5.0': + dependencies: + '@eslint/core': 1.0.0 + levn: 0.4.1 + + '@fontsource/fira-sans@5.2.7': {} + + '@heroicons/vue@2.2.0(vue@3.5.26(typescript@5.9.3))': + dependencies: + vue: 3.5.26(typescript@5.9.3) + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@intlify/core-base@11.2.7': + dependencies: + '@intlify/message-compiler': 11.2.7 + '@intlify/shared': 11.2.7 + + '@intlify/message-compiler@11.2.7': + dependencies: + '@intlify/shared': 11.2.7 + source-map-js: 1.2.1 + + '@intlify/shared@11.2.7': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@pkgr/core@0.2.9': {} + + '@popperjs/core@2.11.8': {} + + '@rolldown/pluginutils@1.0.0-beta.53': {} + + '@rolldown/pluginutils@1.0.0-beta.56': {} + + '@rollup/plugin-babel@5.3.1(@babel/core@7.28.5)(rollup@2.79.2)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + rollup: 2.79.2 + transitivePeerDependencies: + - supports-color + + '@rollup/plugin-node-resolve@15.3.1(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@2.79.2) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.11 + optionalDependencies: + rollup: 2.79.2 + + '@rollup/plugin-replace@2.4.2(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + magic-string: 0.25.9 + rollup: 2.79.2 + + '@rollup/plugin-terser@0.4.4(rollup@2.79.2)': + dependencies: + serialize-javascript: 6.0.2 + smob: 1.5.0 + terser: 5.44.1 + optionalDependencies: + rollup: 2.79.2 + + '@rollup/pluginutils@3.1.0(rollup@2.79.2)': + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 2.79.2 + + '@rollup/pluginutils@5.3.0(rollup@2.79.2)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 2.79.2 + + '@rollup/rollup-android-arm-eabi@4.54.0': + optional: true + + '@rollup/rollup-android-arm64@4.54.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.54.0': + optional: true + + '@rollup/rollup-darwin-x64@4.54.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.54.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.54.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.54.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.54.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.54.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.54.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.54.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.54.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.54.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.54.0': + optional: true + + '@surma/rollup-plugin-off-main-thread@2.2.3': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.25.9 + string.prototype.matchall: 4.0.12 + + '@tailwindcss/node@4.1.18': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.4 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.18 + + '@tailwindcss/oxide-android-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide@4.1.18': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-x64': 4.1.18 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 + + '@tailwindcss/postcss@4.1.18': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 + postcss: 8.5.6 + tailwindcss: 4.1.18 + + '@tanstack/table-core@8.21.3': {} + + '@tanstack/virtual-core@3.13.13': {} + + '@tanstack/vue-table@8.21.3(vue@3.5.26(typescript@5.9.3))': + dependencies: + '@tanstack/table-core': 8.21.3 + vue: 3.5.26(typescript@5.9.3) + + '@tanstack/vue-virtual@3.13.13(vue@3.5.26(typescript@5.9.3))': + dependencies: + '@tanstack/virtual-core': 3.13.13 + vue: 3.5.26(typescript@5.9.3) + + '@tsconfig/node22@22.0.5': {} + + '@types/estree@0.0.39': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/lodash@4.17.21': {} + + '@types/node@25.0.3': + dependencies: + undici-types: 7.16.0 + + '@types/reconnectingwebsocket@1.0.10': {} + + '@types/resolve@1.20.2': {} + + '@types/trusted-types@2.0.7': {} + + '@types/web-bluetooth@0.0.21': {} + + '@typescript-eslint/eslint-plugin@8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.1 + '@typescript-eslint/type-utils': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.1 + eslint: 9.39.2(jiti@2.6.1) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.50.1 + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.1 + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.50.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3) + '@typescript-eslint/types': 8.50.1 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.50.1': + dependencies: + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/visitor-keys': 8.50.1 + + '@typescript-eslint/tsconfig-utils@8.50.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.50.1': {} + + '@typescript-eslint/typescript-estree@8.50.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.50.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3) + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/visitor-keys': 8.50.1 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.50.1 + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.50.1': + dependencies: + '@typescript-eslint/types': 8.50.1 + eslint-visitor-keys: 4.2.1 + + '@vitejs/plugin-vue-jsx@5.1.2(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.56 + '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.28.5) + vite: 7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vue: 3.5.26(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@6.0.3(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.53 + vite: 7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vue: 3.5.26(typescript@5.9.3) + + '@volar/language-core@2.4.27': + dependencies: + '@volar/source-map': 2.4.27 + + '@volar/source-map@2.4.27': {} + + '@volar/typescript@2.4.27': + dependencies: + '@volar/language-core': 2.4.27 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/babel-helper-vue-transform-on@2.0.1': {} + + '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.28.5)': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@vue/babel-helper-vue-transform-on': 2.0.1 + '@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.28.5) + '@vue/shared': 3.5.26 + optionalDependencies: + '@babel/core': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.28.5)': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/parser': 7.28.5 + '@vue/compiler-sfc': 3.5.26 + transitivePeerDependencies: + - supports-color + + '@vue/compiler-core@3.5.26': + dependencies: + '@babel/parser': 7.28.5 + '@vue/shared': 3.5.26 + entities: 7.0.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.26': + dependencies: + '@vue/compiler-core': 3.5.26 + '@vue/shared': 3.5.26 + + '@vue/compiler-sfc@3.5.26': + dependencies: + '@babel/parser': 7.28.5 + '@vue/compiler-core': 3.5.26 + '@vue/compiler-dom': 3.5.26 + '@vue/compiler-ssr': 3.5.26 + '@vue/shared': 3.5.26 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.26': + dependencies: + '@vue/compiler-dom': 3.5.26 + '@vue/shared': 3.5.26 + + '@vue/devtools-api@6.6.4': {} + + '@vue/eslint-config-prettier@10.2.0(eslint@9.39.2(jiti@2.6.1))(prettier@3.7.4)': + dependencies: + eslint: 9.39.2(jiti@2.6.1) + eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-prettier: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.7.4) + prettier: 3.7.4 + transitivePeerDependencies: + - '@types/eslint' + + '@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@10.6.2(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/utils': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + eslint-plugin-vue: 10.6.2(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))) + fast-glob: 3.3.3 + typescript-eslint: 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + vue-eslint-parser: 10.2.0(eslint@9.39.2(jiti@2.6.1)) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@vue/language-core@3.2.1': + dependencies: + '@volar/language-core': 2.4.27 + '@vue/compiler-dom': 3.5.26 + '@vue/shared': 3.5.26 + alien-signals: 3.1.1 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.3 + + '@vue/reactivity@3.5.26': + dependencies: + '@vue/shared': 3.5.26 + + '@vue/runtime-core@3.5.26': + dependencies: + '@vue/reactivity': 3.5.26 + '@vue/shared': 3.5.26 + + '@vue/runtime-dom@3.5.26': + dependencies: + '@vue/reactivity': 3.5.26 + '@vue/runtime-core': 3.5.26 + '@vue/shared': 3.5.26 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.26(vue@3.5.26(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.5.26 + '@vue/shared': 3.5.26 + vue: 3.5.26(typescript@5.9.3) + + '@vue/shared@3.5.26': {} + + '@vue/tsconfig@0.8.1(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3))': + optionalDependencies: + typescript: 5.9.3 + vue: 3.5.26(typescript@5.9.3) + + '@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.1.0 + '@vueuse/shared': 14.1.0(vue@3.5.26(typescript@5.9.3)) + vue: 3.5.26(typescript@5.9.3) + + '@vueuse/metadata@14.1.0': {} + + '@vueuse/shared@14.1.0(vue@3.5.26(typescript@5.9.3))': + dependencies: + vue: 3.5.26(typescript@5.9.3) + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + alien-signals@3.1.1: {} + + ansi-escapes@7.2.0: + dependencies: + environment: 1.1.0 + + ansi-regex@2.1.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@2.2.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + async-function@1.0.0: {} + + async@3.2.6: {} + + asynckit@0.4.0: {} + + at-least-node@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios@1.13.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + core-js-compat: 3.47.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + balanced-match@1.0.2: {} + + baseline-browser-mapping@2.9.11: {} + + boolbase@1.0.0: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001761 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + buffer-from@1.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001761: {} + + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@5.1.1: + dependencies: + slice-ansi: 7.1.2 + string-width: 8.1.0 + + color-convert@0.5.3: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@14.0.2: {} + + commander@2.20.3: {} + + common-tags@1.8.2: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + core-js-compat@3.47.0: + dependencies: + browserslist: 4.28.1 + + countup.js@2.9.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-random-string@2.0.0: {} + + css-color-converter@1.1.1: + dependencies: + color-convert: 0.5.3 + color-name: 1.1.4 + + css-unit-converter@1.1.2: {} + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + daisyui@5.5.14: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + dayjs@1.11.19: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + detect-indent@7.0.2: {} + + detect-libc@2.1.2: {} + + detect-newline@4.0.1: {} + + dompurify@3.3.1: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + echarts@6.0.0: + dependencies: + tslib: 2.3.0 + zrender: 6.0.0 + + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-to-chromium@1.5.267: {} + + emoji-regex@10.6.0: {} + + enhanced-resolve@5.18.4: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + entities@7.0.0: {} + + environment@1.1.0: {} + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)): + dependencies: + eslint: 9.39.2(jiti@2.6.1) + + eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.7.4): + dependencies: + eslint: 9.39.2(jiti@2.6.1) + prettier: 3.7.4 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@2.6.1)) + + eslint-plugin-vue@10.6.2(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + eslint: 9.39.2(jiti@2.6.1) + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 7.1.1 + semver: 7.7.3 + vue-eslint-parser: 10.2.0(eslint@9.39.2(jiti@2.6.1)) + xml-name-validator: 4.0.0 + optionalDependencies: + '@typescript-eslint/parser': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.2(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@1.0.1: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + eventemitter3@5.0.1: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.0: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + follow-redirects@1.15.11: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-east-asian-width@1.4.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-own-enumerable-property-symbols@3.0.2: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + git-hooks-list@4.1.1: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + + has-bigints@1.1.0: {} + + has-flag@1.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + husky@9.1.7: {} + + idb@7.1.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + ipaddr.js@2.3.0: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.4.0 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-module@1.0.0: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-obj@1.0.1: {} + + is-plain-obj@4.1.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-regexp@1.0.0: {} + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.4 + picocolors: 1.1.1 + + jiti@2.6.1: {} + + js-base64@2.6.4: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema@0.4.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpointer@5.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + lint-staged@16.2.7: + dependencies: + commander: 14.0.2 + listr2: 9.0.5 + micromatch: 4.0.8 + nano-spawn: 2.0.0 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.2 + + listr2@9.0.5: + dependencies: + cli-truncate: 5.1.1 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.debounce@4.0.8: {} + + lodash.merge@4.6.2: {} + + lodash.sortby@4.7.0: {} + + lodash@4.17.21: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.2.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-function@5.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + misans@4.1.0: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + nano-spawn@2.0.0: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.27: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@7.2.0: + dependencies: + yocto-queue: 1.2.2 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pidtree@0.6.0: {} + + possible-typed-array-names@1.1.0: {} + + postcss-conditionals@2.1.0: + dependencies: + css-color-converter: 1.1.1 + css-unit-converter: 1.1.2 + postcss: 5.2.18 + + postcss-for@2.1.1: + dependencies: + postcss: 5.2.18 + postcss-simple-vars: 2.0.0 + + postcss-selector-parser@7.1.1: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-simple-vars@2.0.0: + dependencies: + postcss: 5.2.18 + + postcss@5.2.18: + dependencies: + chalk: 1.1.3 + js-base64: 2.6.4 + source-map: 0.5.7 + supports-color: 3.2.3 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier-plugin-organize-imports@4.3.0(prettier@3.7.4)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3)): + dependencies: + prettier: 3.7.4 + typescript: 5.9.3 + optionalDependencies: + vue-tsc: 3.2.1(typescript@5.9.3) + + prettier-plugin-tailwindcss@0.7.2(prettier-plugin-organize-imports@4.3.0(prettier@3.7.4)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3)))(prettier@3.7.4): + dependencies: + prettier: 3.7.4 + optionalDependencies: + prettier-plugin-organize-imports: 4.3.0(prettier@3.7.4)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3)) + + prettier@3.7.4: {} + + pretty-bytes@5.6.0: {} + + pretty-bytes@6.1.1: {} + + pretty-bytes@7.1.0: {} + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + reconnectingwebsocket@1.0.0: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.0: + dependencies: + jsesc: 3.1.0 + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rollup@2.79.2: + optionalDependencies: + fsevents: 2.3.3 + + rollup@4.54.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.54.0 + '@rollup/rollup-android-arm64': 4.54.0 + '@rollup/rollup-darwin-arm64': 4.54.0 + '@rollup/rollup-darwin-x64': 4.54.0 + '@rollup/rollup-freebsd-arm64': 4.54.0 + '@rollup/rollup-freebsd-x64': 4.54.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 + '@rollup/rollup-linux-arm-musleabihf': 4.54.0 + '@rollup/rollup-linux-arm64-gnu': 4.54.0 + '@rollup/rollup-linux-arm64-musl': 4.54.0 + '@rollup/rollup-linux-loong64-gnu': 4.54.0 + '@rollup/rollup-linux-ppc64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-musl': 4.54.0 + '@rollup/rollup-linux-s390x-gnu': 4.54.0 + '@rollup/rollup-linux-x64-gnu': 4.54.0 + '@rollup/rollup-linux-x64-musl': 4.54.0 + '@rollup/rollup-openharmony-arm64': 4.54.0 + '@rollup/rollup-win32-arm64-msvc': 4.54.0 + '@rollup/rollup-win32-ia32-msvc': 4.54.0 + '@rollup/rollup-win32-x64-gnu': 4.54.0 + '@rollup/rollup-win32-x64-msvc': 4.54.0 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + semver@6.3.1: {} + + semver@7.7.3: {} + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@4.1.0: {} + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + smob@1.5.0: {} + + sort-object-keys@2.0.1: {} + + sort-package-json@3.6.0: + dependencies: + detect-indent: 7.0.2 + detect-newline: 4.0.1 + git-hooks-list: 4.1.1 + is-plain-obj: 4.1.0 + semver: 7.7.3 + sort-object-keys: 2.0.1 + tinyglobby: 0.2.15 + + sortablejs@1.14.0: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + sourcemap-codec@1.4.8: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string-argv@0.3.2: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + string-width@8.1.0: + dependencies: + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + subsetted-fonts@1.0.4: {} + + supports-color@2.0.0: {} + + supports-color@3.2.3: + dependencies: + has-flag: 1.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + + tailwind-merge@3.4.0: {} + + tailwindcss@4.1.18: {} + + tapable@2.3.0: {} + + temp-dir@2.0.0: {} + + tempy@0.6.0: + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + + terser@5.44.1: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tippy.js@6.3.7: + dependencies: + '@popperjs/core': 2.11.8 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tslib@2.3.0: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.16.0: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@7.16.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unique-string@2.0.0: + dependencies: + crypto-random-string: 2.0.0 + + universalify@2.0.1: {} + + upath@1.2.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + uuid@13.0.0: {} + + vite-plugin-pwa@1.2.0(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(workbox-build@7.3.0)(workbox-window@7.3.0): + dependencies: + debug: 4.4.3 + pretty-bytes: 6.1.1 + tinyglobby: 0.2.15 + vite: 7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + workbox-build: 7.3.0 + workbox-window: 7.3.0 + transitivePeerDependencies: + - supports-color + + vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): + dependencies: + esbuild: 0.27.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.54.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.0.3 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + terser: 5.44.1 + yaml: 2.8.2 + + vscode-uri@3.1.0: {} + + vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1)): + dependencies: + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + + vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)): + dependencies: + '@intlify/core-base': 11.2.7 + '@intlify/shared': 11.2.7 + '@vue/devtools-api': 6.6.4 + vue: 3.5.26(typescript@5.9.3) + + vue-json-pretty@2.6.0(vue@3.5.26(typescript@5.9.3)): + dependencies: + vue: 3.5.26(typescript@5.9.3) + + vue-router@4.6.4(vue@3.5.26(typescript@5.9.3)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.26(typescript@5.9.3) + + vue-tsc@3.2.1(typescript@5.9.3): + dependencies: + '@volar/typescript': 2.4.27 + '@vue/language-core': 3.2.1 + typescript: 5.9.3 + + vue@3.5.26(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.26 + '@vue/compiler-sfc': 3.5.26 + '@vue/runtime-dom': 3.5.26 + '@vue/server-renderer': 3.5.26(vue@3.5.26(typescript@5.9.3)) + '@vue/shared': 3.5.26 + optionalDependencies: + typescript: 5.9.3 + + vuedraggable@4.1.0(vue@3.5.26(typescript@5.9.3)): + dependencies: + sortablejs: 1.14.0 + vue: 3.5.26(typescript@5.9.3) + + webidl-conversions@4.0.2: {} + + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + workbox-background-sync@7.3.0: + dependencies: + idb: 7.1.1 + workbox-core: 7.3.0 + + workbox-broadcast-update@7.3.0: + dependencies: + workbox-core: 7.3.0 + + workbox-build@7.3.0: + dependencies: + '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1) + '@babel/core': 7.28.5 + '@babel/preset-env': 7.28.5(@babel/core@7.28.5) + '@babel/runtime': 7.28.4 + '@rollup/plugin-babel': 5.3.1(@babel/core@7.28.5)(rollup@2.79.2) + '@rollup/plugin-node-resolve': 15.3.1(rollup@2.79.2) + '@rollup/plugin-replace': 2.4.2(rollup@2.79.2) + '@rollup/plugin-terser': 0.4.4(rollup@2.79.2) + '@surma/rollup-plugin-off-main-thread': 2.2.3 + ajv: 8.17.1 + common-tags: 1.8.2 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 7.2.3 + lodash: 4.17.21 + pretty-bytes: 5.6.0 + rollup: 2.79.2 + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 7.3.0 + workbox-broadcast-update: 7.3.0 + workbox-cacheable-response: 7.3.0 + workbox-core: 7.3.0 + workbox-expiration: 7.3.0 + workbox-google-analytics: 7.3.0 + workbox-navigation-preload: 7.3.0 + workbox-precaching: 7.3.0 + workbox-range-requests: 7.3.0 + workbox-recipes: 7.3.0 + workbox-routing: 7.3.0 + workbox-strategies: 7.3.0 + workbox-streams: 7.3.0 + workbox-sw: 7.3.0 + workbox-window: 7.3.0 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-cacheable-response@7.3.0: + dependencies: + workbox-core: 7.3.0 + + workbox-core@7.3.0: {} + + workbox-expiration@7.3.0: + dependencies: + idb: 7.1.1 + workbox-core: 7.3.0 + + workbox-google-analytics@7.3.0: + dependencies: + workbox-background-sync: 7.3.0 + workbox-core: 7.3.0 + workbox-routing: 7.3.0 + workbox-strategies: 7.3.0 + + workbox-navigation-preload@7.3.0: + dependencies: + workbox-core: 7.3.0 + + workbox-precaching@7.3.0: + dependencies: + workbox-core: 7.3.0 + workbox-routing: 7.3.0 + workbox-strategies: 7.3.0 + + workbox-range-requests@7.3.0: + dependencies: + workbox-core: 7.3.0 + + workbox-recipes@7.3.0: + dependencies: + workbox-cacheable-response: 7.3.0 + workbox-core: 7.3.0 + workbox-expiration: 7.3.0 + workbox-precaching: 7.3.0 + workbox-routing: 7.3.0 + workbox-strategies: 7.3.0 + + workbox-routing@7.3.0: + dependencies: + workbox-core: 7.3.0 + + workbox-strategies@7.3.0: + dependencies: + workbox-core: 7.3.0 + + workbox-streams@7.3.0: + dependencies: + workbox-core: 7.3.0 + workbox-routing: 7.3.0 + + workbox-sw@7.3.0: {} + + workbox-window@7.3.0: + dependencies: + '@types/trusted-types': 2.0.7 + workbox-core: 7.3.0 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + xml-name-validator@4.0.0: {} + + yallist@3.1.1: {} + + yaml@2.8.2: {} + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.2: {} + + zrender@6.0.0: + dependencies: + tslib: 2.3.0 diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..52d7e38 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,7 @@ +export default { + plugins: { + 'postcss-for': {}, + 'postcss-conditionals': {}, + '@tailwindcss/postcss': {}, + }, +} diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..3c01d30 Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon-dark.svg b/public/favicon-dark.svg new file mode 100644 index 0000000..5dd4db2 --- /dev/null +++ b/public/favicon-dark.svg @@ -0,0 +1 @@ + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..10c79b8 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..c40ef3c --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 0000000..1073674 --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/pwa-192x192.png b/public/pwa-192x192.png new file mode 100644 index 0000000..474f887 Binary files /dev/null and b/public/pwa-192x192.png differ diff --git a/public/pwa-512x512.png b/public/pwa-512x512.png new file mode 100644 index 0000000..ce3505d Binary files /dev/null and b/public/pwa-512x512.png differ diff --git a/public/pwa-maskable-192x192.png b/public/pwa-maskable-192x192.png new file mode 100644 index 0000000..ea041e0 Binary files /dev/null and b/public/pwa-maskable-192x192.png differ diff --git a/public/pwa-maskable-512x512.png b/public/pwa-maskable-512x512.png new file mode 100644 index 0000000..5a0e51c Binary files /dev/null and b/public/pwa-maskable-512x512.png differ diff --git a/readme/mobile.png b/readme/mobile.png new file mode 100644 index 0000000..6837904 Binary files /dev/null and b/readme/mobile.png differ diff --git a/readme/pc.png b/readme/pc.png new file mode 100644 index 0000000..8a6f93e Binary files /dev/null and b/readme/pc.png differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..d1ddf64 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,148 @@ + + + diff --git a/src/api/geoip.ts b/src/api/geoip.ts new file mode 100644 index 0000000..5e7d7e4 --- /dev/null +++ b/src/api/geoip.ts @@ -0,0 +1,206 @@ +import { IP_INFO_API } from '@/constant' +import { IPInfoAPI } from '@/store/settings' + +export interface IPInfo { + ip: string + country: string + region: string + city: string + asn: string + organization: string +} + +// china +export const getIPFromIpipnetAPI = async () => { + const response = await fetch('https://myip.ipip.net/json?t=' + Date.now()) + + return (await response.json()) as { + data: { + ip: string + location: string[] + } + } +} + +// global +const getIPFromIpsbAPI = async (ip = '') => { + const response = await fetch( + 'https://api.ip.sb/geoip' + (ip ? `/${ip}` : '') + '?t=' + Date.now(), + ) + + return (await response.json()) as { + organization: string + longitude: number + city: string + region: string + timezone: string + isp: string + offset: number + asn: number + asn_organization: string + country: string + ip: string + latitude: number + postal_code: string + continent_code: string + country_code: string + region_code: string + } +} + +const getIPFromIPWhoisAPI = async (ip = '') => { + const response = await fetch('https://ipwho.is' + (ip ? `/${ip}` : '') + '?t=' + Date.now()) + + return (await response.json()) as { + ip: string + success: boolean + type: string + continent: string + continent_code: string + country: string + country_code: string + region: string + region_code: string + city: string + latitude: number + longitude: number + is_eu: boolean + postal: string + calling_code: string + capital: string + borders: string + flag: { + img: string + emoji: string + emoji_unicode: string + } + connection: { + asn: number + org: string + isp: string + domain: string + } + timezone: { + id: string + abbr: string + is_dst: boolean + offset: number + utc: string + current_time: string + } + } +} + +const getIPFromIPapiisAPI = async (ip = '') => { + const response = await fetch( + 'https://api.ipapi.is' + (ip ? `/?q=${ip}` : '') + (ip ? '&' : '?') + 't=' + Date.now(), + ) + + return (await response.json()) as { + ip: string + rir: string + is_bogon: boolean + is_mobile: boolean + is_satellite: boolean + is_crawler: boolean + is_datacenter: boolean + is_tor: boolean + is_proxy: boolean + is_vpn: boolean + is_abuser: boolean + datacenter: { + datacenter: string + network: string + region: string + service: string + network_border_group: string + } + company: { + name: string + abuser_score: string + domain: string + type: string + network: string + whois: string + } + abuse: { + name: string + address: string + email: string + phone: string + } + asn: { + asn: number + abuser_score: string + route: string + descr: string + country: string + active: boolean + org: string + domain: string + abuse: string + type: string + created: string + updated: string + rir: string + whois: string + } + location: { + is_eu_member: boolean + calling_code: string + currency_code: string + continent: string + country: string + country_code: string + state: string + city: string + latitude: number + longitude: number + zip: string + timezone: string + local_time: string + local_time_unix: number + is_dst: boolean + } + elapsed_ms: number + } +} + +export const getIPInfo = async (ip = ''): Promise => { + switch (IPInfoAPI.value) { + case IP_INFO_API.IPAPI: + const ipapi = await getIPFromIPapiisAPI(ip) + + return { + ip: ipapi.ip, + country: ipapi.location.country, + region: ipapi.location.state, + city: ipapi.location.city, + asn: ipapi.asn.asn.toString(), + organization: ipapi.asn.org, + } + case IP_INFO_API.IPWHOIS: + const ipwhois = await getIPFromIPWhoisAPI(ip) + + return { + ip: ipwhois.ip, + region: ipwhois.region, + country: ipwhois.country, + city: ipwhois.city, + asn: ipwhois.connection.asn.toString(), + organization: ipwhois.connection.org, + } + case IP_INFO_API.IPSB: + default: + const ipsb = await getIPFromIpsbAPI(ip) + + return { + ip: ipsb.ip, + country: ipsb.country, + region: ipsb.region, + city: ipsb.city, + asn: ipsb.asn.toString(), + organization: ipsb.organization, + } + } +} diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..909ac35 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,390 @@ +import { ROUTE_NAME } from '@/constant' +import { showNotification } from '@/helper/notification' +import { getUrlFromBackend } from '@/helper/utils' +import router from '@/router' +import { autoUpgradeCore, checkUpgradeCore } from '@/store/settings' +import { activeBackend, activeUuid } from '@/store/setup' +import type { + Backend, + Config, + DNSQuery, + NodeRank, + Proxy, + ProxyProvider, + Rule, + RuleProvider, +} from '@/types' +import axios, { AxiosError } from 'axios' +import { debounce } from 'lodash' +import ReconnectingWebSocket from 'reconnectingwebsocket' +import { computed, nextTick, ref, watch } from 'vue' + +axios.interceptors.request.use((config) => { + config.baseURL = getUrlFromBackend(activeBackend.value!) + config.headers['Authorization'] = 'Bearer ' + activeBackend.value?.password + return config +}) + +const ignoreNotificationUrls = ['/delay', '/weights'] + +axios.interceptors.response.use( + null, + ( + error: AxiosError<{ + message: string + }>, + ) => { + if (error.status === 401 && activeUuid.value) { + const currentBackendUuid = activeUuid.value + activeUuid.value = null + router.push({ + name: ROUTE_NAME.setup, + query: { editBackend: currentBackendUuid }, + }) + nextTick(() => { + showNotification({ content: 'unauthorizedTip' }) + }) + } else if (!ignoreNotificationUrls.some((url) => error.config?.url?.endsWith(url))) { + const errorMessage = error.response?.data?.message || error.message + + showNotification({ + key: errorMessage, + content: `${error.config?.url} \n${errorMessage}`, + type: 'alert-error', + }) + return Promise.reject(error) + } + + return error + }, +) + +export const version = ref() +export const isCoreUpdateAvailable = ref(false) +export const fetchVersionAPI = () => { + return axios.get<{ version: string }>('/version') +} +export const isSingBox = computed(() => version.value?.includes('sing-box')) +export const zashboardVersion = ref(__APP_VERSION__) + +watch( + activeBackend, + async (val) => { + if (val) { + const { data } = await fetchVersionAPI() + + version.value = data?.version || '' + if (isSingBox.value || !checkUpgradeCore.value || activeBackend.value?.disableUpgradeCore) + return + isCoreUpdateAvailable.value = await fetchBackendUpdateAvailableAPI() + + if (isCoreUpdateAvailable.value && autoUpgradeCore.value) { + upgradeCoreAPI('auto') + } + } + }, + { immediate: true }, +) + +export const fetchProxiesAPI = () => { + return axios.get<{ proxies: Record }>('/proxies') +} + +export const selectProxyAPI = (proxyGroup: string, name: string) => { + return axios.put(`/proxies/${encodeURIComponent(proxyGroup)}`, { name }) +} + +export const deleteFixedProxyAPI = (proxyGroup: string) => { + return axios.delete(`/proxies/${encodeURIComponent(proxyGroup)}`) +} + +export const fetchProxyLatencyAPI = (proxyName: string, url: string, timeout: number) => { + return axios.get<{ delay: number }>(`/proxies/${encodeURIComponent(proxyName)}/delay`, { + params: { + url, + timeout, + }, + }) +} + +export const fetchProxyGroupLatencyAPI = (proxyName: string, url: string, timeout: number) => { + return axios.get>(`/group/${encodeURIComponent(proxyName)}/delay`, { + params: { + url, + timeout, + }, + }) +} + +export const fetchSmartWeightsAPI = () => { + return axios.get<{ + message: string + weights: Record + }>(`/group/weights`) +} + +// deprecated +export const fetchSmartGroupWeightsAPI = (proxyName: string) => { + return axios.get<{ + message: string + weights: NodeRank[] + }>(`/group/${encodeURIComponent(proxyName)}/weights`) +} + +export const flushSmartGroupWeightsAPI = () => { + return axios.post(`/cache/smart/flush`) +} + +export const fetchProxyProviderAPI = () => { + return axios.get<{ providers: Record }>('/providers/proxies') +} + +export const updateProxyProviderAPI = (name: string) => { + return axios.put(`/providers/proxies/${encodeURIComponent(name)}`) +} + +export const proxyProviderHealthCheckAPI = (name: string) => { + return axios.get>( + `/providers/proxies/${encodeURIComponent(name)}/healthcheck`, + { + timeout: 15000, + }, + ) +} + +export const fetchRulesAPI = () => { + return axios.get<{ rules: Rule[] }>('/rules') +} + +export const toggleRuleDisabledAPI = (data: Record) => { + return axios.patch(`/rules/disable`, data) +} + +export const toggleRuleDisabledSingBoxAPI = (uuid: string) => { + return axios.put(`/rules/${encodeURIComponent(uuid)}`) +} + +export const fetchRuleProvidersAPI = () => { + return axios.get<{ providers: Record }>('/providers/rules') +} + +export const updateRuleProviderAPI = (name: string) => { + return axios.put(`/providers/rules/${encodeURIComponent(name)}`) +} + +export const blockConnectionByIdAPI = (id: string) => { + return axios.delete(`/connections/smart/${id}`) +} + +export const disconnectByIdAPI = (id: string) => { + return axios.delete(`/connections/${id}`) +} + +export const disconnectAllAPI = () => { + return axios.delete('/connections') +} + +export const getConfigsAPI = () => { + return axios.get('/configs') +} + +export const patchConfigsAPI = (configs: Record) => { + return axios.patch('/configs', configs) +} + +export const flushFakeIPAPI = () => { + return axios.post('/cache/fakeip/flush') +} + +export const flushDNSCacheAPI = () => { + return axios.post('/cache/dns/flush') +} + +export const reloadConfigsAPI = () => { + return axios.put('/configs?reload=true', { path: '', payload: '' }) +} + +export const upgradeUIAPI = () => { + return axios.post('/upgrade/ui') +} + +export const updateGeoDataAPI = () => { + return axios.post('/configs/geo') +} + +export const upgradeCoreAPI = (type: 'release' | 'alpha' | 'auto') => { + const url = type === 'auto' ? '/upgrade' : `/upgrade?channel=${type}` + + return axios.post(url) +} + +export const restartCoreAPI = () => { + return axios.post('/restart') +} + +export const queryDNSAPI = (params: { name: string; type: string }) => { + return axios.get('/dns/query', { + params, + }) +} + +const createWebSocket = (url: string, searchParams?: Record) => { + const backend = activeBackend.value! + const resurl = new URL(`${getUrlFromBackend(backend).replace('http', 'ws')}/${url}`) + + resurl.searchParams.append('token', backend?.password || '') + + if (searchParams) { + Object.entries(searchParams).forEach(([key, value]) => { + resurl.searchParams.append(key, value) + }) + } + + const data = ref() + const websocket = new ReconnectingWebSocket(resurl.toString()) + + const close = () => { + websocket.close() + } + + const messageHandler = ({ data: message }: { data: string }) => { + data.value = JSON.parse(message) + } + + websocket.onmessage = url === 'logs' ? messageHandler : debounce(messageHandler, 100) + + return { + data, + close, + } +} + +export const fetchConnectionsAPI = () => { + return createWebSocket('connections') +} + +export const fetchLogsAPI = (params: Record = {}) => { + return createWebSocket('logs', params) +} + +export const fetchMemoryAPI = () => { + return createWebSocket('memory') +} + +export const fetchTrafficAPI = () => { + return createWebSocket('traffic') +} + +export const isBackendAvailable = async (backend: Backend, timeout: number = 10000) => { + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), timeout) + + try { + const res = await fetch(`${getUrlFromBackend(backend)}/version`, { + method: 'GET', + headers: { + Authorization: `Bearer ${backend.password}`, + }, + signal: controller.signal, + }) + + return res.ok + } catch { + return false + } finally { + clearTimeout(timeoutId) + } +} + +const CACHE_DURATION = 1000 * 60 * 60 + +interface CacheEntry { + timestamp: number + version: string + data: T +} + +async function fetchWithLocalCache(url: string, version: string): Promise { + const cacheKey = 'cache/' + url + const cacheRaw = localStorage.getItem(cacheKey) + + if (cacheRaw) { + try { + const cache: CacheEntry = JSON.parse(cacheRaw) + const now = Date.now() + + if (now - cache.timestamp < CACHE_DURATION && cache.version === version) { + return cache.data + } else { + localStorage.removeItem(cacheKey) + } + } catch (e) { + console.warn('Failed to parse cache for', url, e) + } + } + + const response = await fetch(url) + if (!response.ok) { + throw new Error(`Fetch failed: ${response.status} ${response.statusText}`) + } + + const data: T = await response.json() + const newCache: CacheEntry = { + timestamp: Date.now(), + version, + data, + } + + localStorage.setItem(cacheKey, JSON.stringify(newCache)) + return data +} + +export const fetchIsUIUpdateAvailable = async () => { + const { tag_name } = await fetchWithLocalCache<{ tag_name: string }>( + 'https://api.github.com/repos/Zephyruso/zashboard/releases/latest', + zashboardVersion.value, + ) + + return Boolean(tag_name && tag_name !== `v${zashboardVersion.value}`) +} + +const check = async (url: string, versionNumber: string) => { + const { assets } = await fetchWithLocalCache<{ assets: { name: string }[] }>(url, versionNumber) + const alreadyLatest = assets.some(({ name }) => name.includes(versionNumber)) + + return !alreadyLatest +} + +export const fetchBackendUpdateAvailableAPI = async () => { + const match = /(alpha-smart|alpha|beta|meta)-?(\w+)/.exec(version.value) + + if (!match) { + const { tag_name } = await fetchWithLocalCache<{ tag_name: string }>( + 'https://api.github.com/repos/MetaCubeX/mihomo/releases/latest', + version.value, + ) + + return Boolean(tag_name && !tag_name.endsWith(version.value)) + } + + const channel = match[1], + versionNumber = match[2] + + if (channel === 'meta') + return await check( + 'https://api.github.com/repos/MetaCubeX/mihomo/releases/latest', + versionNumber, + ) + if (channel === 'alpha') + return await check( + 'https://api.github.com/repos/MetaCubeX/mihomo/releases/tags/Prerelease-Alpha', + versionNumber, + ) + if (channel === 'alpha-smart') + return await check( + 'https://api.github.com/repos/vernesong/mihomo/releases/tags/Prerelease-Alpha', + versionNumber, + ) + + return false +} diff --git a/src/api/latency.ts b/src/api/latency.ts new file mode 100644 index 0000000..3429a59 --- /dev/null +++ b/src/api/latency.ts @@ -0,0 +1,37 @@ +const getLatencyFromUrlAPI = (url: string) => { + return new Promise((resolve) => { + const startTime = performance.now() + const img = document.createElement('img') + img.src = url + '?_=' + new Date().getTime() + img.style.display = 'none' + img.onload = () => { + const endTime = performance.now() + img.remove() + + resolve(endTime - startTime) + } + img.onerror = () => { + img.remove() + + resolve(0) + } + + document.body.appendChild(img) + }) +} + +export const getCloudflareLatencyAPI = () => { + return getLatencyFromUrlAPI('https://www.cloudflare.com/favicon.ico') +} + +export const getYouTubeLatencyAPI = () => { + return getLatencyFromUrlAPI('https://yt3.ggpht.com/favicon.ico') +} + +export const getGithubLatencyAPI = () => { + return getLatencyFromUrlAPI('https://github.githubassets.com/favicon.ico') +} + +export const getBaiduLatencyAPI = () => { + return getLatencyFromUrlAPI('https://apps.bdimg.com/favicon.ico') +} diff --git a/src/assets/NotoColorEmoji-flagsonly.ttf b/src/assets/NotoColorEmoji-flagsonly.ttf new file mode 100644 index 0000000..6e1642e Binary files /dev/null and b/src/assets/NotoColorEmoji-flagsonly.ttf differ diff --git a/src/assets/TwemojiMozilla-flags.woff2 b/src/assets/TwemojiMozilla-flags.woff2 new file mode 100644 index 0000000..b87c4ce Binary files /dev/null and b/src/assets/TwemojiMozilla-flags.woff2 differ diff --git a/src/assets/load-fonts.ts b/src/assets/load-fonts.ts new file mode 100644 index 0000000..182e847 --- /dev/null +++ b/src/assets/load-fonts.ts @@ -0,0 +1,33 @@ +export const loadFonts = () => { + if (import.meta.env.MODE === 'cdn-fonts') { + const createLink = (href: string) => { + const link = document.createElement('link') + link.rel = 'stylesheet' + link.href = href + link.media = 'print' + link.onload = () => { + link.media = 'all' + } + document.head.appendChild(link) + } + + createLink('https://unpkg.com/subsetted-fonts@latest/MiSans-VF/MiSans-VF.css') + createLink('https://unpkg.com/subsetted-fonts@latest/SarasaUiSC-Regular/SarasaUiSC-Regular.css') + createLink('https://unpkg.com/subsetted-fonts@latest/PingFangSC-Regular/PingFangSC-Regular.css') + createLink('https://unpkg.com/@fontsource/fira-sans') + } else if (import.meta.env.MODE === 'MiSans') { + import('subsetted-fonts/MiSans-VF/MiSans-VF.css') + } else if (import.meta.env.MODE === 'SarasaUi') { + import('subsetted-fonts/SarasaUiSC-Regular/SarasaUiSC-Regular.css') + } else if (import.meta.env.MODE === 'PingFang') { + import('subsetted-fonts/PingFangSC-Regular/PingFangSC-Regular.css') + } else if (import.meta.env.MODE === 'FiraSans') { + import('@fontsource/fira-sans/index.css') + } else if (import.meta.env.MODE === 'SystemUI') { + } else { + import('@fontsource/fira-sans/index.css') + import('subsetted-fonts/MiSans-VF/MiSans-VF.css') + import('subsetted-fonts/SarasaUiSC-Regular/SarasaUiSC-Regular.css') + import('subsetted-fonts/PingFangSC-Regular/PingFangSC-Regular.css') + } +} diff --git a/src/assets/main.css b/src/assets/main.css new file mode 100644 index 0000000..763c19b --- /dev/null +++ b/src/assets/main.css @@ -0,0 +1,376 @@ +@import 'tailwindcss'; + +@config '../../tailwind.config.ts'; +@plugin 'daisyui' { + themes: all; + exclude: rootscrollgutter; +} + +@theme { + --font-MiSans-NotoEmoji: 'MiSans-VF', 'NotoEmoji', system-ui; + --font-SarasaUI-NotoEmoji: 'SarasaUiSC-Regular', 'NotoEmoji', system-ui; + --font-PingFang-NotoEmoji: 'PingFangSC-Regular', 'NotoEmoji', system-ui; + --font-FiraSans-NotoEmoji: 'Fira Sans', 'NotoEmoji', system-ui; + --font-SystemUI-NotoEmoji: 'NotoEmoji', system-ui; + + --font-MiSans-Twemoji: 'MiSans-VF', 'Twemoji', system-ui; + --font-SarasaUI-Twemoji: 'SarasaUiSC-Regular', 'Twemoji', system-ui; + --font-PingFang-Twemoji: 'PingFangSC-Regular', 'Twemoji', system-ui; + --font-FiraSans-Twemoji: 'Fira Sans', 'Twemoji', system-ui; + --font-SystemUI-Twemoji: 'Twemoji', system-ui; +} + +@layer utilities { + @font-face { + font-family: 'NotoEmoji'; + src: url('./NotoColorEmoji-flagsonly.ttf') format('truetype'); + } +} + +@layer utilities { + @font-face { + font-family: 'Twemoji'; + src: url('./TwemojiMozilla-flags.woff2') format('woff2'); + } +} +@utility card { + @apply bg-base-100 rounded-lg shadow-md; + .card-body { + @apply p-4; + } +} + +@utility collapse { + @apply bg-base-100 rounded-lg shadow-md; +} + +.collapse.transparent-collapse { + @apply bg-transparent! shadow-none! backdrop-blur-none!; +} + +@utility badge { + @apply bg-base-200/80; +} + +@utility select { + @apply w-auto; +} + +@utility btn { + @apply border-none; +} + +@utility tabs { + .tab { + @apply px-2; + } + .tab.tab-active { + @apply bg-primary text-primary-content; + } +} + +@utility tab { + @apply max-md:min-w-24; +} + +@utility table { + & tr, + & th, + & td { + @apply border-0; + } + @apply border-separate border-spacing-0; +} + +@utility modal { + @apply max-md:bg-transparent! max-md:backdrop-blur-sm max-md:transition-[backdrop-filter]; +} + +.select.select-sm, +.input.input-sm, +.btn.btn-sm, +.tabs.tabs-xs .tab { + @apply text-sm; +} + +@for $i from 0 to 100 { + .custom-background.custom-background-$(i) { + .bg-base-100, + .bg-primary, + .card, + .collapse, + .input, + .select, + .toggle, + .settings-menu, + .ctrls-bar, + .tabs { + @apply bg-base-100/$(i); + } + + .badge { + @apply bg-base-200/$(i) border-base-200/$(i); + } + + option, + optgroup { + @apply backdrop-blur-sm; + } + + .bg-base-200, + .dock, + .table tbody tr:nth-child(even) { + @apply bg-base-200/$(i); + } + + .modal-box { + @if $i > 70 { + @apply bg-base-100/$(i); + } @else { + @apply bg-base-100/70; + } + } + + .bg-primary:not(.tab.tab-active) { + @if $i > 60 { + @apply bg-primary/$(i); + } @else { + @apply bg-primary/60; + } + } + + .bg-primary .latency-tag { + @if $i > 90 { + @apply bg-base-100/$(i); + } @else { + @apply bg-base-100/90; + } + } + + .home-page.bg-base-200\/50 { + background-color: transparent; + } + } +} + +@for $i from 0 to 40 { + .blur-intensity-$(i) { + .card, + .collapse, + .sidebar, + .nav-bar, + .modal-box, + .need-blur, + .table thead { + /* prettier-ignore */ + backdrop-filter: blur($(i)px); + } + + .pinned-td { + @apply bg-transparent! backdrop-blur-xl; + } + + .table tbody::before { + position: absolute; + content: ''; + top: 0; + left: 0; + width: 100%; + height: 100%; + /* prettier-ignore */ + backdrop-filter: blur($(i)px); + } + } +} + +.tippy-box { + @apply bg-neutral text-neutral-content z-[9999] whitespace-pre-line shadow-md; +} +.tippy-box[data-placement^='top'] > .tippy-arrow:before { + @apply border-t-neutral; +} +.tippy-box[data-placement^='right'] > .tippy-arrow:before { + @apply border-r-neutral; +} +.tippy-box[data-placement^='bottom'] > .tippy-arrow:before { + @apply border-b-neutral; +} +.tippy-box[data-placement^='left'] > .tippy-arrow:before { + @apply border-l-neutral; +} + +.tippy-box[data-theme^='base'] { + @apply bg-base-100 border-base-200 text-base-content z-9999 border shadow-md; +} +.tippy-box[data-theme^='base'][data-placement^='top'] > .tippy-arrow:before { + @apply border-t-base-100; +} +.tippy-box[data-theme^='base'][data-placement^='right'] > .tippy-arrow:before { + @apply border-r-base-100; +} +.tippy-box[data-theme^='base'][data-placement^='bottom'] > .tippy-arrow:before { + @apply border-b-base-100; +} +.tippy-box[data-theme^='base'][data-placement^='left'] > .tippy-arrow:before { + @apply border-l-base-100; +} + +.tippy-box[data-theme^='transparent'] { + @apply rounded-lg; + .tippy-content { + @apply p-0; + } +} +.tippy-box[data-theme^='transparent'][data-placement^='top'] { + @apply -mb-2; +} +.tippy-box[data-theme^='transparent'][data-placement^='right'] { + @apply -ml-2; +} +.tippy-box[data-theme^='transparent'][data-placement^='bottom'] { + @apply -mt-2; +} +.tippy-box[data-theme^='transparent'][data-placement^='left'] { + @apply -mr-2; +} + +.slide-right-enter-active, +.slide-right-leave-active, +.slide-left-enter-active, +.slide-left-leave-active { + transition: all 0.2s ease-in-out; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; +} + +.slide-left-enter-from { + transform: translateX(100%); +} +.slide-right-enter-from { + transform: translateX(-100%); +} + +.slide-left-leave-from, +.slide-right-leave-from, +.slide-left-leave-to, +.slide-right-leave-to { + display: none; +} + +.slide-left-enter-to, +.slide-right-enter-to { + transform: translateX(0); +} + +div { + @apply scrollbar-thin; +} + +.text-main { + @apply text-primary; +} + +[data-theme='dark'] .text-main { + color: oklch(72% 0.233 277.117); +} + +[data-theme='lofi'] .text-main, +[data-theme='pastel'] .text-main, +[data-theme='wireframe'] .text-main, +[data-theme='business'] .text-main, +[data-theme='black'] .text-main { + @apply text-info; +} + +@keyframes bounceIn { + 0% { + opacity: 0.5; + transform: scale(0.85); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +@keyframes bounceInForPC { + 0% { + opacity: 0.5; + transform: scale(0.97); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +.bounce-in { + animation: bounceIn 0.4s ease-out; +} + +@media screen and (min-width: 1024px) { + .bounce-in { + animation: bounceInForPC 0.4s ease-out; + } +} + +@keyframes progressBar { + from { + width: 100%; + } + to { + width: 0%; + } +} + +.dock-shadow { + background: linear-gradient( + to top, + rgba(0, 0, 0, 0.3), + rgba(0, 0, 0, 0.16), + rgba(0, 0, 0, 0.08), + rgba(0, 0, 0, 0.02), + rgba(0, 0, 0, 0) + ); + width: 100%; + height: env(safe-area-inset-bottom); + position: fixed; + bottom: 0; + z-index: 10; +} +.ctrls-bar { + @media screen and (max-width: 768px) { + background: linear-gradient( + to bottom, + var(--color-base-100), + color-mix(in srgb, var(--color-base-100) 10%, transparent) + ); + } + @apply md:bg-base-100/20 fixed top-0 right-0 left-0 z-30 shadow-sm backdrop-blur-lg md:sticky; +} + +.dock { + @apply absolute right-2 left-2 z-30 rounded-3xl; +} + +.settings-title { + @apply flex items-center gap-2 py-2 text-lg font-bold; +} +.settings-grid { + @apply grid max-w-7xl grid-cols-1 gap-x-4 gap-y-1 lg:grid-cols-2 lg:gap-x-8 xl:gap-x-12 2xl:gap-x-16; +} +.sidebar-collapsed .settings-grid { + @apply md:grid-cols-2; +} +.setting-item { + @apply border-base-300/80 flex h-10 items-center gap-2 border-b; + .setting-item-label { + @apply flex-1 text-sm font-medium; + svg { + @apply inline-block; + } + } +} diff --git a/src/assets/metacubex.jpg b/src/assets/metacubex.jpg new file mode 100644 index 0000000..8177892 Binary files /dev/null and b/src/assets/metacubex.jpg differ diff --git a/src/assets/sing-box.svg b/src/assets/sing-box.svg new file mode 100644 index 0000000..146d085 --- /dev/null +++ b/src/assets/sing-box.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/theme.css b/src/assets/theme.css new file mode 100644 index 0000000..bf491ab --- /dev/null +++ b/src/assets/theme.css @@ -0,0 +1,48 @@ +@plugin "daisyui/theme" { + name: 'dark-legacy'; + --color-primary: oklch(65.69% 0.196 275.75); + --color-primary-content: #050617; + --color-secondary: oklch(74.8% 0.26 342.55); + --color-secondary-content: #190211; + --color-accent: oklch(74.51% 0.167 183.61); + --color-accent-content: #000e0c; + --color-neutral: #2a323c; + --color-neutral-content: #a6adbb; + --color-base-100: #1d232a; + --color-base-200: #191e24; + --color-base-300: #15191e; + --color-base-content: #a6adbb; + --radius-selector: 1rem; + --radius-field: 0.5rem; + --radius-box: 1rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 0; + --noise: 0; + color-scheme: dark; +} + +@plugin "daisyui/theme" { + name: 'light-legacy'; + --color-primary: oklch(49.12% 0.3096 275.75); + --color-primary-content: #d4dbff; + --color-secondary: oklch(69.71% 0.329 342.55); + --color-secondary-content: oklch(98.71% 0.0106 342.55); + --color-accent: oklch(76.76% 0.184 183.61); + --color-accent-content: #00100d; + --color-neutral: #2b3440; + --color-neutral-content: #d7dde4; + --color-base-100: oklch(100% 0 0); + --color-base-200: #f2f2f2; + --color-base-300: #e5e6e6; + --color-base-content: #1f2937; + --radius-selector: 1rem; + --radius-field: 0.5rem; + --radius-box: 1rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 0; + --noise: 0; +} diff --git a/src/components/common/BackendVersion.vue b/src/components/common/BackendVersion.vue new file mode 100644 index 0000000..24f6cb1 --- /dev/null +++ b/src/components/common/BackendVersion.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/common/CollapseCard.vue b/src/components/common/CollapseCard.vue new file mode 100644 index 0000000..37c083d --- /dev/null +++ b/src/components/common/CollapseCard.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/components/common/DialogWrapper.vue b/src/components/common/DialogWrapper.vue new file mode 100644 index 0000000..3f684fe --- /dev/null +++ b/src/components/common/DialogWrapper.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/components/common/ImportSettings.vue b/src/components/common/ImportSettings.vue new file mode 100644 index 0000000..4c8ab5d --- /dev/null +++ b/src/components/common/ImportSettings.vue @@ -0,0 +1,131 @@ + + + diff --git a/src/components/common/ProxyChains.vue b/src/components/common/ProxyChains.vue new file mode 100644 index 0000000..7fb77e8 --- /dev/null +++ b/src/components/common/ProxyChains.vue @@ -0,0 +1,29 @@ + + + diff --git a/src/components/common/TextInput.vue b/src/components/common/TextInput.vue new file mode 100644 index 0000000..9d06b4d --- /dev/null +++ b/src/components/common/TextInput.vue @@ -0,0 +1,130 @@ + + + diff --git a/src/components/common/VirtualScroller.vue b/src/components/common/VirtualScroller.vue new file mode 100644 index 0000000..c97303e --- /dev/null +++ b/src/components/common/VirtualScroller.vue @@ -0,0 +1,94 @@ + + + diff --git a/src/components/connections/ConnectionCard.tsx b/src/components/connections/ConnectionCard.tsx new file mode 100644 index 0000000..893f408 --- /dev/null +++ b/src/components/connections/ConnectionCard.tsx @@ -0,0 +1,195 @@ +import { blockConnectionByIdAPI, disconnectByIdAPI } from '@/api' +import { useBounceOnVisible } from '@/composables/bouncein' +import { useConnections } from '@/composables/connections' +import { + CONNECTION_TAB_TYPE, + CONNECTIONS_TABLE_ACCESSOR_KEY, + PROXY_CHAIN_DIRECTION, +} from '@/constant' +import { + getDestinationFromConnection, + getDestinationTypeFromConnection, + getHostFromConnection, + getInboundUserFromConnection, + getNetworkTypeFromConnection, + getProcessFromConnection, +} from '@/helper' +import { getIPLabelFromMap } from '@/helper/sourceip' +import { fromNow, prettyBytesHelper } from '@/helper/utils' +import { connectionTabShow } from '@/store/connections' +import { connectionCardLines, proxyChainDirection } from '@/store/settings' +import type { Connection } from '@/types' +import { + ArrowDownCircleIcon, + ArrowDownIcon, + ArrowRightCircleIcon, + ArrowUpCircleIcon, + ArrowUpIcon, + NoSymbolIcon, + XMarkIcon, +} from '@heroicons/vue/24/outline' +import { first, last } from 'lodash' +import { defineComponent } from 'vue' +import type { JSX } from 'vue/jsx-runtime' +import ProxyName from '../proxies/ProxyName.vue' + +export default defineComponent<{ + conn: Connection +}>({ + props: { + conn: Object, + }, + name: 'ConnectionCard', + setup(props) { + const { handlerInfo } = useConnections() + + useBounceOnVisible() + + return () => { + const conn = props.conn + const metadata = conn.metadata + const componentMap: Record = { + [CONNECTIONS_TABLE_ACCESSOR_KEY.Host]: ( + {getHostFromConnection(conn)} + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.Destination]: ( + {getDestinationFromConnection(conn)} + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.RemoteAddress]: ( + {conn.metadata.remoteDestination || '-'} + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.SourceIP]: ( + {getIPLabelFromMap(metadata.sourceIP)} + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.SourcePort]: ( + {metadata.sourcePort} + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.SniffHost]: ( + {metadata.sniffHost || '-'} + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.Type]: ( + {getNetworkTypeFromConnection(conn)} + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.Rule]: ( + + {conn.rule} + {conn.rulePayload && <>: {conn.rulePayload}} + + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.Process]: ( + {getProcessFromConnection(conn)} + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.Chains]: ( + + {} + {last(conn.chains) !== first(conn.chains) && ( + <> + + {} + + )} + + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.Outbound]: ( + {conn.chains[0]} + ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.Download]: ( +
+ {prettyBytesHelper(conn.download)} + +
+ ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.Upload]: ( +
+ {prettyBytesHelper(conn.upload)} + +
+ ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.DlSpeed]: ( +
+ {prettyBytesHelper(conn.downloadSpeed)}/s + +
+ ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.UlSpeed]: ( +
+ {prettyBytesHelper(conn.uploadSpeed)}/s + +
+ ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.ConnectTime]: ( +
{fromNow(conn.start)}
+ ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.DestinationType]: ( +
{getDestinationTypeFromConnection(conn)}
+ ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.InboundUser]: ( +
{getInboundUserFromConnection(conn)}
+ ), + [CONNECTIONS_TABLE_ACCESSOR_KEY.Close]: (() => { + const closeButton = ( + + ) + + if (metadata.smartBlock === 'normal') { + const degradeButton = ( + + ) + return ( +
+ {degradeButton} + {closeButton} +
+ ) + } + return closeButton + })(), + } + return ( +
2 ? 'p-2' : 'p-1', + ]} + onClick={() => handlerInfo(conn)} + > + {connectionCardLines.value.map((line) => ( +
+ {line + .filter( + (key) => + key !== CONNECTIONS_TABLE_ACCESSOR_KEY.Close || + connectionTabShow.value !== CONNECTION_TAB_TYPE.CLOSED, + ) + .map((key) => { + return componentMap[key] + })} +
+ ))} +
+ ) + } + }, +}) diff --git a/src/components/connections/ConnectionCardList.vue b/src/components/connections/ConnectionCardList.vue new file mode 100644 index 0000000..1026a67 --- /dev/null +++ b/src/components/connections/ConnectionCardList.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/components/connections/ConnectionDetails.vue b/src/components/connections/ConnectionDetails.vue new file mode 100644 index 0000000..bbe0c31 --- /dev/null +++ b/src/components/connections/ConnectionDetails.vue @@ -0,0 +1,132 @@ + + + diff --git a/src/components/connections/ConnectionTable.vue b/src/components/connections/ConnectionTable.vue new file mode 100644 index 0000000..9f01624 --- /dev/null +++ b/src/components/connections/ConnectionTable.vue @@ -0,0 +1,711 @@ + + + + + diff --git a/src/components/logs/LogsCard.vue b/src/components/logs/LogsCard.vue new file mode 100644 index 0000000..3060e22 --- /dev/null +++ b/src/components/logs/LogsCard.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/components/overview/BasicCharts.vue b/src/components/overview/BasicCharts.vue new file mode 100644 index 0000000..a31d28e --- /dev/null +++ b/src/components/overview/BasicCharts.vue @@ -0,0 +1,216 @@ + + + diff --git a/src/components/overview/ChartsCard.vue b/src/components/overview/ChartsCard.vue new file mode 100644 index 0000000..74ddfa4 --- /dev/null +++ b/src/components/overview/ChartsCard.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/components/overview/ConnectionHistory.vue b/src/components/overview/ConnectionHistory.vue new file mode 100644 index 0000000..cf8b8bd --- /dev/null +++ b/src/components/overview/ConnectionHistory.vue @@ -0,0 +1,439 @@ + + + diff --git a/src/components/overview/ConnectionStatus.vue b/src/components/overview/ConnectionStatus.vue new file mode 100644 index 0000000..b373b30 --- /dev/null +++ b/src/components/overview/ConnectionStatus.vue @@ -0,0 +1,82 @@ + + + diff --git a/src/components/overview/ConnectionsCharts.vue b/src/components/overview/ConnectionsCharts.vue new file mode 100644 index 0000000..107eb63 --- /dev/null +++ b/src/components/overview/ConnectionsCharts.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/components/overview/IPCheck.vue b/src/components/overview/IPCheck.vue new file mode 100644 index 0000000..df22cf6 --- /dev/null +++ b/src/components/overview/IPCheck.vue @@ -0,0 +1,123 @@ + + + diff --git a/src/components/overview/MemoryCharts.vue b/src/components/overview/MemoryCharts.vue new file mode 100644 index 0000000..302e910 --- /dev/null +++ b/src/components/overview/MemoryCharts.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/components/overview/NetworkCard.vue b/src/components/overview/NetworkCard.vue new file mode 100644 index 0000000..2f8e123 --- /dev/null +++ b/src/components/overview/NetworkCard.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/components/overview/OverviewCardSettingsDialog.vue b/src/components/overview/OverviewCardSettingsDialog.vue new file mode 100644 index 0000000..1386818 --- /dev/null +++ b/src/components/overview/OverviewCardSettingsDialog.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/src/components/overview/ProviderTrafficOverview.vue b/src/components/overview/ProviderTrafficOverview.vue new file mode 100644 index 0000000..06d5053 --- /dev/null +++ b/src/components/overview/ProviderTrafficOverview.vue @@ -0,0 +1,154 @@ + + + diff --git a/src/components/overview/RuleHitCountCard.vue b/src/components/overview/RuleHitCountCard.vue new file mode 100644 index 0000000..158ed02 --- /dev/null +++ b/src/components/overview/RuleHitCountCard.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/components/overview/RuleHitCountChart.vue b/src/components/overview/RuleHitCountChart.vue new file mode 100644 index 0000000..c0f1409 --- /dev/null +++ b/src/components/overview/RuleHitCountChart.vue @@ -0,0 +1,282 @@ + + + diff --git a/src/components/overview/SpeedCharts.vue b/src/components/overview/SpeedCharts.vue new file mode 100644 index 0000000..f64e099 --- /dev/null +++ b/src/components/overview/SpeedCharts.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/components/overview/StatisticsStats.vue b/src/components/overview/StatisticsStats.vue new file mode 100644 index 0000000..b5f8252 --- /dev/null +++ b/src/components/overview/StatisticsStats.vue @@ -0,0 +1,72 @@ + + + diff --git a/src/components/overview/TopologyCharts.vue b/src/components/overview/TopologyCharts.vue new file mode 100644 index 0000000..4c54e3f --- /dev/null +++ b/src/components/overview/TopologyCharts.vue @@ -0,0 +1,465 @@ + + + diff --git a/src/components/proxies/LatencyTag.vue b/src/components/proxies/LatencyTag.vue new file mode 100644 index 0000000..166d78e --- /dev/null +++ b/src/components/proxies/LatencyTag.vue @@ -0,0 +1,103 @@ + + + diff --git a/src/components/proxies/ProxiesByProvider.vue b/src/components/proxies/ProxiesByProvider.vue new file mode 100644 index 0000000..48f1675 --- /dev/null +++ b/src/components/proxies/ProxiesByProvider.vue @@ -0,0 +1,100 @@ + + + diff --git a/src/components/proxies/ProxiesContent.vue b/src/components/proxies/ProxiesContent.vue new file mode 100644 index 0000000..55bc720 --- /dev/null +++ b/src/components/proxies/ProxiesContent.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/components/proxies/ProxyGroup.vue b/src/components/proxies/ProxyGroup.vue new file mode 100644 index 0000000..9007109 --- /dev/null +++ b/src/components/proxies/ProxyGroup.vue @@ -0,0 +1,135 @@ + + + diff --git a/src/components/proxies/ProxyGroupForMobile.vue b/src/components/proxies/ProxyGroupForMobile.vue new file mode 100644 index 0000000..f057e63 --- /dev/null +++ b/src/components/proxies/ProxyGroupForMobile.vue @@ -0,0 +1,257 @@ + + + diff --git a/src/components/proxies/ProxyGroupNow.vue b/src/components/proxies/ProxyGroupNow.vue new file mode 100644 index 0000000..5580a91 --- /dev/null +++ b/src/components/proxies/ProxyGroupNow.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/components/proxies/ProxyIcon.vue b/src/components/proxies/ProxyIcon.vue new file mode 100644 index 0000000..cfc215c --- /dev/null +++ b/src/components/proxies/ProxyIcon.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/components/proxies/ProxyName.vue b/src/components/proxies/ProxyName.vue new file mode 100644 index 0000000..7ed5767 --- /dev/null +++ b/src/components/proxies/ProxyName.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/components/proxies/ProxyNodeCard.vue b/src/components/proxies/ProxyNodeCard.vue new file mode 100644 index 0000000..84e0c06 --- /dev/null +++ b/src/components/proxies/ProxyNodeCard.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/src/components/proxies/ProxyNodeGrid.vue b/src/components/proxies/ProxyNodeGrid.vue new file mode 100644 index 0000000..ebbaf41 --- /dev/null +++ b/src/components/proxies/ProxyNodeGrid.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/components/proxies/ProxyPreview.vue b/src/components/proxies/ProxyPreview.vue new file mode 100644 index 0000000..1515db9 --- /dev/null +++ b/src/components/proxies/ProxyPreview.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/src/components/proxies/ProxyProvider.vue b/src/components/proxies/ProxyProvider.vue new file mode 100644 index 0000000..6044042 --- /dev/null +++ b/src/components/proxies/ProxyProvider.vue @@ -0,0 +1,145 @@ + + + diff --git a/src/components/rules/RuleCard.vue b/src/components/rules/RuleCard.vue new file mode 100644 index 0000000..4b26eb1 --- /dev/null +++ b/src/components/rules/RuleCard.vue @@ -0,0 +1,292 @@ + + + diff --git a/src/components/rules/RuleProvider.vue b/src/components/rules/RuleProvider.vue new file mode 100644 index 0000000..29a116b --- /dev/null +++ b/src/components/rules/RuleProvider.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/components/settings/BackendSettings.vue b/src/components/settings/BackendSettings.vue new file mode 100644 index 0000000..447f97b --- /dev/null +++ b/src/components/settings/BackendSettings.vue @@ -0,0 +1,373 @@ + + + diff --git a/src/components/settings/BackendSwitch.vue b/src/components/settings/BackendSwitch.vue new file mode 100644 index 0000000..ffe1460 --- /dev/null +++ b/src/components/settings/BackendSwitch.vue @@ -0,0 +1,73 @@ + + + diff --git a/src/components/settings/ConnectionCardSettings.vue b/src/components/settings/ConnectionCardSettings.vue new file mode 100644 index 0000000..864d331 --- /dev/null +++ b/src/components/settings/ConnectionCardSettings.vue @@ -0,0 +1,102 @@ + + + diff --git a/src/components/settings/ConnectionsSettings.vue b/src/components/settings/ConnectionsSettings.vue new file mode 100644 index 0000000..ed96757 --- /dev/null +++ b/src/components/settings/ConnectionsSettings.vue @@ -0,0 +1,130 @@ + + + diff --git a/src/components/settings/CustomTheme.vue b/src/components/settings/CustomTheme.vue new file mode 100644 index 0000000..9868aa5 --- /dev/null +++ b/src/components/settings/CustomTheme.vue @@ -0,0 +1,212 @@ + + + diff --git a/src/components/settings/DnsQuery.vue b/src/components/settings/DnsQuery.vue new file mode 100644 index 0000000..0b118b1 --- /dev/null +++ b/src/components/settings/DnsQuery.vue @@ -0,0 +1,81 @@ + + + diff --git a/src/components/settings/EditBackendModal.vue b/src/components/settings/EditBackendModal.vue new file mode 100644 index 0000000..8d09043 --- /dev/null +++ b/src/components/settings/EditBackendModal.vue @@ -0,0 +1,227 @@ + + + diff --git a/src/components/settings/GeneralSettings.vue b/src/components/settings/GeneralSettings.vue new file mode 100644 index 0000000..7d13309 --- /dev/null +++ b/src/components/settings/GeneralSettings.vue @@ -0,0 +1,189 @@ + + + diff --git a/src/components/settings/GroupTestUrlsSettings.vue b/src/components/settings/GroupTestUrlsSettings.vue new file mode 100644 index 0000000..7afdb2d --- /dev/null +++ b/src/components/settings/GroupTestUrlsSettings.vue @@ -0,0 +1,127 @@ + + + diff --git a/src/components/settings/IconSettings.vue b/src/components/settings/IconSettings.vue new file mode 100644 index 0000000..caad577 --- /dev/null +++ b/src/components/settings/IconSettings.vue @@ -0,0 +1,111 @@ + + + diff --git a/src/components/settings/LanguageSelect.vue b/src/components/settings/LanguageSelect.vue new file mode 100644 index 0000000..53eda82 --- /dev/null +++ b/src/components/settings/LanguageSelect.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/components/settings/OverviewCard.vue b/src/components/settings/OverviewCard.vue new file mode 100644 index 0000000..d582290 --- /dev/null +++ b/src/components/settings/OverviewCard.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/components/settings/OverviewSettings.vue b/src/components/settings/OverviewSettings.vue new file mode 100644 index 0000000..52dace7 --- /dev/null +++ b/src/components/settings/OverviewSettings.vue @@ -0,0 +1,120 @@ + + + diff --git a/src/components/settings/ProxiesSettings.vue b/src/components/settings/ProxiesSettings.vue new file mode 100644 index 0000000..2e5af4f --- /dev/null +++ b/src/components/settings/ProxiesSettings.vue @@ -0,0 +1,329 @@ + + + diff --git a/src/components/settings/SettingsMenu.vue b/src/components/settings/SettingsMenu.vue new file mode 100644 index 0000000..973e40e --- /dev/null +++ b/src/components/settings/SettingsMenu.vue @@ -0,0 +1,169 @@ + + + diff --git a/src/components/settings/SettingsVisibilityDialog.vue b/src/components/settings/SettingsVisibilityDialog.vue new file mode 100644 index 0000000..e998b6d --- /dev/null +++ b/src/components/settings/SettingsVisibilityDialog.vue @@ -0,0 +1,367 @@ + + + + + diff --git a/src/components/settings/SourceIPInput.vue b/src/components/settings/SourceIPInput.vue new file mode 100644 index 0000000..2bb8660 --- /dev/null +++ b/src/components/settings/SourceIPInput.vue @@ -0,0 +1,140 @@ + + + diff --git a/src/components/settings/SourceIPLabels.vue b/src/components/settings/SourceIPLabels.vue new file mode 100644 index 0000000..73efe27 --- /dev/null +++ b/src/components/settings/SourceIPLabels.vue @@ -0,0 +1,133 @@ + + + diff --git a/src/components/settings/TableSettings.vue b/src/components/settings/TableSettings.vue new file mode 100644 index 0000000..3b0c50a --- /dev/null +++ b/src/components/settings/TableSettings.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/components/settings/ThemeSelector.vue b/src/components/settings/ThemeSelector.vue new file mode 100644 index 0000000..f053c24 --- /dev/null +++ b/src/components/settings/ThemeSelector.vue @@ -0,0 +1,69 @@ + + + diff --git a/src/components/settings/UpgradeCoreModal.vue b/src/components/settings/UpgradeCoreModal.vue new file mode 100644 index 0000000..becd078 --- /dev/null +++ b/src/components/settings/UpgradeCoreModal.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/components/settings/ZashboardSettings.vue b/src/components/settings/ZashboardSettings.vue new file mode 100644 index 0000000..c6dc0d2 --- /dev/null +++ b/src/components/settings/ZashboardSettings.vue @@ -0,0 +1,389 @@ + + + diff --git a/src/components/sidebar/CommonCtrl.vue b/src/components/sidebar/CommonCtrl.vue new file mode 100644 index 0000000..32914d9 --- /dev/null +++ b/src/components/sidebar/CommonCtrl.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/components/sidebar/ConnectionCtrl.tsx b/src/components/sidebar/ConnectionCtrl.tsx new file mode 100644 index 0000000..6ec3588 --- /dev/null +++ b/src/components/sidebar/ConnectionCtrl.tsx @@ -0,0 +1,240 @@ +import { disconnectAllAPI, disconnectByIdAPI } from '@/api' +import { useCtrlsBar } from '@/composables/useCtrlsBar' +import { ROUTE_NAME, SETTINGS_MENU_KEY, SORT_DIRECTION, SORT_TYPE } from '@/constant' +import { useTooltip } from '@/helper/tooltip' +import { + connectionFilter, + connections, + connectionSortDirection, + connectionSortType, + isPaused, + quickFilterEnabled, + quickFilterRegex, + renderConnections, +} from '@/store/connections' +import { useConnectionCard } from '@/store/settings' +import { + ArrowDownCircleIcon, + ArrowUpCircleIcon, + LinkIcon, + LinkSlashIcon, + PauseIcon, + PlayIcon, + QuestionMarkCircleIcon, + WrenchScrewdriverIcon, + XMarkIcon, +} from '@heroicons/vue/24/outline' +import { defineComponent, ref } from 'vue' +import { useI18n } from 'vue-i18n' +import { useRouter } from 'vue-router' +import DialogWrapper from '../common/DialogWrapper.vue' +import TextInput from '../common/TextInput.vue' +import ConnectionCardSettings from '../settings/ConnectionCardSettings.vue' +import TableSettings from '../settings/TableSettings.vue' +import ConnectionTabs from './ConnectionTabs.vue' +import SourceIPFilter from './SourceIPFilter.vue' + +const handlerClickCloseAll = () => { + if (renderConnections.value.length === connections.value.length) { + disconnectAllAPI() + } else { + renderConnections.value.forEach((conn) => { + disconnectByIdAPI(conn.id) + }) + } +} + +export default defineComponent({ + name: 'ConnectionCtrl', + components: { + TextInput, + ConnectionTabs, + SourceIPFilter, + }, + setup() { + const { t } = useI18n() + const router = useRouter() + const settingsModel = ref(false) + const { showTip, updateTip } = useTooltip() + const { isLargeCtrlsBar } = useCtrlsBar(useConnectionCard.value ? 860 : 720) + + return () => { + const sortForCards = ( +
+ {t('sortBy')} +
+ + +
+
+ ) + + const settingsModal = ( + <> + + +
+
+ {t('hideConnectionRegex')} + +
+
+ {t('hideConnection')} + +
+ showTip(e, t('hideConnectionTip'), { + appendTo: 'parent', + }) + } + > + +
+
+ {useConnectionCard.value ? : } +
+ +
+
+ + ) + + const searchInput = ( + + ) + + const buttons = ( + <> + + + + + ) + + const content = !isLargeCtrlsBar.value ? ( +
+
+ + {!useConnectionCard.value && ( +
+ {settingsModal} + {buttons} +
+ )} +
+ {useConnectionCard.value && ( +
+ {sortForCards} + {settingsModal} + {buttons} +
+ )} +
+ + {searchInput} +
+
+ ) : ( +
+ + {useConnectionCard.value && sortForCards} + +
{searchInput}
+ {settingsModal} + {buttons} +
+ ) + + return
{content}
+ } + }, +}) diff --git a/src/components/sidebar/ConnectionTabs.vue b/src/components/sidebar/ConnectionTabs.vue new file mode 100644 index 0000000..3d5f8c6 --- /dev/null +++ b/src/components/sidebar/ConnectionTabs.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/components/sidebar/LogsCtrl.tsx b/src/components/sidebar/LogsCtrl.tsx new file mode 100644 index 0000000..70d7809 --- /dev/null +++ b/src/components/sidebar/LogsCtrl.tsx @@ -0,0 +1,316 @@ +import { isSingBox } from '@/api' +import { useCtrlsBar } from '@/composables/useCtrlsBar' +import { LOG_LEVEL } from '@/constant' +import { useTooltip } from '@/helper/tooltip' +import { + initLogs, + isPaused, + logFilter, + logFilterEnabled, + logFilterRegex, + logLevel, + logTypeFilter, + logs, +} from '@/store/logs' +import { logRetentionLimit, logSearchHistory } from '@/store/settings' +import { + ArrowDownTrayIcon, + LinkIcon, + LinkSlashIcon, + PauseIcon, + PlayIcon, + QuestionMarkCircleIcon, + WrenchScrewdriverIcon, + XMarkIcon, +} from '@heroicons/vue/24/outline' +import dayjs from 'dayjs' +import { debounce } from 'lodash' +import { computed, defineComponent, ref, watch } from 'vue' +import { useI18n } from 'vue-i18n' +import DialogWrapper from '../common/DialogWrapper.vue' +import TextInput from '../common/TextInput.vue' + +export default defineComponent({ + setup() { + const { t } = useI18n() + const settingsModel = ref(false) + const { isLargeCtrlsBar } = useCtrlsBar() + const { showTip, updateTip } = useTooltip() + const insertLogSearchHistory = debounce((log: string) => { + if (!log) { + return + } + + const idx = logSearchHistory.value.indexOf(log) + + if (idx !== -1) { + logSearchHistory.value.splice(idx, 1) + } + + logSearchHistory.value.unshift(log) + if (logSearchHistory.value.length > 5) { + logSearchHistory.value.pop() + } + }, 1500) + + watch(logFilter, insertLogSearchHistory) + + const logLevels = computed(() => { + if (isSingBox.value) { + return Object.values(LOG_LEVEL) + } + return [LOG_LEVEL.Debug, LOG_LEVEL.Info, LOG_LEVEL.Warning, LOG_LEVEL.Error, LOG_LEVEL.Silent] + }) + + const logFilterOptions = computed(() => { + const types: string[] = [] + const levels: string[] = [] + + if (isSingBox.value) { + for (const log of logs.value) { + const startIndex = log.payload.startsWith('[') ? log.payload.indexOf(']') + 2 : 0 + const endIndex = log.payload.indexOf(':', startIndex) + const type = log.payload.slice(startIndex, endIndex + 1) + + if (!types.includes(type)) { + types.push(type) + } + + if (!levels.includes(log.type)) { + levels.push(log.type) + } + } + } else { + for (const log of logs.value) { + const index = log.payload.indexOf(' ') + const type = index === -1 ? log.payload : log.payload.slice(0, index) + + if (!types.includes(type)) { + types.push(type) + } + + if (!levels.includes(log.type)) { + levels.push(log.type) + } + } + } + + return { + levels: levels.sort((a, b) => { + const aIdx = logLevels.value.indexOf(a as LOG_LEVEL) + const bIdx = logLevels.value.indexOf(b as LOG_LEVEL) + return aIdx - bIdx + }), + types: types.sort(), + } + }) + + const downloadAllLogs = () => { + const blob = new Blob( + [ + logs.value + .map((log) => + [ + log.seq.toString().padEnd(5, ' '), + log.time, + log.type.padEnd(7, ' '), + log.payload, + ].join('\t'), + ) + .join('\n'), + ], + { + type: 'text/plain', + }, + ) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = dayjs().format('YYYY-MM-DD HH-mm-ss') + '.log' + a.click() + URL.revokeObjectURL(url) + } + + return () => { + const levelSelect = ( + + ) + const searchInput = ( + (logSearchHistory.value = val)} + /> + ) + + const logTypeSelect = ( + + ) + + const settingsModal = ( + <> + + +
+
+ {t('logRetentionLimit')} + +
+
+ {t('hideLogRegex')} + +
+
+ {t('hideLog')} + +
+ showTip(e, t('hideLogTip'), { + appendTo: 'parent', + }) + } + > + +
+
+
+
+ + ) + + const buttons = ( +
+ {settingsModal} + + + + +
+ ) + + const content = !isLargeCtrlsBar.value ? ( +
+
+
{levelSelect}
+ {buttons} +
+
+ {logTypeSelect} + {searchInput} +
+
+ ) : ( +
+
+ {levelSelect} +
+ {logTypeSelect} + {searchInput} +
+
+ {buttons} +
+ ) + + return
{content}
+ } + }, +}) diff --git a/src/components/sidebar/OverviewCarousel.vue b/src/components/sidebar/OverviewCarousel.vue new file mode 100644 index 0000000..4111997 --- /dev/null +++ b/src/components/sidebar/OverviewCarousel.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/components/sidebar/OverviewCtrl.vue b/src/components/sidebar/OverviewCtrl.vue new file mode 100644 index 0000000..1b993a2 --- /dev/null +++ b/src/components/sidebar/OverviewCtrl.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/components/sidebar/ProxiesCtrl.tsx b/src/components/sidebar/ProxiesCtrl.tsx new file mode 100644 index 0000000..790af93 --- /dev/null +++ b/src/components/sidebar/ProxiesCtrl.tsx @@ -0,0 +1,364 @@ +import { disconnectByIdAPI, isSingBox, updateProxyProviderAPI } from '@/api' +import { renderGroups } from '@/composables/proxies' +import { useCtrlsBar } from '@/composables/useCtrlsBar' +import { PROXY_SORT_TYPE, PROXY_TAB_TYPE, ROUTE_NAME, SETTINGS_MENU_KEY } from '@/constant' +import { getMinCardWidth } from '@/helper/utils' +import { configs, updateConfigs } from '@/store/config' +import { activeConnections } from '@/store/connections' +import { + allProxiesLatencyTest, + fetchProxies, + hasSmartGroup, + proxiesFilter, + proxiesTabShow, + proxyGroupList, + proxyProviederList, +} from '@/store/proxies' +import { + automaticDisconnection, + collapseGroupMap, + displayFinalOutbound, + groupProxiesByProvider, + hideUnavailableProxies, + manageHiddenGroup, + minProxyCardWidth, + proxyCardSize, + proxySortType, + twoColumnProxyGroup, + useSmartGroupSort, +} from '@/store/settings' +import { + ArrowPathIcon, + BoltIcon, + ChevronDownIcon, + ChevronUpIcon, + WrenchScrewdriverIcon, +} from '@heroicons/vue/24/outline' +import { every } from 'lodash' +import { computed, defineComponent, ref } from 'vue' +import { useI18n } from 'vue-i18n' +import { useRouter } from 'vue-router' +import DialogWrapper from '../common/DialogWrapper.vue' +import TextInput from '../common/TextInput.vue' + +export default defineComponent({ + name: 'ProxiesCtrl', + setup() { + const { t } = useI18n() + const router = useRouter() + const isUpgrading = ref(false) + const isAllLatencyTesting = ref(false) + const settingsModel = ref(false) + const { isLargeCtrlsBar } = useCtrlsBar() + const handlerClickUpdateAllProviders = async () => { + if (isUpgrading.value) return + isUpgrading.value = true + try { + await Promise.all( + proxyProviederList.value.map((provider) => updateProxyProviderAPI(provider.name)), + ) + await fetchProxies() + isUpgrading.value = false + } catch { + await fetchProxies() + isUpgrading.value = false + } + } + + const hasProviders = computed(() => { + return proxyProviederList.value.length > 0 + }) + + const defaultModes = ['direct', 'rule', 'global'] + const modeList = computed(() => { + return configs.value?.['mode-list'] || configs.value?.['modes'] || defaultModes + }) + const needTranslateModes = computed(() => { + return every(modeList.value, (mode) => defaultModes.includes(mode.toLowerCase())) + }) + + const handlerModeChange = (e: Event) => { + const mode = (e.target as HTMLSelectElement).value + updateConfigs({ mode }) + if (isSingBox.value && automaticDisconnection.value) { + activeConnections.value.forEach((connection) => { + if (connection.rule.includes('clash_mode')) { + disconnectByIdAPI(connection.id) + } + }) + } + } + + const handlerClickLatencyTestAll = async () => { + if (isAllLatencyTesting.value) return + isAllLatencyTesting.value = true + try { + await allProxiesLatencyTest() + isAllLatencyTesting.value = false + } catch { + isAllLatencyTesting.value = false + } + } + + const hasNotCollapsed = computed(() => { + return renderGroups.value.some((name) => collapseGroupMap.value[name]) + }) + + const handlerClickToggleCollapse = () => { + collapseGroupMap.value = Object.fromEntries( + renderGroups.value.map((name) => [name, !hasNotCollapsed.value]), + ) + } + + const handlerResetProxyCardWidth = () => { + minProxyCardWidth.value = getMinCardWidth(proxyCardSize.value) + } + + const tabsWithNumbers = computed(() => { + return Object.values(PROXY_TAB_TYPE).map((type) => { + return { + type, + count: + type === PROXY_TAB_TYPE.PROXIES + ? proxyGroupList.value.length + : proxyProviederList.value.length, + } + }) + }) + return () => { + const tabs = ( +
+ {tabsWithNumbers.value.map(({ type, count }) => { + return ( + (proxiesTabShow.value = type)} + > + {t(type)} ({count}) + + ) + })} +
+ ) + const upgradeAllIcon = proxiesTabShow.value === PROXY_TAB_TYPE.PROVIDER && ( + + ) + const modeSelect = configs.value && ( + + ) + const sort = ( + + ) + + const latencyTestAll = ( + + ) + + const toggleCollapseAll = ( + + ) + + const searchInput = ( + + ) + + const settingsModal = ( + <> + + +
+
+ {t('sortBy')} + {sort} +
+ {hasSmartGroup.value && ( +
+ {t('useSmartGroupSort')} + +
+ )} +
+ {t('groupProxiesByProvider')} + +
+
+ {t('unavailableProxy')} + +
+
+ {t('manageHiddenGroup')} + +
+
+ {t('automaticDisconnection')} + +
+
+ {t('displayFinalOutbound')} + +
+
+ {t('minProxyCardWidth')} +
+ + +
+
+
+ +
+
+ + ) + + const content = !isLargeCtrlsBar.value ? ( +
+ {hasProviders.value && ( +
+ {tabs} + {upgradeAllIcon} +
+ )} +
+ {modeSelect} + {searchInput} + {settingsModal} + {toggleCollapseAll} + {latencyTestAll} +
+
+ ) : ( +
+ {hasProviders.value && tabs} + {modeSelect} +
{searchInput}
+ {upgradeAllIcon} + {settingsModal} + {toggleCollapseAll} + {latencyTestAll} +
+ ) + + return
{content}
+ } + }, +}) diff --git a/src/components/sidebar/RulesCtrl.tsx b/src/components/sidebar/RulesCtrl.tsx new file mode 100644 index 0000000..4478df7 --- /dev/null +++ b/src/components/sidebar/RulesCtrl.tsx @@ -0,0 +1,176 @@ +import { updateRuleProviderAPI } from '@/api' +import { useCtrlsBar } from '@/composables/useCtrlsBar' +import { RULE_TAB_TYPE } from '@/constant' +import { showNotification } from '@/helper/notification' +import { fetchRules, ruleProviderList, rules, rulesFilter, rulesTabShow } from '@/store/rules' +import { + disconnectOnRuleDisable, + displayLatencyInRule, + displayNowNodeInRule, +} from '@/store/settings' +import { ArrowPathIcon, WrenchScrewdriverIcon } from '@heroicons/vue/24/outline' +import { computed, defineComponent, ref } from 'vue' +import { useI18n } from 'vue-i18n' +import DialogWrapper from '../common/DialogWrapper.vue' +import TextInput from '../common/TextInput.vue' + +export default defineComponent({ + name: 'RulesCtrl', + setup() { + const { t } = useI18n() + const settingsModel = ref(false) + const isUpgrading = ref(false) + const { isLargeCtrlsBar } = useCtrlsBar() + const hasProviders = computed(() => { + return ruleProviderList.value.length > 0 + }) + + const handlerClickUpgradeAllProviders = async () => { + if (isUpgrading.value) return + isUpgrading.value = true + try { + let updateCount = 0 + + await Promise.all( + ruleProviderList.value.map((provider) => + updateRuleProviderAPI(provider.name).then(() => { + updateCount++ + + const isFinished = updateCount === ruleProviderList.value.length + + showNotification({ + key: 'updateFinishedTip', + content: 'updateFinishedTip', + params: { + number: `${updateCount}/${ruleProviderList.value.length}`, + }, + type: isFinished ? 'alert-success' : 'alert-info', + timeout: isFinished ? 2000 : 0, + }) + }), + ), + ) + await fetchRules() + isUpgrading.value = false + } catch { + await fetchRules() + isUpgrading.value = false + } + } + + const tabsWithNumbers = computed(() => { + return Object.values(RULE_TAB_TYPE).map((type) => { + return { + type, + count: type === RULE_TAB_TYPE.RULES ? rules.value.length : ruleProviderList.value.length, + } + }) + }) + + return () => { + const tabs = ( +
+ {tabsWithNumbers.value.map(({ type, count }) => { + return ( + (rulesTabShow.value = type)} + > + {t(type)} ({count}) + + ) + })} +
+ ) + const upgradeAllIcon = rulesTabShow.value === RULE_TAB_TYPE.PROVIDER && ( + + ) + + const searchInput = ( + + ) + + const settingsModal = ( + <> + + +
+
+ {t('displaySelectedNode')} + +
+
+ {t('displayLatencyNumber')} + +
+
+ {t('disconnectOnRuleDisable')} + +
+
+
+ + ) + + const content = !isLargeCtrlsBar.value ? ( +
+ {hasProviders.value && ( +
+ {tabs} + {upgradeAllIcon} +
+ )} +
+ {searchInput} + {settingsModal} +
+
+ ) : ( +
+ {hasProviders.value && tabs} + {searchInput} +
+ {upgradeAllIcon} + {settingsModal} +
+ ) + + return
{content}
+ } + }, +}) diff --git a/src/components/sidebar/SideBar.vue b/src/components/sidebar/SideBar.vue new file mode 100644 index 0000000..c2f45cd --- /dev/null +++ b/src/components/sidebar/SideBar.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/components/sidebar/SidebarButtons.vue b/src/components/sidebar/SidebarButtons.vue new file mode 100644 index 0000000..1ff52fa --- /dev/null +++ b/src/components/sidebar/SidebarButtons.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/components/sidebar/SourceIPFilter.vue b/src/components/sidebar/SourceIPFilter.vue new file mode 100644 index 0000000..5a5a223 --- /dev/null +++ b/src/components/sidebar/SourceIPFilter.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/components/sidebar/VerticalInfos.vue b/src/components/sidebar/VerticalInfos.vue new file mode 100644 index 0000000..aaeee0d --- /dev/null +++ b/src/components/sidebar/VerticalInfos.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/composables/bouncein.ts b/src/composables/bouncein.ts new file mode 100644 index 0000000..18287aa --- /dev/null +++ b/src/composables/bouncein.ts @@ -0,0 +1,35 @@ +import { isMiddleScreen } from '@/helper/utils' +import { scrollAnimationEffect } from '@/store/settings' +import { useCurrentElement, useElementVisibility } from '@vueuse/core' +import { onMounted, watch, type Ref } from 'vue' + +const className = 'bounce-in' +const initClassName = ['scale-85', 'opacity-0'] + +export function useBounceOnVisible(el: Ref = useCurrentElement()) { + if (!isMiddleScreen.value || !scrollAnimationEffect.value) return + + const visible = useElementVisibility(el) + + onMounted(() => { + if (!el.value) return + + el.value.classList.add(...initClassName) + + watch( + visible, + (value) => { + if (!el.value) return + + if (value) { + el.value.classList.add(className) + el.value.classList.remove(...initClassName) + } else { + el.value.classList.remove(className) + el.value.classList.add(...initClassName) + } + }, + { immediate: true }, + ) + }) +} diff --git a/src/composables/connections.ts b/src/composables/connections.ts new file mode 100644 index 0000000..95865dc --- /dev/null +++ b/src/composables/connections.ts @@ -0,0 +1,20 @@ +import type { Connection } from '@/types' +import { nextTick, ref } from 'vue' + +const infoConn = ref(null) +const connectionDetailModalShow = ref(false) + +export const useConnections = () => { + const handlerInfo = async (conn: Connection) => { + infoConn.value = null + await nextTick() + infoConn.value = conn + connectionDetailModalShow.value = true + } + + return { + infoConn, + connectionDetailModalShow, + handlerInfo, + } +} diff --git a/src/composables/keyboard.ts b/src/composables/keyboard.ts new file mode 100644 index 0000000..a8103ea --- /dev/null +++ b/src/composables/keyboard.ts @@ -0,0 +1,44 @@ +import { renderRoutes } from '@/helper' +import { activeBackend } from '@/store/setup' +import { computed, onMounted, onUnmounted } from 'vue' +import { useRouter } from 'vue-router' + +export const useKeyboard = () => { + const router = useRouter() + + const routeShortcuts = computed(() => { + return renderRoutes.value.map((route, index) => ({ + key: (index + 1).toString(), + route, + })) + }) + + const handleKeydown = (event: KeyboardEvent) => { + if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) { + return + } + + if (!activeBackend.value || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) { + return + } + + const key = event.key + const route = routeShortcuts.value.find((s) => s.key === key) + if (route) { + event.preventDefault() + router.push({ name: route.route }) + } + } + + onMounted(() => { + document.addEventListener('keydown', handleKeydown) + }) + + onUnmounted(() => { + document.removeEventListener('keydown', handleKeydown) + }) + + return { + routeShortcuts, + } +} diff --git a/src/composables/overview.ts b/src/composables/overview.ts new file mode 100644 index 0000000..3dda367 --- /dev/null +++ b/src/composables/overview.ts @@ -0,0 +1,20 @@ +import { ref } from 'vue' + +type IPInfo = { + ip: string[] + ipWithPrivacy: string[] +} + +export const ipForChina = ref({ + ip: [], + ipWithPrivacy: [], +}) +export const ipForGlobal = ref({ + ip: [], + ipWithPrivacy: [], +}) + +export const baiduLatency = ref('') +export const githubLatency = ref('') +export const youtubeLatency = ref('') +export const cloudflareLatency = ref('') diff --git a/src/composables/paddingViews.ts b/src/composables/paddingViews.ts new file mode 100644 index 0000000..84c59eb --- /dev/null +++ b/src/composables/paddingViews.ts @@ -0,0 +1,41 @@ +import { isMiddleScreen } from '@/helper/utils' +import { computed, ref } from 'vue' + +export const ctrlsBottom = ref(0) +export const dockTop = ref(0) +export const usePaddingForViews = ( + config = { + offsetTop: 8, + offsetBottom: 8, + }, +) => { + const { offsetTop, offsetBottom } = config + const paddingTop = computed(() => { + if (isMiddleScreen.value) { + return ctrlsBottom.value + offsetTop + } + return 0 + }) + const paddingBottom = computed(() => { + if (isMiddleScreen.value) { + return dockTop.value + offsetBottom + } + return 0 + }) + + const padding = computed(() => { + if (isMiddleScreen.value) { + return { + paddingTop: `${paddingTop.value}px`, + paddingBottom: `${paddingBottom.value}px`, + } + } + return {} + }) + + return { + padding, + paddingTop, + paddingBottom, + } +} diff --git a/src/composables/proxies.ts b/src/composables/proxies.ts new file mode 100644 index 0000000..a085330 --- /dev/null +++ b/src/composables/proxies.ts @@ -0,0 +1,50 @@ +import { isSingBox } from '@/api' +import { GLOBAL, PROXY_TAB_TYPE } from '@/constant' +import { isHiddenGroup } from '@/helper' +import { configs } from '@/store/config' +import { proxiesTabShow, proxyGroupList, proxyMap, proxyProviederList } from '@/store/proxies' +import { customGlobalNode, displayGlobalByMode, manageHiddenGroup } from '@/store/settings' +import { isEmpty } from 'lodash' +import { computed, ref } from 'vue' + +const filterGroups = (all: string[]) => { + if (manageHiddenGroup.value) { + return all + } + + return all.filter((name) => !isHiddenGroup(name)) +} + +const getRenderGroups = () => { + if (isEmpty(proxyMap.value)) { + return [] + } + + if (proxiesTabShow.value === PROXY_TAB_TYPE.PROVIDER) { + return proxyProviederList.value.map((group) => group.name) + } + + if (displayGlobalByMode.value) { + if (configs.value?.mode.toUpperCase() === GLOBAL) { + return [ + isSingBox.value && proxyMap.value[customGlobalNode.value] ? customGlobalNode.value : GLOBAL, + ] + } + + return filterGroups(proxyGroupList.value) + } + + return filterGroups([...proxyGroupList.value, GLOBAL]) +} + +export const disableProxiesPageScroll = ref(false) +export const isProxiesPageMounted = ref(false) +export const renderGroups = computed(() => { + const groups = getRenderGroups() + + if (isProxiesPageMounted.value) { + return groups + } + + return groups.slice(0, 16) +}) diff --git a/src/composables/proxiesScroll.ts b/src/composables/proxiesScroll.ts new file mode 100644 index 0000000..1f23af4 --- /dev/null +++ b/src/composables/proxiesScroll.ts @@ -0,0 +1,48 @@ +import { PROXY_CARD_SIZE } from '@/constant' +import { findScrollableParent } from '@/helper/utils' +import { minProxyCardWidth, proxyCardSize } from '@/store/settings' +import { useCurrentElement, useElementSize, useInfiniteScroll } from '@vueuse/core' +import { computed, nextTick, onMounted, ref, watch } from 'vue' + +export const useCalculateMaxProxies = (totalProxies: number, activeIndex: number) => { + const el = useCurrentElement() + const { width } = useElementSize(el) + const initMaxProxies = computed(() => { + return ( + Math.max(Math.floor(width.value / minProxyCardWidth.value), 2) * + (proxyCardSize.value === PROXY_CARD_SIZE.LARGE ? 9 : 12) + ) + }) + const maxProxies = ref(Math.max(24, activeIndex + 12)) + + onMounted(() => { + watch( + initMaxProxies, + () => { + maxProxies.value = Math.max(maxProxies.value, initMaxProxies.value) + }, + { immediate: true }, + ) + + nextTick(() => { + const scrollEl = findScrollableParent(el.value as HTMLElement) + + useInfiniteScroll( + scrollEl, + () => { + maxProxies.value = Math.min((maxProxies.value += initMaxProxies.value), totalProxies) + }, + { + distance: 100, + canLoadMore: () => { + return maxProxies.value < totalProxies + }, + }, + ) + }) + }) + + return { + maxProxies, + } +} diff --git a/src/composables/renderProxies.ts b/src/composables/renderProxies.ts new file mode 100644 index 0000000..d788f7d --- /dev/null +++ b/src/composables/renderProxies.ts @@ -0,0 +1,93 @@ +import { NOT_CONNECTED, PROXY_SORT_TYPE } from '@/constant' +import { isProxyGroup } from '@/helper' +import { getLatencyByName, proxiesFilter } from '@/store/proxies' +import { hideUnavailableProxies, proxySortType, useSmartGroupSort } from '@/store/settings' +import { smartOrderMap } from '@/store/smart' +import { computed, type ComputedRef } from 'vue' + +export function useRenderProxies(proxies: ComputedRef, proxyGroup?: string) { + const renderProxies = computed(() => { + return getRenderProxies(proxies.value, proxyGroup) + }) + const availableProxies = computed(() => { + return renderProxies.value.filter( + (proxy) => getLatencyByName(proxy, proxyGroup) !== NOT_CONNECTED, + ).length + }) + + const proxiesCount = computed(() => { + const all = proxies.value.length + + return `${availableProxies.value}/${all}` + }) + + return { + renderProxies, + proxiesCount, + } +} + +const getRenderProxies = (proxies: string[], groupName?: string) => { + const latencyMap = new Map() + + proxies = [...proxies] + proxies.forEach((name) => { + latencyMap.set(name, getLatencyByName(name, groupName)) + }) + + if (hideUnavailableProxies.value) { + proxies = proxies.filter((name) => { + return isProxyGroup(name) || latencyMap.get(name)! > NOT_CONNECTED + }) + } + + if (proxiesFilter.value) { + const filters = proxiesFilter.value.split(' ').map((f) => f.toLowerCase().trim()) + + proxies = proxies.filter((name) => { + name = name.toLowerCase() + return filters.every((f) => name.includes(f)) + }) + } + + if (useSmartGroupSort.value && smartOrderMap.value[groupName!]) { + const orderMap = smartOrderMap.value[groupName!] + + return proxies.sort((a, b) => { + const ia = orderMap[a] ?? Number.MAX_SAFE_INTEGER + const ib = orderMap[b] ?? Number.MAX_SAFE_INTEGER + return ia - ib + }) + } + + if (proxySortType.value === PROXY_SORT_TYPE.DEFAULT) { + return proxies + } + + const proxyGroups: string[] = [] + const proxyNodes: string[] = [] + + proxies.forEach((proxy) => { + if (isProxyGroup(proxy)) { + proxyGroups.push(proxy) + } else { + proxyNodes.push(proxy) + } + }) + + const getLatencyForSort = (name: string) => { + const latency = latencyMap.get(name)! + return latency === 0 ? Infinity : latency + } + const sortFuncMap = { + [PROXY_SORT_TYPE.NAME_ASC]: (prev: string, next: string) => prev.localeCompare(next), + [PROXY_SORT_TYPE.NAME_DESC]: (prev: string, next: string) => next.localeCompare(prev), + [PROXY_SORT_TYPE.LATENCY_ASC]: (prev: string, next: string) => + getLatencyForSort(prev) - getLatencyForSort(next), + [PROXY_SORT_TYPE.LATENCY_DESC]: (prev: string, next: string) => + getLatencyForSort(next) - getLatencyForSort(prev), + } + const sortFunc = sortFuncMap[proxySortType.value] + + return proxyGroups.concat(proxyNodes.sort(sortFunc)) +} diff --git a/src/composables/settings.ts b/src/composables/settings.ts new file mode 100644 index 0000000..6b9b9d9 --- /dev/null +++ b/src/composables/settings.ts @@ -0,0 +1,19 @@ +import { fetchIsUIUpdateAvailable, upgradeUIAPI } from '@/api' +import { autoUpgrade } from '@/store/settings' +import { ref } from 'vue' + +const isUIUpdateAvailable = ref(false) + +export const useSettings = () => { + const checkUIUpdate = async () => { + isUIUpdateAvailable.value = await fetchIsUIUpdateAvailable() + if (isUIUpdateAvailable.value && autoUpgrade.value) { + upgradeUIAPI() + } + } + + return { + isUIUpdateAvailable, + checkUIUpdate, + } +} diff --git a/src/composables/statistics.ts b/src/composables/statistics.ts new file mode 100644 index 0000000..e66ebbb --- /dev/null +++ b/src/composables/statistics.ts @@ -0,0 +1,24 @@ +import { prettyBytesHelper } from '@/helper/utils' +import { activeConnections, downloadTotal, uploadTotal } from '@/store/connections' +import { downloadSpeed, memory, uploadSpeed } from '@/store/overview' +import { computed } from 'vue' + +export enum STATISTICS_TYPE { + CONNECTIONS = 'connections', + DOWNLOAD = 'download', + DL_SPEED = 'dlSpeed', + MEMORY_USAGE = 'memoryUsage', + UPLOAD = 'upload', + UL_SPEED = 'ulSpeed', +} + +export const statisticsMap = computed(() => { + return { + [STATISTICS_TYPE.CONNECTIONS]: activeConnections.value.length, + [STATISTICS_TYPE.MEMORY_USAGE]: prettyBytesHelper(memory.value, { binary: true }), + [STATISTICS_TYPE.DOWNLOAD]: prettyBytesHelper(downloadTotal.value), + [STATISTICS_TYPE.UPLOAD]: prettyBytesHelper(uploadTotal.value), + [STATISTICS_TYPE.DL_SPEED]: prettyBytesHelper(downloadSpeed.value) + '/s', + [STATISTICS_TYPE.UL_SPEED]: prettyBytesHelper(uploadSpeed.value) + '/s', + } +}) diff --git a/src/composables/swipe.ts b/src/composables/swipe.ts new file mode 100644 index 0000000..2d0297f --- /dev/null +++ b/src/composables/swipe.ts @@ -0,0 +1,112 @@ +import { CONNECTION_TAB_TYPE, PROXY_TAB_TYPE, ROUTE_NAME, RULE_TAB_TYPE } from '@/constant' +import { renderRoutes } from '@/helper' +import { connectionTabShow } from '@/store/connections' +import { proxiesTabShow, proxyProviederList } from '@/store/proxies' +import { ruleProviderList, rulesTabShow } from '@/store/rules' +import { swipeInPages, swipeInTabs } from '@/store/settings' +import { useSwipe } from '@vueuse/core' +import { flatten } from 'lodash' +import { computed, ref, watch } from 'vue' +import { useRoute, useRouter } from 'vue-router' + +export const disableSwipe = ref(false) + +export const useSwipeRouter = () => { + const swiperRef = ref() + const route = useRoute() + const router = useRouter() + const { direction } = useSwipe(swiperRef, { threshold: 75 }) + + const swipeList = computed(() => { + return flatten( + renderRoutes.value.map((r) => { + if (swipeInTabs.value) { + if (r === ROUTE_NAME.proxies && proxyProviederList.value.length > 0) { + return Object.values(PROXY_TAB_TYPE).map((tab) => { + return [ + () => route.name === ROUTE_NAME.proxies && proxiesTabShow.value === tab, + () => { + router.push({ name: ROUTE_NAME.proxies }) + proxiesTabShow.value = tab + }, + ] + }) + } else if (r === ROUTE_NAME.connections) { + return Object.values(CONNECTION_TAB_TYPE).map((tab) => { + return [ + () => route.name === ROUTE_NAME.connections && connectionTabShow.value === tab, + () => { + router.push({ name: ROUTE_NAME.connections }) + connectionTabShow.value = tab + }, + ] + }) + } else if (r === ROUTE_NAME.rules && ruleProviderList.value.length > 0) { + return Object.values(RULE_TAB_TYPE).map((tab) => { + return [ + () => route.name === ROUTE_NAME.rules && rulesTabShow.value === tab, + () => { + router.push({ name: ROUTE_NAME.rules }) + rulesTabShow.value = tab + }, + ] + }) + } + } + + return [[() => route.name === r, () => router.push({ name: r })]] + }), + ) + }) + + const getNextIndexInSwipeList = () => { + return swipeList.value.findIndex((s) => s[0]()) + } + + const getNextRouteName = () => { + const routeName = route.name as ROUTE_NAME + + if (routeName === ROUTE_NAME.setup) { + return router.push({ name: ROUTE_NAME.proxies }) + } + + return swipeList.value[(getNextIndexInSwipeList() + 1) % swipeList.value.length]?.[1]?.() + } + const getPrevRouteName = () => { + const routeName = route.name as ROUTE_NAME + + if (routeName === ROUTE_NAME.setup) { + return router.push({ name: ROUTE_NAME.proxies }) + } + + return swipeList.value[ + (getNextIndexInSwipeList() - 1 + swipeList.value.length) % swipeList.value.length + ]?.[1]?.() + } + + const isInputActive = () => { + const activeEl = document.activeElement + return activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA') + } + + watch(direction, () => { + if (!swipeInPages.value) return + + if ( + document.querySelector('dialog:modal') || + isInputActive() || + window.getSelection()?.toString()?.length || + disableSwipe.value + ) + return + if (direction.value === 'right') { + getPrevRouteName() + } else if (direction.value === 'left') { + getNextRouteName() + } + }) + + return { + swiperRef, + } +} diff --git a/src/composables/useCtrlsBar.ts b/src/composables/useCtrlsBar.ts new file mode 100644 index 0000000..43d00c6 --- /dev/null +++ b/src/composables/useCtrlsBar.ts @@ -0,0 +1,27 @@ +import { useCurrentElement, useElementBounding } from '@vueuse/core' +import { computed, onUnmounted, watch } from 'vue' +import { ctrlsBottom } from './paddingViews' + +export function useCtrlsBar(width: number = 720) { + const element = useCurrentElement() + const { width: ctrlsBarWidth, bottom: ctrlsBarBottom } = useElementBounding(element) + const isLargeCtrlsBar = computed(() => { + return ctrlsBarWidth.value > width + }) + + watch( + ctrlsBarBottom, + () => { + ctrlsBottom.value = ctrlsBarBottom.value + }, + { immediate: true }, + ) + + onUnmounted(() => { + ctrlsBottom.value = 0 + }) + + return { + isLargeCtrlsBar, + } +} diff --git a/src/constant/index.ts b/src/constant/index.ts new file mode 100644 index 0000000..3ab8eb3 --- /dev/null +++ b/src/constant/index.ts @@ -0,0 +1,298 @@ +import { + ArrowsRightLeftIcon, + Cog6ToothIcon, + CubeTransparentIcon, + DocumentTextIcon, + GlobeAltIcon, + SwatchIcon, +} from '@heroicons/vue/24/outline' + +export const IS_APPLE_DEVICE = /Mac|iPod|iPhone|iPad/.test(navigator.platform) + +export const GLOBAL = 'GLOBAL' +export const TEST_URL = 'https://www.gstatic.com/generate_204' +export const IPV6_TEST_URL = 'https://ipv6.google.com/generate_204' +export const NOT_CONNECTED = 0 +export enum LANG { + EN_US = 'en-US', + ZH_CN = 'zh-CN', + ZH_TW = 'zh-TW', + RU_RU = 'ru-RU', +} + +export enum FONTS { + MI_SANS = 'MiSans', + SARASA_UI = 'SarasaUi', + PING_FANG = 'PingFang', + FIRA_SANS = 'FiraSans', + SYSTEM_UI = 'SystemUI', +} + +export enum EMOJIS { + TWEMOJI = 'twemoji', + NOTO_COLOR_EMOJI = 'noto-color-emoji', +} + +export enum CONNECTIONS_TABLE_ACCESSOR_KEY { + Close = 'close', + Type = 'type', + Process = 'process', + Host = 'host', + Rule = 'rule', + Chains = 'chains', + Outbound = 'outbound', + DlSpeed = 'dlSpeed', + UlSpeed = 'ulSpeed', + Download = 'dl', + Upload = 'ul', + ConnectTime = 'connectTime', + SourceIP = 'sourceIP', + SourcePort = 'sourcePort', + SniffHost = 'sniffHost', + Destination = 'destination', + DestinationType = 'destinationType', + RemoteAddress = 'remoteAddress', + InboundUser = 'inboundUser', +} + +export enum TABLE_WIDTH_MODE { + AUTO = 'auto', + MANUAL = 'manual', +} + +export enum PROXY_SORT_TYPE { + DEFAULT = 'defaultsort', + NAME_ASC = 'nameasc', + NAME_DESC = 'namedesc', + LATENCY_ASC = 'latencyasc', + LATENCY_DESC = 'latencydesc', +} + +export enum PROXY_PREVIEW_TYPE { + AUTO = 'auto', + DOTS = 'dots', + BAR = 'bar', +} + +export enum RULE_TAB_TYPE { + RULES = 'rules', + PROVIDER = 'ruleProvider', +} + +export enum PROXY_TAB_TYPE { + PROXIES = 'proxies', + PROVIDER = 'proxyProvider', +} + +export enum SORT_TYPE { + HOST = 'host', + CHAINS = 'chains', + RULE = 'rule', + TYPE = 'type', + CONNECT_TIME = 'connectTime', + DOWNLOAD = 'download', + DOWNLOAD_SPEED = 'downloadSpeed', + UPLOAD = 'upload', + UPLOAD_SPEED = 'uploadSpeed', + SOURCE_IP = 'sourceIP', + INBOUND_USER = 'inboundUser', +} + +export enum SORT_DIRECTION { + ASC = 'asc', + DESC = 'desc', +} + +export enum CONNECTION_TAB_TYPE { + ACTIVE = 'activeConnections', + CLOSED = 'closedConnections', +} + +export enum LOG_LEVEL { + Trace = 'trace', + Debug = 'debug', + Info = 'info', + Warning = 'warning', + Error = 'error', + Fatal = 'fatal', + Panic = 'panic', + Silent = 'silent', +} + +export enum ROUTE_NAME { + overview = 'overview', + proxies = 'proxies', + connections = 'connections', + logs = 'logs', + rules = 'rules', + settings = 'settings', + setup = 'setup', +} + +export const ROUTE_ICON_MAP = { + [ROUTE_NAME.overview]: CubeTransparentIcon, + [ROUTE_NAME.proxies]: GlobeAltIcon, + [ROUTE_NAME.connections]: ArrowsRightLeftIcon, + [ROUTE_NAME.rules]: SwatchIcon, + [ROUTE_NAME.logs]: DocumentTextIcon, + [ROUTE_NAME.settings]: Cog6ToothIcon, + [ROUTE_NAME.setup]: CubeTransparentIcon, +} + +export enum TABLE_SIZE { + SMALL = 'small', + LARGE = 'large', +} + +export enum PROXY_CARD_SIZE { + SMALL = 'small', + LARGE = 'large', +} + +export enum MIN_PROXY_CARD_WIDTH { + SMALL = 130, + LARGE = 145, +} + +export enum PROXY_CHAIN_DIRECTION { + NORMAL = 'normal', + REVERSE = 'reverse', +} + +export enum PROXY_TYPE { + Direct = 'direct', + Reject = 'reject', + RejectDrop = 'rejectdrop', + Compatible = 'compatible', + Pass = 'pass', + Dns = 'dns', + Selector = 'selector', + Fallback = 'fallback', + URLTest = 'urltest', + Smart = 'smart', + LoadBalance = 'loadbalance', +} + +export const SIMPLE_CARD_STYLE = [ + [CONNECTIONS_TABLE_ACCESSOR_KEY.Host, CONNECTIONS_TABLE_ACCESSOR_KEY.ConnectTime], + [ + CONNECTIONS_TABLE_ACCESSOR_KEY.Chains, + CONNECTIONS_TABLE_ACCESSOR_KEY.DlSpeed, + CONNECTIONS_TABLE_ACCESSOR_KEY.Close, + ], +] + +export const DETAILED_CARD_STYLE = [ + [CONNECTIONS_TABLE_ACCESSOR_KEY.Host, CONNECTIONS_TABLE_ACCESSOR_KEY.ConnectTime], + [ + CONNECTIONS_TABLE_ACCESSOR_KEY.Type, + CONNECTIONS_TABLE_ACCESSOR_KEY.Download, + CONNECTIONS_TABLE_ACCESSOR_KEY.Upload, + ], + [ + CONNECTIONS_TABLE_ACCESSOR_KEY.Chains, + CONNECTIONS_TABLE_ACCESSOR_KEY.DlSpeed, + CONNECTIONS_TABLE_ACCESSOR_KEY.Close, + ], +] + +export const ALL_THEME = [ + 'light', + 'dark', + 'light-legacy', + 'dark-legacy', + 'cupcake', + 'bumblebee', + 'emerald', + 'corporate', + 'synthwave', + 'retro', + 'cyberpunk', + 'valentine', + 'halloween', + 'garden', + 'forest', + 'aqua', + 'lofi', + 'pastel', + 'fantasy', + 'wireframe', + 'black', + 'luxury', + 'dracula', + 'cmyk', + 'autumn', + 'business', + 'acid', + 'lemonade', + 'night', + 'coffee', + 'winter', + 'dim', + 'nord', + 'sunset', + 'caramellatte', + 'abyss', + 'silk', +] + +export const DEFAULT_THEME = { + name: 'custom', + id: '', + '--border': '1px', + '--color-base-100': '#ffffff', + '--color-base-200': '#fcfcfc', + '--color-base-300': '#f2f2f2', + '--color-base-content': '#2d2d33', + '--color-primary': '#5a3cd2', + '--color-primary-content': '#f3efff', + '--color-secondary': '#ea4c5a', + '--color-secondary-content': '#fff1f2', + '--color-accent': '#49c6c1', + '--color-accent-content': '#285e66', + '--color-neutral': '#1e1e1f', + '--color-neutral-content': '#ececec', + '--color-info': '#5b90ff', + '--color-info-content': '#273c66', + '--color-success': '#44c07a', + '--color-success-content': '#1d472f', + '--color-warning': '#e5a300', + '--color-warning-content': '#705322', + '--color-error': '#d13a30', + '--color-error-content': '#551d1d', + '--depth': '0', + '--noise': '0', + '--radius-box': '1rem', + '--radius-field': '0.5rem', + '--radius-selector': '1rem', + '--size-field': '0.25rem', + '--size-selector': '0.25rem', + 'color-scheme': 'dark', + default: false, + prefersdark: false, +} + +export type THEME = Record + +export enum IP_INFO_API { + IPSB = 'ip.sb', + IPWHOIS = 'ipwho.is', + IPAPI = 'ipapi.is', +} + +export enum SETTINGS_MENU_KEY { + general = 'generalSettings', + backend = 'backendSettings', + proxies = 'proxySettings', + connections = 'connectionSettings', + overview = 'overviewSettings', +} + +export enum OVERVIEW_CARD { + ChartsCard = 'ChartsCard', + NetworkCard = 'NetworkCard', + ProviderTrafficOverview = 'ProviderTrafficOverview', + TopologyCharts = 'TopologyCharts', + ConnectionHistory = 'ConnectionHistory', + RuleHitCountCard = 'RuleHitCountCard', +} diff --git a/src/helper/autoImportSettings.ts b/src/helper/autoImportSettings.ts new file mode 100644 index 0000000..d6cd0e4 --- /dev/null +++ b/src/helper/autoImportSettings.ts @@ -0,0 +1,66 @@ +import { showNotification } from '@/helper/notification' +import { useStorage } from '@vueuse/core' +const IMPORT_SETTINGS_URL_KEY = 'config/import-settings-url' + +export const DEFAULT_SETTINGS_URL = './zashboard-settings.json' +export const importSettingsUrl = useStorage(IMPORT_SETTINGS_URL_KEY, DEFAULT_SETTINGS_URL) +export const autoImportSettings = useStorage('config/auto-import-settings', false) + +const autoImportSettingsHash = useStorage('cache/auto-import-settings-hash', '') +const calculateSettingsHash = async (settings: Record) => { + const sortedKeys = Object.keys(settings).sort() + const hashString = sortedKeys.map((key) => `${key}:${settings[key]}`).join('|') + + let hash = 0 + for (let i = 0; i < hashString.length; i++) { + const char = hashString.charCodeAt(i) + hash = (hash << 5) - hash + char + hash = hash & hash + } + return Math.abs(hash).toString(16).padStart(8, '0') +} +export const importSettingsFromUrl = async (force = false) => { + const res = await fetch(importSettingsUrl.value) + const errorHandler = () => { + showNotification({ + content: 'importFailed', + params: { url: res.url }, + type: 'alert-error', + }) + } + if (!res.ok) { + errorHandler() + return + } + let settings: Record = {} + try { + settings = await res.json() + } catch { + errorHandler() + return + } + + if (!settings) { + errorHandler() + return + } + + const newHash = await calculateSettingsHash(settings) + + if (newHash === autoImportSettingsHash.value && !force) { + return + } + + showNotification({ + content: 'importing', + }) + autoImportSettingsHash.value = newHash + + for (const key in settings) { + if (key === IMPORT_SETTINGS_URL_KEY && !settings[key]) { + continue + } + localStorage.setItem(key, settings[key] as string) + } + location.reload() +} diff --git a/src/helper/dayjs.ts b/src/helper/dayjs.ts new file mode 100644 index 0000000..7b4c514 --- /dev/null +++ b/src/helper/dayjs.ts @@ -0,0 +1,36 @@ +import { language } from '@/store/settings' +import dayjs from 'dayjs' +import 'dayjs/locale/ru' +import 'dayjs/locale/zh-cn' +import 'dayjs/locale/zh-tw' +import relativeTime from 'dayjs/plugin/relativeTime' +import updateLocale from 'dayjs/plugin/updateLocale' +import { watch } from 'vue' + +dayjs.extend(relativeTime) +dayjs.extend(updateLocale) +dayjs.updateLocale('en', { + relativeTime: { + future: 'in %s', + past: '%s ago', + s: 'seconds', + m: 'a minute', + mm: '%d minutes', + h: 'an hour', + hh: '%d hours', + d: 'a day', + dd: '%d days', + M: 'a month', + MM: '%d months', + y: 'a year', + yy: '%d years', + }, +}) + +watch( + () => language.value, + () => { + dayjs.locale(language.value) + }, + { immediate: true }, +) diff --git a/src/helper/index.ts b/src/helper/index.ts new file mode 100644 index 0000000..e7cd0d6 --- /dev/null +++ b/src/helper/index.ts @@ -0,0 +1,183 @@ +import { NOT_CONNECTED, PROXY_CHAIN_DIRECTION, PROXY_TYPE, ROUTE_NAME } from '@/constant' +import { showNotification } from '@/helper/notification' +import { timeSaved } from '@/store/overview' +import { hiddenGroupMap, proxyMap } from '@/store/proxies' +import { + customThemes, + lowLatency, + mediumLatency, + proxyChainDirection, + splitOverviewPage, +} from '@/store/settings' +import type { Connection } from '@/types' +import dayjs from 'dayjs' +import * as ipaddr from 'ipaddr.js' +import { head } from 'lodash' +import { computed } from 'vue' +import { prettyBytesHelper } from './utils' + +export const isProxyGroup = (name: string) => { + const proxyNode = proxyMap.value[name] + + if (!proxyNode) { + return false + } + + if (proxyNode.all?.length) { + return true + } + + return [ + PROXY_TYPE.Dns, + PROXY_TYPE.Compatible, + PROXY_TYPE.Direct, + PROXY_TYPE.Reject, + PROXY_TYPE.RejectDrop, + PROXY_TYPE.Pass, + PROXY_TYPE.Fallback, + PROXY_TYPE.URLTest, + PROXY_TYPE.LoadBalance, + PROXY_TYPE.Selector, + PROXY_TYPE.Smart, + ].includes(proxyNode.type.toLowerCase() as PROXY_TYPE) +} + +export const getHostFromConnection = (connection: Connection) => { + const port = connection.metadata.destinationPort + const host = + connection.metadata.host || connection.metadata.sniffHost || connection.metadata.destinationIP + + if (host.includes(':')) { + return `[${host}]:${port}` + } + return `${host}:${port}` +} + +export const getProcessFromConnection = (connection: Connection) => { + return ( + connection.metadata.process || + connection.metadata.processPath.replace(/^.*[/\\](.*)$/, '$1') || + '-' + ) +} + +export const getDestinationFromConnection = (connection: Connection) => { + const finalProxyType = proxyMap.value[head(connection.chains) || '']?.type.toLowerCase() + + if (finalProxyType === PROXY_TYPE.Direct && connection.metadata.remoteDestination) { + return connection.metadata.remoteDestination + } + + return connection.metadata.destinationIP || connection.metadata.host +} + +export const getDestinationTypeFromConnection = (connection: Connection) => { + const destination = getDestinationFromConnection(connection) + + if (ipaddr.IPv4.isIPv4(destination)) { + return 'IPv4' + } else if (ipaddr.IPv6.isIPv6(destination)) { + return 'IPv6' + } else { + return 'FQDN' + } +} + +export const getChainsStringFromConnection = (connection: Connection) => { + const chains = [...connection.chains] + + if (proxyChainDirection.value === PROXY_CHAIN_DIRECTION.NORMAL) { + chains.reverse() + } + + return chains.join('') +} + +export const getNetworkTypeFromConnection = (connection: Connection) => { + return `${connection.metadata.type} | ${connection.metadata.network}` +} + +export const getInboundUserFromConnection = (connection: Connection) => { + return ( + connection.metadata.inboundUser || + connection.metadata.inboundName || + connection.metadata.inboundPort || + '-' + ) +} + +export const getToolTipForParams = ( + params: ToolTipParams, + config: { + suffix: string + binary: boolean + }, +) => { + const { suffix = '', binary = false } = config + + // fake data + if (params.data.name < timeSaved + 1) { + return `` + } + return ` +
+
+ ${params.seriesName} + (${dayjs(params.data.name).format('HH:mm:ss')}): ${prettyBytesHelper(params.data.value, { + binary: binary, + })}${suffix} +
` +} + +export const getColorForLatency = (latency: number) => { + if (latency === NOT_CONNECTED) { + return '' + } else if (latency < lowLatency.value) { + return 'text-green-500' + } else if (latency < mediumLatency.value) { + return 'text-yellow-500' + } else { + return 'text-red-500' + } +} + +export const renderRoutes = computed(() => { + return Object.values(ROUTE_NAME).filter((r) => { + return ![ROUTE_NAME.setup, !splitOverviewPage.value && ROUTE_NAME.overview].includes(r) + }) +}) + +export const applyCustomThemes = () => { + document.querySelectorAll('.custom-theme').forEach((style) => { + style.remove() + }) + customThemes.value.forEach((theme) => { + const style = document.createElement('style') + const styleString = Object.entries(theme) + .filter(([key]) => !['prefersdark', 'default', 'name', 'type', 'id'].includes(key)) + .map(([key, value]) => `${key}:${value}`) + .join(';') + + style.innerHTML = `[data-theme="${theme.name}"] { + ${styleString} + }` + + style.className = `custom-theme ${theme.name}` + document.head.appendChild(style) + }) +} + +export const isHiddenGroup = (group: string) => { + if (Reflect.has(hiddenGroupMap.value, group)) { + return hiddenGroupMap.value[group] + } + + return proxyMap.value[group]?.hidden +} + +export const handlerUpgradeSuccess = () => { + showNotification({ + content: 'upgradeSuccess', + type: 'alert-success', + }) +} diff --git a/src/helper/indexeddb.ts b/src/helper/indexeddb.ts new file mode 100644 index 0000000..ff484b1 --- /dev/null +++ b/src/helper/indexeddb.ts @@ -0,0 +1,180 @@ +import { customBackgroundURL } from '@/store/settings' +import dayjs from 'dayjs' +import { computed, ref, watch } from 'vue' + +const useIndexedDB = (dbKey: string) => { + const cacheMap = new Map() + const openDatabase = () => + new Promise((resolve, reject) => { + const request = indexedDB.open(dbKey, 1) + request.onupgradeneeded = () => { + const db = request.result + if (!db.objectStoreNames.contains(dbKey)) { + db.createObjectStore(dbKey, { keyPath: 'key' }) + } + } + request.onsuccess = () => { + const db = request.result + const store = db.transaction(dbKey, 'readonly').objectStore(dbKey) + const cursorRequest = store.openCursor() + + cursorRequest.onsuccess = (event) => { + const cursor = (event.target as IDBRequest).result + + if (cursor) { + cacheMap.set(cursor.key as string, cursor.value.value) + cursor.continue() + } else { + resolve(request.result) + } + } + cursorRequest.onerror = () => reject(cursorRequest.error) + } + request.onerror = () => reject(request.error) + }) + + const dbPromise = openDatabase() + + const executeTransaction = async ( + mode: IDBTransactionMode, + operation: (store: IDBObjectStore) => IDBRequest, + ) => { + const db = await dbPromise + return new Promise((resolve, reject) => { + const transaction = db.transaction(dbKey, mode) + const store = transaction.objectStore(dbKey) + const request = operation(store) + + request.onsuccess = () => resolve(request.result) + request.onerror = () => reject(request.error) + }) + } + + const put = async (key: string, value: string) => { + await dbPromise + cacheMap.set(key, value) + return executeTransaction('readwrite', (store) => + store.put({ + key, + value, + }), + ) + } + + const get = async (key: string) => { + await dbPromise + return cacheMap.get(key) + } + + const clear = async () => { + await dbPromise + cacheMap.clear() + return executeTransaction('readwrite', (store) => store.clear()) + } + + const isExists = async (key: string) => { + await dbPromise + return cacheMap.has(key) + } + + const del = async (key: string) => { + await dbPromise + cacheMap.delete(key) + return executeTransaction('readwrite', (store) => store.delete(key)) + } + + const getAllKeys = async () => { + await dbPromise + return Array.from(cacheMap.keys()) + } + + return { + put, + get, + del, + getAllKeys, + isExists, + clear, + } +} + +const backgroundDB = useIndexedDB('base64') +const backgroundImageKey = 'background-image' + +export const saveBase64ToIndexedDB = (val: string) => backgroundDB.put(backgroundImageKey, val) +export const getBase64FromIndexedDB = () => backgroundDB.get(backgroundImageKey) +export const deleteBase64FromIndexedDB = () => backgroundDB.clear() +export const LOCAL_IMAGE = 'local-image' + +const date = dayjs().format('YYYY-MM-DD') +const backgroundInDB = ref('') +const getBackgroundInDB = async () => { + backgroundInDB.value = (await getBase64FromIndexedDB()) || '' +} + +watch( + () => customBackgroundURL.value, + () => { + if (customBackgroundURL.value.includes(LOCAL_IMAGE)) { + getBackgroundInDB() + } + }, + { + immediate: true, + }, +) + +export const backgroundImage = computed(() => { + if (!customBackgroundURL.value) { + return '' + } + + if (customBackgroundURL.value.includes(LOCAL_IMAGE)) { + return `background-image: url('${backgroundInDB.value}');` + } + return `background-image: url('${customBackgroundURL.value}?v=${date}');` +}) + +export interface ConnectionHistoryData { + key: string + download: number + upload: number + count: number +} + +export enum ConnectionHistoryType { + SourceIP = 'sourceIP', + Destination = 'destination', + Process = 'process', + Outbound = 'outbound', +} + +const connectionHistoryDB = useIndexedDB('connection-history') + +export const saveConnectionHistoryToIndexedDB = async ( + uuid: string, + aggregationType: ConnectionHistoryType, + data: ConnectionHistoryData[], +) => { + const jsonData = JSON.stringify(data) + return connectionHistoryDB.put(`${uuid}-${aggregationType}`, jsonData) +} + +export const getConnectionHistoryFromIndexedDB = async ( + uuid: string, + aggregationType: ConnectionHistoryType, +): Promise => { + const jsonData = await connectionHistoryDB.get(`${uuid}-${aggregationType}`) + if (!jsonData) { + return [] + } + try { + return JSON.parse(jsonData) as ConnectionHistoryData[] + } catch { + return [] + } +} + +export const clearConnectionHistoryFromIndexedDB = async () => { + return connectionHistoryDB.clear() +} diff --git a/src/helper/notification.ts b/src/helper/notification.ts new file mode 100644 index 0000000..c46ad8c --- /dev/null +++ b/src/helper/notification.ts @@ -0,0 +1,162 @@ +import { i18n } from '@/i18n' +import { type Ref } from 'vue' + +const t = i18n.global.t +const alertMap = new Map< + string, + { + timer: number + alert: HTMLElement + progressBar: HTMLElement + startTime: number + remainingTime: number + isPaused: boolean + } +>() +let toastRef: Ref | null = null + +export const initNotification = (toast: Ref) => { + toastRef = toast +} + +const pauseTimer = (alertKey: string) => { + const alertData = alertMap.get(alertKey) + if (alertData && !alertData.isPaused) { + clearTimeout(alertData.timer) + alertData.isPaused = true + alertData.remainingTime = alertData.remainingTime - (Date.now() - alertData.startTime) + alertData.progressBar.style.animationPlayState = 'paused' + } +} + +const resumeTimer = (alertKey: string) => { + const alertData = alertMap.get(alertKey) + if (alertData && alertData.isPaused) { + alertData.isPaused = false + alertData.startTime = Date.now() + alertData.timer = setTimeout(() => { + alertMap.delete(alertKey) + alertData.alert.remove() + }, alertData.remainingTime) + alertData.progressBar.style.animationPlayState = 'running' + } +} + +const setTimer = ( + alert: HTMLElement, + timeout: number, + alertKey?: string, + progressBar?: HTMLElement | null, +) => { + let timer = -1 + + if (timeout !== 0) { + // 设置进度条动画 + if (progressBar) { + progressBar.style.animation = `progressBar ${timeout}ms linear forwards` + } + + timer = setTimeout(() => { + if (alertKey) { + alertMap.delete(alertKey) + } + alert.remove() + }, timeout) + } + + if (alertKey && progressBar) { + alertMap.set(alertKey, { + alert, + timer, + progressBar, + startTime: Date.now(), + remainingTime: timeout, + isPaused: false, + }) + } +} + +const closeAlert = (alert: HTMLElement, alertKey?: string) => { + if (alertKey) { + const alertData = alertMap.get(alertKey) + if (alertData) { + clearTimeout(alertData.timer) + alertMap.delete(alertKey) + } + } + alert.remove() +} + +const setAlert = ( + alert: HTMLElement, + content: string, + params: Record, + type: string, + alertKey: string, +): HTMLElement | null => { + alert.className = `alert flex p-2 pr-5 relative ${type}` + + const contentDiv = document.createElement('div') + contentDiv.className = 'break-all whitespace-pre-wrap' + contentDiv.innerHTML = t(content, params) + + const closeButton = document.createElement('button') + closeButton.className = 'absolute top-0 right-0 btn btn-xs btn-circle btn-ghost' + closeButton.innerHTML = ` + + + + ` + closeButton.addEventListener('click', () => closeAlert(alert, alertKey)) + + const progressContainer = document.createElement('div') + progressContainer.className = + 'absolute -bottom-2 left-1 right-1 h-1 bg-transparent rounded-lg overflow-hidden' + + const progressBar = document.createElement('div') + progressBar.className = 'h-full bg-primary/30 transition-all duration-100 ease-linear' + progressBar.style.width = '100%' + + progressContainer.appendChild(progressBar) + + alert.innerHTML = '' + alert.appendChild(contentDiv) + alert.appendChild(closeButton) + alert.appendChild(progressContainer) + + alert.addEventListener('mouseenter', () => pauseTimer(alertKey)) + alert.addEventListener('mouseleave', () => resumeTimer(alertKey)) + + return progressBar +} + +export const showNotification = ({ + content, + params = {}, + key, + type = 'alert-warning', + timeout = 3000, +}: { + content: string + params?: Record + key?: string + type?: 'alert-warning' | 'alert-success' | 'alert-error' | 'alert-info' | '' + timeout?: number +}) => { + const alertKey = key || content + + if (alertKey && alertMap.has(alertKey)) { + const { alert, timer } = alertMap.get(alertKey)! + clearTimeout(timer) + + const progressBar = setAlert(alert, content, params, type, alertKey) + setTimer(alert, timeout, alertKey, progressBar) + return + } + + const alert = document.createElement('div') + + const progressBar = setAlert(alert, content, params, type, alertKey) + toastRef?.value?.insertBefore(alert, toastRef?.value?.firstChild) + setTimer(alert, timeout, alertKey, progressBar) +} diff --git a/src/helper/sourceip.ts b/src/helper/sourceip.ts new file mode 100644 index 0000000..4ee059b --- /dev/null +++ b/src/helper/sourceip.ts @@ -0,0 +1,70 @@ +import { sourceIPLabelList } from '@/store/settings' +import { activeBackend } from '@/store/setup' +import { watch } from 'vue' + +const CACHE_SIZE = 256 +const ipLabelCache = new Map() +const sourceIPMap = new Map() +const sourceIPRegexList: { regex: RegExp; label: string }[] = [] + +const preprocessSourceIPList = () => { + ipLabelCache.clear() + sourceIPMap.clear() + sourceIPRegexList.length = 0 + + for (const { key, label, scope } of sourceIPLabelList.value) { + if (scope && !scope.includes(activeBackend.value?.uuid as string)) continue + if (key.startsWith('/')) { + sourceIPRegexList.push({ regex: new RegExp(key.slice(1), 'i'), label }) + } else { + sourceIPMap.set(key, label) + } + } +} + +const cacheResult = (ip: string, label: string) => { + ipLabelCache.set(ip, label) + + if (ipLabelCache.size > CACHE_SIZE) { + const firstKey = ipLabelCache.keys().next().value + + if (firstKey) { + ipLabelCache.delete(firstKey) + } + } + + return label +} + +watch(() => [sourceIPLabelList.value, activeBackend.value], preprocessSourceIPList, { + immediate: true, + deep: true, +}) + +export const getIPLabelFromMap = (ip: string) => { + if (!ip) return ip === '' ? 'Inner' : '' + + if (ipLabelCache.has(ip)) { + return ipLabelCache.get(ip)! + } + + const isIPv6 = ip.includes(':') + + if (isIPv6) { + for (const [key, label] of sourceIPMap.entries()) { + if (ip.endsWith(key)) { + return cacheResult(ip, label) + } + } + } else if (sourceIPMap.has(ip)) { + return cacheResult(ip, sourceIPMap.get(ip)!) + } + + for (const { regex, label } of sourceIPRegexList) { + if (regex.test(ip)) { + return cacheResult(ip, label) + } + } + + return cacheResult(ip, ip) +} diff --git a/src/helper/tooltip.ts b/src/helper/tooltip.ts new file mode 100644 index 0000000..8ec6988 --- /dev/null +++ b/src/helper/tooltip.ts @@ -0,0 +1,81 @@ +import tippy, { type Instance, type Props } from 'tippy.js' + +let appContent: HTMLElement +let tippyInstance: Instance | null = null +let currentTarget: HTMLElement | null = null + +export const useTooltip = () => { + if (!appContent) { + appContent = document.getElementById('app-content')! + } + + const showTip = (event: Event, content: string | HTMLElement, config: Partial = {}) => { + if (currentTarget === event.currentTarget) { + return + } + + tippyInstance?.destroy() + tippyInstance = tippy(event.currentTarget as HTMLElement, { + content, + placement: 'top', + animation: 'scale', + appendTo: appContent, + allowHTML: true, + showOnCreate: true, + onHidden: () => { + tippyInstance?.destroy() + tippyInstance = null + currentTarget = null + }, + popperOptions: { + modifiers: [ + { + name: 'preventOverflow', + options: { + boundary: 'clippingParents', + padding: 8, + }, + }, + { + name: 'flip', + options: { + fallbackPlacements: ['top', 'bottom', 'right', 'left'], + }, + }, + ], + }, + ...config, + }) + + currentTarget = event.currentTarget as HTMLElement + } + + const hideTip = () => { + tippyInstance?.hide() + } + + const updateTip = (content: string | HTMLElement) => { + tippyInstance?.setContent(content) + } + + return { + showTip, + hideTip, + updateTip, + } +} + +const { showTip } = useTooltip() + +export const checkTruncation = (e: Event) => { + const target = e.target as HTMLElement + const { scrollWidth, clientWidth } = target + + if (scrollWidth > clientWidth) { + showTip(e, target.innerText, { + delay: [700, 0], + trigger: 'mouseenter', + touch: ['hold', 500], + }) + } +} diff --git a/src/helper/utils.ts b/src/helper/utils.ts new file mode 100644 index 0000000..9c45e0e --- /dev/null +++ b/src/helper/utils.ts @@ -0,0 +1,113 @@ +import { MIN_PROXY_CARD_WIDTH, PROXY_CARD_SIZE } from '@/constant' +import type { Backend } from '@/types' +import { useMediaQuery } from '@vueuse/core' +import dayjs from 'dayjs' +import prettyBytes, { type Options } from 'pretty-bytes' + +export const isPreferredDark = useMediaQuery('(prefers-color-scheme: dark)') +export const isMiddleScreen = useMediaQuery('(max-width: 768px)') +export const isPWA = (() => { + return window.matchMedia('(display-mode: standalone)').matches || navigator.standalone +})() + +export const prettyBytesHelper = (bytes: number, opts?: Options) => { + return prettyBytes(bytes, { + binary: false, + ...opts, + }) +} + +export const fromNow = (timestamp: string) => { + return dayjs(timestamp).fromNow() +} + +export const exportSettings = () => { + const settings: Record = {} + + for (const key in localStorage) { + if (key.startsWith('config/') || key.startsWith('setup/')) { + settings[key] = localStorage.getItem(key) + } + } + + const blob = new Blob([JSON.stringify(settings, null, 2)], { type: 'application/json' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = 'zashboard-settings' + a.click() + URL.revokeObjectURL(url) +} + +export const getUrlFromBackend = (end: Omit) => { + return `${end.protocol}://${end.host}:${end.port}${end.secondaryPath || ''}` +} + +export const getLabelFromBackend = (end: Omit) => { + return end.label || getUrlFromBackend(end) +} + +export const getMinCardWidth = (size: PROXY_CARD_SIZE) => { + return size === PROXY_CARD_SIZE.LARGE ? MIN_PROXY_CARD_WIDTH.LARGE : MIN_PROXY_CARD_WIDTH.SMALL +} + +export const SCROLLABLE_PARENT_CLASS = 'scrollable-parent' + +export const scrollIntoCenter = (el: HTMLElement) => { + const scrollableParent = findScrollableParent(el) + + if (!scrollableParent) return + + const elRect = el.getBoundingClientRect() + const parentRect = scrollableParent.getBoundingClientRect() + + if (elRect.top >= parentRect.top && elRect.bottom <= parentRect.bottom) return + + const parentTop = scrollableParent.offsetTop + const childTop = el.offsetTop + + const centerOffset = + childTop - parentTop - scrollableParent.clientHeight / 2 + el.clientHeight / 2 + + scrollableParent.scrollTo({ + top: centerOffset, + behavior: 'smooth', + }) +} + +export const findScrollableParent = (el: HTMLElement | null): HTMLElement | null => { + const parent = el?.parentElement + + if ( + parent?.classList.contains(SCROLLABLE_PARENT_CLASS) && + parent.scrollHeight > parent.clientHeight + ) { + return parent + } + + return parent ? findScrollableParent(parent) : null +} + +export const getBackendFromUrl = () => { + const query = new URLSearchParams( + window.location.search || location.hash.match(/\?.*$/)?.[0]?.replace('?', ''), + ) + + if (query.has('hostname')) { + return { + protocol: query.get('http') + ? 'http' + : query.get('https') + ? 'https' + : window.location.protocol.replace(':', ''), + secondaryPath: query.get('secondaryPath') || '', + host: query.get('hostname') as string, + port: query.get('port') as string, + password: query.get('secret') || '', + label: query.get('label') || '', + disableUpgradeCore: + query.get('disableUpgradeCore') === '1' || query.get('disableUpgradeCore') === 'core', + } + } + return null +} diff --git a/src/i18n/en.ts b/src/i18n/en.ts new file mode 100644 index 0000000..1e62200 --- /dev/null +++ b/src/i18n/en.ts @@ -0,0 +1,309 @@ +const en = { + setup: 'Setup', + overview: 'Overview', + proxies: 'Proxies', + rules: 'Rules', + connections: 'Connections', + logs: 'Logs', + protocol: 'Protocol', + host: 'Host', + port: 'Port', + password: 'Password', + submit: 'Submit', + cancel: 'Cancel', + download: 'Download', + upload: 'Upload', + downloadSpeed: 'Download speed', + uploadSpeed: 'Upload speed', + speed: 'Speed', + memoryUsage: 'Memory usage', + version: 'Version', + noContent: 'No content', + flushFakeIP: 'Flush Fake IP', + flushDNSCache: 'Flush DNS cache', + flushDNSCacheSuccess: 'DNS cache flushed successfully', + flushFakeIPSuccess: 'Fake IP flushed successfully', + restartCoreSuccess: 'Core restarted successfully', + reloadConfigsSuccess: 'Configs reloaded successfully', + updateGeoSuccess: 'Geo database updated successfully', + chains: 'Chains', + outbound: 'Outbound', + sortBy: 'Sort by', + rule: 'Rule', + sourceIP: 'Source IP', + activeConnections: 'Active', + closedConnections: 'Closed', + logLevel: 'Log level', + logType: 'Log type', + twoColumnProxyGroup: 'Two-Column proxy group', + type: 'Type', + process: 'Process', + connectTime: 'Connect time', + sourcePort: 'Source port', + destination: 'Resolved address', + destinationType: 'Resolved type', + inboundUser: 'Inbound user', + dl: 'DL', + ul: 'UL', + dlSpeed: 'DL speed', + ulSpeed: 'UL speed', + settings: 'Settings', + speedtestUrl: 'Speedtest URL', + speedtestTimeout: 'Speedtest timeout', + connectionStyle: 'Connection style', + card: 'Card', + table: 'Table', + customTableColumns: 'Custom table columns', + customCardLines: 'Custom card lines', + close: 'Close', + defaultTheme: 'Default theme', + darkTheme: 'Dark theme', + proxyProvider: 'Proxy provider', + ruleProvider: 'Rule provider', + expire: 'Expire', + noExpire: 'No expiry', + updated: 'Updated', + upgradeUI: 'Upgrade dashboard', + reloadConfigs: 'Reload configs', + mode: 'Mode', + proxySortType: 'Proxy sort type', + defaultsort: 'Config order', + nameasc: 'Name ascending', + namedesc: 'Name descending', + latencydesc: 'Latency descending', + latencyasc: 'Latency ascending', + language: 'Language', + automaticDisconnection: 'Auto disconnect on node switch', + backend: 'Backend', + tunMode: 'Tun mode', + upgradeCore: 'Upgrade core', + upgradeToRelease: 'Upgrade to release', + upgradeToAlpha: 'Upgrade to alpha', + updateGeoDatabase: 'Update Geo', + truncateProxyName: 'Truncate proxy name', + sourceIPLabels: 'Source IP labels', + proxyPreviewType: 'Proxy preview type', + auto: 'Auto', + dots: 'Dots', + bar: 'Bar', + exportSettings: 'Export settings', + importSettings: 'Import settings', + connectionSettings: 'Connection settings', + proxySettings: 'Proxy settings', + logSettings: 'Log settings', + ruleSettings: 'Rule settings', + connectionDetails: 'Connection details', + customTheme: 'Custom theme', + unavailableProxy: 'Hide unavailable proxies', + protocolTips: + 'You are trying to connect to an HTTP backend, but zashboard is provided via HTTPS. This may cause connection errors. Please allow insecure content in your browser settings or use the HTTP version of zashboard, such as http://board.zash.run.place.', + global: 'Global', + direct: 'Direct', + lowLatencyDesc: 'Yellow threshold', + mediumLatencyDesc: 'Red threshold', + fonts: 'Fonts', + emoji: 'Emoji', + unauthorizedTip: 'Unauthorized, please log in again.', + restartCore: 'Restart core', + checkUpgrade: 'Check for updates', + autoUpgrade: 'Auto upgrade', + secondaryPath: 'Secondary path', + secondaryPathTip: 'If present, start with "/", otherwise leave empty.', + logRetentionLimit: 'Log retention limit', + DNSQuery: 'DNS query', + currentBackendUnavailable: + 'The current backend is unavailable. Would you like to switch to another backend?', + confirm: 'Confirm', + backendSwitchTo: 'Auto switch to {backend}', + ipv6Test: 'IPv6 test', + socksPort: 'SOCKS port', + httpPort: 'HTTP port', + mixedPort: 'Mixed port', + redirPort: 'Redir port', + tproxyPort: 'TProxy port', + tableSize: 'Table size', + proxyCardSize: 'Proxy card size', + small: 'Small', + normal: 'Normal', + large: 'Large', + autoIPCheckWhenStart: 'Auto IP check on start', + autoConnectionCheckWhenStart: 'Auto connection check on start', + chinaIP: 'China IP', + globalIP: 'Global IP', + networkInfo: 'Network info', + autoSwitchTheme: 'Auto switch theme', + customBackgroundURL: 'Background', + splitOverviewPage: 'Split overview page', + manageHiddenGroup: 'Manage hidden groups', + transparent: 'Transparency', + proxyGroupIconSize: 'Proxy group icon size', + proxyGroupIconMargin: 'Proxy group icon margin', + allowLan: 'Allow LAN', + proxyChainDirection: 'Proxy chain direction', + showFullProxyChain: 'Show full proxy chain', + reverse: 'Reverse', + sniffHost: 'Sniff host', + ipScreenshotTip: 'Please hide the IP when taking screenshots.', + showStatisticsWhenSidebarCollapsed: 'Show statistics when sidebar collapsed', + totalConnections: 'Connection statistics', + totalConnectionsTip: + 'Only connections opened while the panel is open are counted. \nRecording start time: {statsStartTime}', + mostDownloadHost: 'Most downloaded host', + mostUploadHost: 'Most uploaded host', + mostDownloadSourceIP: 'Most downloaded source IP', + mostUploadSourceIP: 'Most uploaded source IP', + mostDownloadProxy: 'Most downloaded proxy', + mostUploadProxy: 'Most uploaded proxy', + manual: 'Manual', + tableWidthMode: 'Table width mode', + testFailed: 'Test failed', + testFinishedTip: '{name}\n{number}/{total} test finished', + testFinishedResultTip: '{name}\nTest finished: {success} success, {failed} timeout', + testFailedTip: '{name}\nTest failed', + updateFinishedTip: '{number} update(s) finished', + independentLatencyTest: 'Independent latency test', + independentLatencyTestTip: + "When enabled, the latency test will use URLs specified in the configuration file instead of zashboard's URL settings. Latency will be displayed independently based on the URLs set in policy groups.", + search: 'Search', + searchMultiple: 'Space-separated keywords', + importing: 'Importing...', + hideConnection: 'Hide connection', + showConnection: 'Show connection', + hideConnectionRegex: 'Hide connection regex', + hideConnectionTip: + 'You can use case-insensitive regular expressions to match and hide unwanted connections.', + hideLog: 'Hide log', + showLog: 'Show log', + hideLogRegex: 'Hide log regex', + hideLogTip: 'You can use case-insensitive regular expressions to match and hide unwanted logs.', + loadBalance: 'Load Balance', + label: 'Label', + optional: 'Optional', + swipeInTabs: 'Swipe to switch between tabs', + swipeInPages: 'Swipe to switch between pages', + simpleCardPreset: 'Simple preset', + detailedCardPreset: 'Detailed preset', + refresh: 'Refresh', + reset: 'Reset', + minProxyCardWidth: 'Proxy card min width', + displayGlobalByMode: 'Display GLOBAL by mode', + displaySelectedNode: 'Show selected node', + displayLatencyNumber: 'Show latency numbers', + disconnectOnRuleDisable: 'Disconnect connections when disabling rule', + tipForFixed: + 'The current policy group is locked to the current node. Run a speed test to restore {type} behavior.', + remoteAddress: 'Remote address', + themeName: 'Theme name', + save: 'Save', + moreDetails: 'More details', + moreSettings: 'More settings', + customIcon: 'Custom icon', + disablePullToRefresh: 'Disable pull to refresh', + disablePullToRefreshTip: + 'Pull-to-refresh and virtual scrolling components may conflict. If you experience lag or accidentally trigger pull-to-refresh while scrolling, try disabling this feature.', + displayAllFeatures: 'Show all features', + displayAllFeaturesTip: + "Show all features, including those not supported by the official sing-box version. If you're using a forked version of sing-box that supports some of these features, you can try enabling them.", + blurIntensity: 'Blur intensity', + scrollAnimationEffect: 'Scroll animation effect', + importFromFile: 'Import from file', + importFromUrl: 'Import from URL', + sync: 'Sync', + upgradeSuccess: 'Upgrade successful', + numberOfChartsInSidebar: 'Number of charts in sidebar', + flushSmartWeights: 'Clear smart weights', + IPInfoAPI: 'IP info API', + IPInfoAPITip: + "This API will be used for IP checks in global node IP information queries, IP geolocation queries in connection details, and IP geolocation queries in zashboard's DNS query feature.", + general: 'General', + groupProxiesByProvider: 'Group proxies by provider', + useSmartGroupSort: 'Smart group sort by usage frequency', + RarelyUsed: 'Rarely used', + OccasionalUsed: 'Occasionally used', + MostUsed: 'Most used', + all: 'All', + autoDisconnectIdleUDP: 'Auto disconnect idle UDP', + autoDisconnectIdleUDPTime: 'UDP idle time', + autoDisconnectIdleUDPTip: + "When enabled, opening zashboard will close UDP connections that have been idle longer than the configured time. This may help with UDP connections that don't disconnect automatically as expected.", + customGlobalNode: 'Custom GLOBAL node', + connectionTopology: 'Connection topology', + editBackend: 'Edit backend', + editBackendTitle: 'Edit backend configuration', + selectBackend: 'Select backend', + backendConnectionFailed: 'Backend connection failed, please check configuration', + backendConfigSaved: 'Backend configuration saved successfully', + saveFailed: 'Save failed', + checking: 'Checking...', + copySuccess: 'Copied successfully', + importFromBackend: 'Import from backend', + importFromBackendTip: + 'The default ./zashboard-settings.json is located in the ui folder. Please ensure the ui folder exists and contains the configuration file.', + importFailed: 'Import failed, please check the URL: {url}', + autoImportFromUrl: 'Auto import', + autoImportFromUrlTip: + 'When enabled, settings will be automatically imported from the URL when opening zashboard. If the hash differs from the last imported hash, settings will be re-imported and the page will be refreshed.', + getting: 'Getting...', + mmdbSizeTip: 'If you are using mmdb format Geo files, the number will be 0', + displayFinalOutbound: 'Show final outbound node', + groupTestUrls: 'Group test URLs', + groupTestUrlsTip: + 'For manual tests triggered from the panel, set test URLs here > URLs in configuration > Global test URLs, but for groups with UrlTest/Fallback, etc., the URLs in the configuration still need to be modified, So this is only recommended for Selector scenes', + groupName: 'Group name', + noData: 'No data', + unknown: 'Unknown', + sourceIPAddress: 'Source IP address', + ruleMatch: 'Rule match', + proxyChainEntry: 'Proxy chain entry', + proxyChainExit: 'Proxy chain exit', + nodeType: 'Node type', + connectionCount: 'Connection count', + zashboardSettings: 'Dashboard Settings', + backendSettings: 'Backend Settings', + generalSettings: 'General Settings', + overviewCard: 'Overview Card', + overviewSettings: 'Overview Settings', + overviewCardSettings: 'Card Settings', + chartsCard: 'Charts Card', + networkCard: 'Network Card', + providerTrafficOverview: 'Provider Traffic Overview', + topologyCharts: 'Topology Charts', + connectionHistory: 'Connection History', + ruleHitCountCard: 'Rule Hit/Miss Statistics', + ruleHitChart: 'Hit Statistics', + ruleMissChart: 'Miss Statistics', + latency: 'Latency', + proxyStyle: 'Proxy style', + icon: 'Icon', + settingsVisibility: 'Settings Visibility', + ports: 'Ports', + actions: 'Actions', + showAllPreset: 'Show All', + minimalPreset: 'Minimal View', + aggregateBy: 'Aggregate by', + aggregateBySourceIP: 'By source IP', + aggregateByDestination: 'By destination', + aggregateByProcess: 'By process', + aggregateByOutbound: 'By outbound', + aggregateByNode: 'By node', + totalTraffic: 'Total traffic', + total: 'Total', + clearConnectionHistory: 'Clear connection history', + clearConnectionHistoryConfirm: + 'Are you sure you want to clear all connection history data? This action cannot be undone.', + clearConnectionHistorySuccess: 'Connection history data cleared successfully', + autoCleanupInterval: 'Auto cleanup interval', + autoCleanupIntervalWeek: 'Weekly', + autoCleanupIntervalMonth: 'Monthly', + autoCleanupIntervalQuarter: 'Quarterly', + autoCleanupIntervalNever: 'Never', + remainingTraffic: 'Remaining', + usedTraffic: 'Used', + ruleHitCount: 'Hit: {count} times', + ruleLastHit: 'Last hit: {time}', + ruleMissCount: 'Miss: {count} times', + ruleLastMiss: 'Last miss: {time}', +} + +export type LANG_MESSAGE = typeof en +export default en diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..1cc2be1 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,18 @@ +import { LANG } from '@/constant' +import { language } from '@/store/settings' +import { createI18n } from 'vue-i18n' +import en from './en' +import ru from './ru' +import zh from './zh' +import zhTW from './zh-tw' + +export const i18n = createI18n({ + legacy: false, + locale: language.value, + messages: { + [LANG.EN_US]: en, + [LANG.ZH_CN]: zh, + [LANG.ZH_TW]: zhTW, + [LANG.RU_RU]: ru, + }, +}) diff --git a/src/i18n/ru.ts b/src/i18n/ru.ts new file mode 100644 index 0000000..3c42ecc --- /dev/null +++ b/src/i18n/ru.ts @@ -0,0 +1,311 @@ +import type { LANG_MESSAGE } from './en' + +const ru: LANG_MESSAGE = { + setup: 'Настройка', + overview: 'Обзор', + proxies: 'Прокси', + rules: 'Правила', + connections: 'Подключения', + logs: 'Журнал', + protocol: 'Протокол', + host: 'Хост', + port: 'Порт', + password: 'Пароль', + submit: 'Отправить', + cancel: 'Отмена', + download: 'Загружено', + upload: 'Отправлено', + downloadSpeed: 'Скорость загрузки', + uploadSpeed: 'Скорость отдачи', + speed: 'Скорость', + memoryUsage: 'Память', + version: 'Версия', + noContent: 'Нет содержимого', + flushFakeIP: 'Очистить Fake IP', + flushDNSCache: 'Очистить DNS кэш', + flushDNSCacheSuccess: 'DNS кэш успешно очищен', + flushFakeIPSuccess: 'Fake IP успешно очищен', + restartCoreSuccess: 'Ядро успешно перезапущено', + reloadConfigsSuccess: 'Конфигурации успешно перезагружены', + updateGeoSuccess: 'Geo база данных успешно обновлена', + chains: 'Цепочки', + outbound: 'Исходящий', + sortBy: 'Сортировать по', + rule: 'Правило', + sourceIP: 'Исходный IP', + activeConnections: 'Активные', + closedConnections: 'Закрытые', + logLevel: 'Уровень журнала', + logType: 'Тип журнала', + twoColumnProxyGroup: 'Группа прокси в два столбца', + type: 'Тип', + process: 'Процесс', + connectTime: 'Время', + sourcePort: 'Исходный порт', + destination: 'Разрешенный адрес', + destinationType: 'Тип разрешения', + inboundUser: 'Входящий пользователь', + dl: 'Загр', + ul: 'Отдч', + dlSpeed: 'Загрузка', + ulSpeed: 'Отдача', + settings: 'Настройки', + speedtestUrl: 'URL теста скорости', + speedtestTimeout: 'Таймаут теста скорости', + connectionStyle: 'Стиль подключения', + card: 'Карточка', + table: 'Таблица', + customTableColumns: 'Пользовательские столбцы таблицы', + customCardLines: 'Пользовательские строчки карточки', + close: 'Закрыть', + defaultTheme: 'Тема по умолчанию', + darkTheme: 'Темная тема', + proxyProvider: 'Провайдер прокси', + ruleProvider: 'Провайдер правил', + expire: 'Истекает', + noExpire: 'Нет', + updated: 'Обновлено', + upgradeUI: 'Обновить панель', + reloadConfigs: 'Перезагрузить конфигурации', + mode: 'Режим', + proxySortType: 'Тип сортировки прокси', + defaultsort: 'По конфигурациям', + nameasc: 'Имя по возрастанию', + namedesc: 'Имя по убыванию', + latencydesc: 'Задержка по убыванию', + latencyasc: 'Задержка по возрастанию', + language: 'Язык', + automaticDisconnection: 'Автоматическое отключение', + backend: 'Бэкенд', + tunMode: 'Режим Tun', + upgradeCore: 'Обновить ядро', + upgradeToRelease: 'Обновить до Release', + upgradeToAlpha: 'Обновить до Alpha', + updateGeoDatabase: 'Обновить GEO', + truncateProxyName: 'Усечение имени прокси', + sourceIPLabels: 'Метки исходного IP', + proxyPreviewType: 'Тип предварительного просмотра прокси', + auto: 'Авто', + dots: 'Точки', + bar: 'Полоса', + exportSettings: 'Экспорт настроек', + importSettings: 'Импорт настроек', + connectionSettings: 'Настройки подключения', + proxySettings: 'Настройки прокси', + logSettings: 'Настройки журнала', + ruleSettings: 'Настройки правил', + connectionDetails: 'Детали подключения', + customTheme: 'Пользовательская тема', + unavailableProxy: 'Скрыть недоступное', + protocolTips: + 'Вы пытаетесь подключиться к HTTP-бэкенду, но Zashboard предоставляется через HTTPS. Это может вызвать ошибки подключения. Разрешите небезопасный контент в настройках браузера или используйте HTTP-версию панели, например, http://board.zash.run.place.', + global: 'Глобальный', + direct: 'Прямой', + lowLatencyDesc: 'Желтый порог', + mediumLatencyDesc: 'Красный порог', + fonts: 'Шрифты', + emoji: 'Emoji', + unauthorizedTip: 'Не авторизован, пожалуйста, войдите снова.', + restartCore: 'Перезапустить ядро', + checkUpgrade: 'Проверить обновления', + autoUpgrade: 'Автоматическое обновление', + secondaryPath: 'Дополнительный путь', + secondaryPathTip: 'Если присутствует, начните с "/", в противном случае оставьте пустым.', + logRetentionLimit: 'Лимит хранения журнала', + DNSQuery: 'DNS-запрос', + currentBackendUnavailable: + 'Текущий бэкенд недоступен. Попробуйте переключиться на другой бэкенд?', + confirm: 'Подтвердить', + backendSwitchTo: 'Автоматическое переключение на {backend}', + ipv6Test: 'IPv6-тест', + socksPort: 'Порт Socks', + httpPort: 'Порт HTTP', + mixedPort: 'Порт Mixed', + redirPort: 'Порт Redir', + tproxyPort: 'Порт TProxy', + tableSize: 'Размер таблицы', + proxyCardSize: 'Размер карточки прокси', + small: 'Маленький', + normal: 'Нормальный', + large: 'Большой', + autoIPCheckWhenStart: 'Автоматическая проверка IP при запуске', + autoConnectionCheckWhenStart: 'Автоматическая проверка соединений при запуске', + chinaIP: 'IP для Китая', + globalIP: 'Мировой IP', + networkInfo: 'Информация о сети', + autoSwitchTheme: 'Автоматический темы', + customBackgroundURL: 'URL фона', + splitOverviewPage: 'Разделить страницу с обзором', + manageHiddenGroup: 'Управление скрытыми группами', + transparent: 'Прозрачность', + proxyGroupIconSize: 'Размер иконки группы прокси', + proxyGroupIconMargin: 'Отступ иконки группы прокси', + allowLan: 'Разрешить локальную сеть', + proxyChainDirection: 'Направление цепочки прокси', + showFullProxyChain: 'Показать полную цепочку прокси', + reverse: 'Обратное', + sniffHost: 'Захватывать хост', + ipScreenshotTip: 'Пожалуйста, убедитесь, что реальный IP скрыт при создании скриншотов.', + showStatisticsWhenSidebarCollapsed: 'Показать статистику при сворачивании панели', + totalConnections: 'Статистика соединений', + totalConnectionsTip: + 'Учитываются только соединения, открытые во время работы панели. \nВремя начала записи: {statsStartTime}', + mostDownloadHost: 'Ресурс с наибольшим скачиванием', + mostUploadHost: 'Ресурс с наибольшей отправкой данных', + mostDownloadSourceIP: 'IP-источник с наибольшим скачиванием', + mostUploadSourceIP: 'IP-источник с наибольшей отдачей', + mostDownloadProxy: 'Прокси с наибольшим скачиванием', + mostUploadProxy: 'Прокси с наибольшей отдачей', + manual: 'Ручной', + tableWidthMode: 'Режим ширины таблицы', + testFailed: 'Тест задержки таймаут', + testFinishedTip: '{name}\n{number}/{total} Тест завершен', + testFinishedResultTip: '{name}\nТест завершен: {success} Успешно, {failed} Таймаут', + testFailedTip: '{name}\nТест задержки таймаут', + updateFinishedTip: '{number} Обновление завершено', + independentLatencyTest: 'Независимый тест задержки', + independentLatencyTestTip: + 'Включение независимого тестирования задержки попытается использовать URL-адреса, указанные в конфигурационном файле, вместо настроек URL-адресов в панели управления во время теста задержки. Задержка будет отображаться отдельно на основе URL-адресов, установленных в группах политик.', + search: 'Поиск', + searchMultiple: 'Ключевые слова через пробелы', + importing: 'Импортируется', + hideConnection: 'Скрыть соединение', + showConnection: 'Показать соединение', + hideConnectionRegex: 'Скрыть соединение Regex', + hideConnectionTip: + 'Используйте регистронезависимое регулярное выражение, чтобы найти и скрыть нежелательные соединения.', + hideLog: 'Скрыть журнал', + showLog: 'Показать журнал', + hideLogRegex: 'Скрыть журнал Regex', + hideLogTip: + 'Используйте регистронезависимое регулярное выражение, чтобы найти и скрыть нежелательные записи журнала.', + loadBalance: 'Балансировка нагрузки', + label: 'Метка', + optional: 'Необязательно', + swipeInTabs: 'Провести для переключения вкладок', + swipeInPages: 'Провести для переключения страниц', + simpleCardPreset: 'Простой', + detailedCardPreset: 'Подробный', + refresh: 'Обновить', + reset: 'Сбросить', + minProxyCardWidth: 'Минимальная ширина карточки прокси', + displayGlobalByMode: 'Отображать GLOBAL по режиму', + displaySelectedNode: 'Отображать выбранный узел', + displayLatencyNumber: 'Отображать задержку', + disconnectOnRuleDisable: 'Разрывать соединения при отключении правила', + tipForFixed: + 'Текущая стратегическая группа закреплена за текущим узлом. Нажмите «Тест скорости», чтобы восстановить поведение {type}.', + remoteAddress: 'удалённый адрес', + themeName: 'Название темы', + save: 'Сохранить', + moreDetails: 'Подробнее', + moreSettings: 'Дополнительные настройки', + customIcon: 'Пользовательская иконка', + disablePullToRefresh: 'Отключить свойство обновления', + disablePullToRefreshTip: + 'Компоненты Pull-to-refresh и виртуальной прокрутки иногда могут конфликтовать. Если при прокрутке вверх-вниз у вас часто возникают задержки или случайные срабатывания Pull-to-refresh, попробуйте отключить Pull-to-refresh.', + displayAllFeatures: 'Показать все функции', + displayAllFeaturesTip: + 'Показать все функции, включая те, которые не поддерживаются официальной версией sing-box. Если вы используете форк sing-box, поддерживающий некоторые из этих функций, вы можете попробовать их включить.', + blurIntensity: 'Интенсивность размытия', + scrollAnimationEffect: 'Эффект анимации прокрутки', + importFromFile: 'Импортировать из файла', + importFromUrl: 'Импортировать из URL', + sync: 'Синхронизировать', + upgradeSuccess: 'Обновление успешно', + numberOfChartsInSidebar: 'Количество графиков в боковой панели', + flushSmartWeights: 'Очистить Smart веса', + IPInfoAPI: 'API информации о IP', + IPInfoAPITip: + 'Этот API будет использоваться для проверки IP-адресов при запросах информации о глобальных узлах, определения геолокации IP-адресов в деталях соединения, а также для определения геолокации IP-адресов при DNS-запросах в панели.', + general: 'Универсальный', + groupProxiesByProvider: 'Группировать прокси по провайдеру', + useSmartGroupSort: 'Сортировать Smart группы по частоте использования', + RarelyUsed: 'Редко используется', + OccasionalUsed: 'Временно используется', + MostUsed: 'Часто используется', + all: 'Все', + autoDisconnectIdleUDP: 'Автоматически отключить UDP-идле', + autoDisconnectIdleUDPTime: 'Время UDP-идле', + autoDisconnectIdleUDPTip: + 'При включении открытие zashboard приведёт к закрытию UDP-соединений, продолжительность которых превышает заданное количество минут. Это может помочь в случае с некоторыми UDP-соединениями, которые не разрываются автоматически, как ожидается.', + customGlobalNode: 'Пользовательский глобальный узел', + connectionTopology: 'Топология соединений', + editBackend: 'Редактировать бэкенд', + editBackendTitle: 'Редактировать конфигурацию бэкенда', + selectBackend: 'Выберите бэкенд', + backendConnectionFailed: 'Не удалось подключиться к бэкенду, проверьте конфигурацию', + backendConfigSaved: 'Конфигурация бэкенда успешно сохранена', + saveFailed: 'Не удалось сохранить', + checking: 'Проверка...', + copySuccess: 'Копирование успешно', + importFromBackend: 'Импортировать из бэкенда', + importFromBackendTip: + 'По умолчанию ./zashboard-settings.json находится в папке ui. Пожалуйста, убедитесь, что папка ui существует и содержит файл конфигурации.', + importFailed: 'Импорт не выполнен, проверьте url {url}', + autoImportFromUrl: 'Автоматический импорт', + autoImportFromUrlTip: + 'При включении настройки будут автоматически импортироваться из url при открытии zashboard. Если хэш отличается от последнего импортированного хэша, настройки будут импортированы заново и страница будет обновлена.', + getting: 'Получение...', + mmdbSizeTip: 'Если вы используете mmdb-формат файлов geo, то количество будет равно 0', + displayFinalOutbound: 'Показать конечный исходящий узел', + groupTestUrls: 'Группа тестовых URL', + groupTestUrlsTip: + 'Для ручных тестов, запущенных из панели, установите тестовые URL > URL в конфигурации > Глобальные тестовые URL, но для групп с UrlTest/Fallback и т.д., URL в конфигурации все равно нужно изменить, поэтому это рекомендуется только для Selector и т.д. ', + groupName: 'Группа имени', + noData: 'Нет данных', + unknown: 'Неизвестно', + sourceIPAddress: 'Исходный IP-адрес', + ruleMatch: 'Совпадение правила', + proxyChainEntry: 'Вход в цепочку прокси', + proxyChainExit: 'Выход из цепочки прокси', + nodeType: 'Тип узла', + connectionCount: 'Количество соединений', + zashboardSettings: 'Настройки панели', + backendSettings: 'Настройки бэкенда', + generalSettings: 'Общие настройки', + overviewCard: 'Карточка обзора', + overviewSettings: 'Настройки обзора', + overviewCardSettings: 'Настройки карточек', + chartsCard: 'Карточка графиков', + networkCard: 'Сетевая карточка', + providerTrafficOverview: 'Обзор трафика провайдера', + topologyCharts: 'Топологические графики', + connectionHistory: 'История подключений', + ruleHitCountCard: 'Статистика попаданий/промахов правил', + ruleHitChart: 'Статистика попаданий', + ruleMissChart: 'Статистика промахов', + latency: 'Задержка', + proxyStyle: 'Стиль прокси', + icon: 'Иконка', + settingsVisibility: 'Видимость настроек', + ports: 'Порты', + actions: 'Действия', + showAllPreset: 'Показать все', + minimalPreset: 'Минимальный вид', + aggregateBy: 'Агрегация по', + aggregateBySourceIP: 'По исходному IP', + aggregateByDestination: 'По назначению', + aggregateByProcess: 'По процессу', + aggregateByOutbound: 'По исходящему', + aggregateByNode: 'По узлу', + totalTraffic: 'Общий трафик', + total: 'Итого', + clearConnectionHistory: 'Очистить историю соединений', + clearConnectionHistoryConfirm: + 'Вы уверены, что хотите очистить все данные истории соединений? Это действие нельзя отменить.', + clearConnectionHistorySuccess: 'История соединений успешно очищена', + autoCleanupInterval: 'Интервал автоматической очистки', + autoCleanupIntervalWeek: 'Еженедельно', + autoCleanupIntervalMonth: 'Ежемесячно', + autoCleanupIntervalQuarter: 'Ежеквартально', + autoCleanupIntervalNever: 'Никогда', + remainingTraffic: 'Осталось', + usedTraffic: 'Использовано', + ruleHitCount: 'Попадание: {count} раз', + ruleLastHit: 'Последнее попадание: {time}', + ruleMissCount: 'Промах: {count} раз', + ruleLastMiss: 'Последний промах: {time}', +} + +export default ru diff --git a/src/i18n/zh-tw.ts b/src/i18n/zh-tw.ts new file mode 100644 index 0000000..926b7c4 --- /dev/null +++ b/src/i18n/zh-tw.ts @@ -0,0 +1,305 @@ +import type { LANG_MESSAGE } from './en' + +const zhTW: LANG_MESSAGE = { + setup: '配置', + overview: '概覽', + proxies: '代理', + rules: '規則', + connections: '連接', + logs: '日誌', + protocol: '協議', + host: '主機', + port: '端口', + password: '密碼', + submit: '提交', + cancel: '取消', + download: '下載', + upload: '上傳', + downloadSpeed: '下載速度', + uploadSpeed: '上傳速度', + speed: '速度', + memoryUsage: '記憶體使用', + version: '版本', + noContent: '無內容', + flushFakeIP: '清空Fake IP', + flushDNSCache: '清空DNS快取', + flushDNSCacheSuccess: 'DNS快取清空成功', + flushFakeIPSuccess: 'Fake IP清空成功', + restartCoreSuccess: '核心重啟成功', + reloadConfigsSuccess: '配置重載成功', + updateGeoSuccess: 'GEO資料庫更新成功', + chains: '代理鏈', + outbound: '出站節點', + sortBy: '排序方式', + rule: '規則', + sourceIP: '源IP', + activeConnections: '活躍', + closedConnections: '已關閉', + logLevel: '日誌等級', + logType: '日誌類型', + twoColumnProxyGroup: '雙列顯示代理組', + type: '類型', + process: '進程', + connectTime: '連接時間', + sourcePort: '源端口', + destination: '解析地址', + destinationType: '解析類型', + inboundUser: '入站用戶', + dl: '下載', + ul: '上傳', + dlSpeed: '下載速度', + ulSpeed: '上傳速度', + settings: '設定', + speedtestUrl: '測速地址', + speedtestTimeout: '測速超時', + connectionStyle: '連接樣式', + card: '卡片', + table: '表格', + customTableColumns: '自訂表格列', + customCardLines: '自訂卡片行', + close: '關閉', + defaultTheme: '預設主題', + darkTheme: '深色主題', + proxyProvider: '代理提供商', + ruleProvider: '規則提供商', + expire: '到期時間', + noExpire: '不限時', + updated: '更新於', + upgradeUI: '更新面板', + reloadConfigs: '重載配置', + mode: '模式', + proxySortType: '代理排序方式', + defaultsort: '按配置排序', + nameasc: '按名稱升序', + namedesc: '按名稱降序', + latencydesc: '按延遲降序', + latencyasc: '按延遲升序', + language: '面板語言', + automaticDisconnection: '切換節點時自動斷開連接', + backend: '後端', + upgradeCore: '更新核心', + upgradeToRelease: '更新到 Release', + upgradeToAlpha: '更新到 Alpha', + updateGeoDatabase: '更新GEO', + tunMode: 'Tun 模式', + truncateProxyName: '截斷節點名稱', + sourceIPLabels: '源IP標籤', + proxyPreviewType: '節點預覽類型', + auto: '自動', + dots: '點', + bar: '條', + exportSettings: '匯出設定', + importSettings: '匯入設定', + connectionSettings: '連接設定', + proxySettings: '代理設定', + logSettings: '日誌設定', + ruleSettings: '規則設定', + connectionDetails: '連接詳情', + customTheme: '自訂主題', + unavailableProxy: '隱藏不可用節點', + protocolTips: + '您正在嘗試連接一個http後端但zashboard是通過https提供的,這可能會導致連接錯誤,請在瀏覽器設定中允許不安全的內容,或者使用http版本面板例如http://board.zash.run.place', + global: '全域', + direct: '直連', + lowLatencyDesc: '黃色的閾值', + mediumLatencyDesc: '紅色的閾值', + fonts: '面板字體', + emoji: 'Emoji', + unauthorizedTip: '未授權,請重新登入', + restartCore: '重啟核心', + checkUpgrade: '檢查更新', + autoUpgrade: '自動更新', + secondaryPath: '二級路徑', + secondaryPathTip: '如果有的話以/開頭,沒有則留空不填', + logRetentionLimit: '日誌保留條數', + DNSQuery: 'DNS 查詢', + currentBackendUnavailable: '當前後端不可用,嘗試切換到其他後端?', + confirm: '確定', + backendSwitchTo: '自動切換到{backend}', + ipv6Test: 'IPv6 測試', + socksPort: 'Socks 端口', + httpPort: 'HTTP 端口', + mixedPort: 'Mixed 端口', + redirPort: 'Redir 端口', + tproxyPort: 'TProxy 端口', + tableSize: '表格尺寸', + proxyCardSize: '節點卡片尺寸', + small: '小', + normal: '正常', + large: '大', + autoIPCheckWhenStart: '自動檢查 IP', + autoConnectionCheckWhenStart: '自動檢查連接', + chinaIP: '中國大陸 IP', + globalIP: '全球節點 IP', + networkInfo: '網路資訊', + autoSwitchTheme: '自動切換主題', + customBackgroundURL: '面板背景', + splitOverviewPage: '分離概覽頁', + manageHiddenGroup: '管理隱藏代理組', + transparent: '透明度', + proxyGroupIconSize: '策略組圖示尺寸', + proxyGroupIconMargin: '策略組圖示間距', + allowLan: '允許區域網路', + proxyChainDirection: '代理鏈方向', + showFullProxyChain: '完整顯示代理鏈', + reverse: '反向', + sniffHost: '嗅探主機', + ipScreenshotTip: '截圖時請確保隱藏IP', + showStatisticsWhenSidebarCollapsed: '側邊欄摺疊時顯示統計', + totalConnections: '連接統計', + totalConnectionsTip: '只能統計面板開啟期間的連接。\n記錄開始時間:{statsStartTime}', + mostDownloadHost: '最多下載主機', + mostUploadHost: '最多上傳主機', + mostDownloadSourceIP: '最多下載源IP', + mostUploadSourceIP: '最多上傳源IP', + mostDownloadProxy: '最多下載節點', + mostUploadProxy: '最多上傳節點', + manual: '手動', + tableWidthMode: '表格寬度模式', + testFailed: '測速超時', + testFinishedTip: '{name}\n{number}/{total} 測試完成', + testFinishedResultTip: '{name}\n測試完成: {success} 成功,{failed} 超時', + testFailedTip: '{name}\n測速超時', + updateFinishedTip: '{number} 更新完成', + independentLatencyTest: '獨立延遲測試', + independentLatencyTestTip: + '開啟獨立延遲測試會在測速中盡可能的使用配置檔案中的url覆蓋面板設定的url,並展示根據策略組設定的url獲取的延遲。', + search: '搜尋', + searchMultiple: '多個關鍵詞用空格分隔', + importing: '正在匯入', + hideConnection: '隱藏連接', + showConnection: '顯示連接', + hideConnectionRegex: '隱藏連接正則', + hideConnectionTip: '可通過不區分大小寫的正則表達式來匹配並隱藏不需要看到的連接', + hideLog: '隱藏日誌', + showLog: '顯示日誌', + hideLogRegex: '隱藏日誌正則', + hideLogTip: '可通過不區分大小寫的正則表達式來匹配並隱藏不需要看到的日誌', + loadBalance: '負載均衡', + label: '標籤', + optional: '可選', + swipeInTabs: '滑動切換頁面二級標籤', + swipeInPages: '滑動切換頁面', + simpleCardPreset: '簡潔預設', + detailedCardPreset: '詳細預設', + refresh: '重新整理', + reset: '重設', + minProxyCardWidth: '節點卡片最小寬度', + displayGlobalByMode: '根據模式顯示 GLOBAL', + displaySelectedNode: '顯示選中節點', + displayLatencyNumber: '顯示延遲數字', + disconnectOnRuleDisable: '禁用規則時打斷連接', + tipForFixed: '當前策略組被固定在了當前節點,點擊測速來恢復{type}行為', + remoteAddress: '遠端地址', + themeName: '主題名稱', + save: '儲存', + moreDetails: '更多詳情', + moreSettings: '更多設定', + customIcon: '自訂圖示', + disablePullToRefresh: '禁用下拉重新整理', + disablePullToRefreshTip: + '下拉重新整理和虛擬滾動的組件有時會有衝突,如果你在上下滾動的時候經常卡頓或者誤觸發下拉重新整理,可以嘗試禁用下拉重新整理', + displayAllFeatures: '顯示所有功能', + displayAllFeaturesTip: + '顯示所有功能,包括sing-box官方版本不支援的功能,如果您使用了fork版本的sing-box支援其中的某些功能,可以嘗試啟用', + blurIntensity: '毛玻璃強度', + scrollAnimationEffect: '滾動動畫效果', + importFromFile: '從檔案匯入', + importFromUrl: '從 URL 匯入', + sync: '同步', + upgradeSuccess: '更新成功', + numberOfChartsInSidebar: '側邊欄圖表數量', + flushSmartWeights: '清空Smart權重', + IPInfoAPI: 'IP資訊API', + IPInfoAPITip: + '此API會用於IP檢查中全球節點IP資訊查詢、連接詳情中的IP地理資訊查詢、面板DNS查詢中的IP地理資訊查詢。', + general: '通用', + groupProxiesByProvider: '節點根據提供商分組', + useSmartGroupSort: 'Smart組根據使用頻率排序', + RarelyUsed: '很少使用', + OccasionalUsed: '偶爾使用', + MostUsed: '經常使用', + all: '全部', + autoDisconnectIdleUDP: '自動斷開空閒UDP', + autoDisconnectIdleUDPTime: 'UDP空閒時間', + autoDisconnectIdleUDPTip: + '啟用後,開啟zashboard時將關閉持續時間超過配置分鐘數的UDP連接,這可能對某些無法正常自動斷開的UDP連接帶來幫助。(這是作者自己的私貨,你大概率不需要開啟它)', + customGlobalNode: '自訂全域節點', + connectionTopology: '連接拓撲', + editBackend: '編輯後端', + editBackendTitle: '修改後端配置', + selectBackend: '選擇後端', + backendConnectionFailed: '後端連接失敗,請檢查配置資訊', + backendConfigSaved: '後端配置儲存成功', + saveFailed: '儲存失敗', + checking: '檢查中...', + copySuccess: '複製成功', + importFromBackend: '從後端匯入', + importFromBackendTip: + '預設的./zashboard-settings.json位於ui資料夾下,匯入前請確保ui資料夾下存在配置檔案。', + importFailed: '匯入失敗,請檢查url {url}', + autoImportFromUrl: '自動匯入', + autoImportFromUrlTip: + '啟用後,每次開啟zashboard時會自動從url設定匯入並計算hash,如果hash與上次匯入的hash不同,則重新匯入並重新整理頁面。', + getting: '獲取中...', + mmdbSizeTip: 'mmdb等格式的geo檔案無法統計數量,因此數量為0', + displayFinalOutbound: '顯示最終出口節點', + groupTestUrls: '組測試連結', + groupTestUrlsTip: + '對於從面板手動觸發的測試,此處設定測試連結 > 配置中的連結 > 面板全域測試連結,但是對於UrlTest/Fallback等具有核心內部的定時測速邏輯的組,仍然需要修改配置中的連結,此處僅推薦Selector等場景', + groupName: '組名', + noData: '暫無資料', + unknown: '未知', + sourceIPAddress: '源IP地址', + ruleMatch: '規則匹配', + proxyChainEntry: '代理鏈入口', + proxyChainExit: '代理鏈出口', + nodeType: '節點類型', + connectionCount: '連接數', + zashboardSettings: '面板設置', + backendSettings: '後端設置', + generalSettings: '常規設置', + overviewCard: '概覽卡片', + overviewSettings: '概覽設置', + overviewCardSettings: '卡片設置', + chartsCard: '圖表卡片', + networkCard: '網絡卡片', + providerTrafficOverview: '提供商流量概覽', + topologyCharts: '拓撲圖表', + connectionHistory: '連接歷史', + ruleHitCountCard: '規則命中統計', + ruleHitChart: '命中統計', + ruleMissChart: '未命中統計', + latency: '延遲', + proxyStyle: '代理樣式', + icon: '圖示', + settingsVisibility: '設置項顯示控制', + ports: '端口', + actions: '操作', + showAllPreset: '全部顯示', + minimalPreset: '精簡顯示', + aggregateBy: '聚合方式', + aggregateBySourceIP: '按源IP', + aggregateByDestination: '按目標地址', + aggregateByProcess: '按進程', + aggregateByOutbound: '按出站節點', + aggregateByNode: '按節點', + totalTraffic: '總流量', + total: '總計', + clearConnectionHistory: '清空連接歷史', + clearConnectionHistoryConfirm: '確定要清空所有連接歷史資料嗎?此操作不可恢復。', + clearConnectionHistorySuccess: '連接歷史資料清空成功', + autoCleanupInterval: '自動清理間隔', + autoCleanupIntervalWeek: '每週', + autoCleanupIntervalMonth: '每月', + autoCleanupIntervalQuarter: '每季度', + autoCleanupIntervalNever: '永不', + remainingTraffic: '剩餘流量', + usedTraffic: '已使用', + ruleHitCount: '命中: {count} 次', + ruleLastHit: '最後命中: {time}', + ruleMissCount: '未命中: {count} 次', + ruleLastMiss: '最後未命中: {time}', +} + +export default zhTW diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts new file mode 100644 index 0000000..da3710b --- /dev/null +++ b/src/i18n/zh.ts @@ -0,0 +1,305 @@ +import type { LANG_MESSAGE } from './en' + +const zh: LANG_MESSAGE = { + setup: '配置', + overview: '概览', + proxies: '代理', + rules: '规则', + connections: '连接', + logs: '日志', + protocol: '协议', + host: '主机', + port: '端口', + password: '密码', + submit: '提交', + cancel: '取消', + download: '下载', + upload: '上传', + downloadSpeed: '下载速度', + uploadSpeed: '上传速度', + speed: '速度', + memoryUsage: '内存使用', + version: '版本', + noContent: '无内容', + flushFakeIP: '清空Fake IP', + flushDNSCache: '清空DNS缓存', + flushDNSCacheSuccess: 'DNS缓存清空成功', + flushFakeIPSuccess: 'Fake IP清空成功', + restartCoreSuccess: '核心重启成功', + reloadConfigsSuccess: '配置重载成功', + updateGeoSuccess: 'GEO数据库更新成功', + chains: '代理链', + outbound: '出站节点', + sortBy: '排序方式', + rule: '规则', + sourceIP: '源IP', + activeConnections: '活跃', + closedConnections: '已关闭', + logLevel: '日志等级', + logType: '日志类型', + twoColumnProxyGroup: '双列显示代理组', + type: '类型', + process: '进程', + connectTime: '连接时间', + sourcePort: '源端口', + destination: '解析地址', + destinationType: '解析类型', + inboundUser: '入站用户', + dl: '下载', + ul: '上传', + dlSpeed: '下载速度', + ulSpeed: '上传速度', + settings: '设置', + speedtestUrl: '测速地址', + speedtestTimeout: '测速超时', + connectionStyle: '连接样式', + card: '卡片', + table: '表格', + customTableColumns: '自定义表格列', + customCardLines: '自定义卡片行', + close: '关闭', + defaultTheme: '默认主题', + darkTheme: '深色主题', + proxyProvider: '代理提供商', + ruleProvider: '规则提供商', + expire: '到期时间', + noExpire: '不限时', + updated: '更新于', + upgradeUI: '更新面板', + reloadConfigs: '重载配置', + mode: '模式', + proxySortType: '代理排序方式', + defaultsort: '按配置排序', + nameasc: '按名称升序', + namedesc: '按名称降序', + latencydesc: '按延迟降序', + latencyasc: '按延迟升序', + language: '面板语言', + automaticDisconnection: '切换节点时自动断开连接', + backend: '后端', + upgradeCore: '更新核心', + upgradeToRelease: '更新到 Release', + upgradeToAlpha: '更新到 Alpha', + updateGeoDatabase: '更新GEO', + tunMode: 'Tun 模式', + truncateProxyName: '截断节点名称', + sourceIPLabels: '源IP标签', + proxyPreviewType: '节点预览类型', + auto: '自动', + dots: '点', + bar: '条', + exportSettings: '导出设置', + importSettings: '导入设置', + connectionSettings: '连接设置', + proxySettings: '代理设置', + logSettings: '日志设置', + ruleSettings: '规则设置', + connectionDetails: '连接详情', + customTheme: '自定义主题', + unavailableProxy: '隐藏不可用节点', + protocolTips: + '您正在尝试连接一个http后端但zashboard是通过https提供的,这可能会导致连接错误,请在浏览器设置中允许不安全的内容,或者使用http版本面板例如http://board.zash.run.place', + global: '全局', + direct: '直连', + lowLatencyDesc: '黄色的阈值', + mediumLatencyDesc: '红色的阈值', + fonts: '面板字体', + emoji: 'Emoji', + unauthorizedTip: '未授权,请重新登录', + restartCore: '重启核心', + checkUpgrade: '检查更新', + autoUpgrade: '自动更新', + secondaryPath: '二级路径', + secondaryPathTip: '如果有的话以/开头,没有则留空不填', + logRetentionLimit: '日志保留条数', + DNSQuery: 'DNS 查询', + currentBackendUnavailable: '当前后端不可用,尝试切换到其他后端?', + confirm: '确定', + backendSwitchTo: '自动切换到{backend}', + ipv6Test: 'IPv6 测试', + socksPort: 'Socks 端口', + httpPort: 'HTTP 端口', + mixedPort: 'Mixed 端口', + redirPort: 'Redir 端口', + tproxyPort: 'TProxy 端口', + tableSize: '表格尺寸', + proxyCardSize: '节点卡片尺寸', + small: '小', + normal: '正常', + large: '大', + autoIPCheckWhenStart: '自动检查 IP', + autoConnectionCheckWhenStart: '自动检查连接', + chinaIP: '中国大陆 IP', + globalIP: '全球节点 IP', + networkInfo: '网络信息', + autoSwitchTheme: '自动切换主题', + customBackgroundURL: '面板背景', + splitOverviewPage: '分离概览页', + manageHiddenGroup: '管理隐藏代理组', + transparent: '透明度', + proxyGroupIconSize: '策略组图标尺寸', + proxyGroupIconMargin: '策略组图标间距', + allowLan: '允许局域网', + proxyChainDirection: '代理链方向', + showFullProxyChain: '完整显示代理链', + reverse: '反向', + sniffHost: '嗅探主机', + ipScreenshotTip: '截图时请确保隐藏IP', + showStatisticsWhenSidebarCollapsed: '侧边栏折叠时显示统计', + totalConnections: '连接统计', + totalConnectionsTip: '只能统计面板打开期间的连接。\n记录开始时间:{statsStartTime}', + mostDownloadHost: '最多下载主机', + mostUploadHost: '最多上传主机', + mostDownloadSourceIP: '最多下载源IP', + mostUploadSourceIP: '最多上传源IP', + mostDownloadProxy: '最多下载节点', + mostUploadProxy: '最多上传节点', + manual: '手动', + tableWidthMode: '表格宽度模式', + testFailed: '测速超时', + testFinishedTip: '{name}\n{number}/{total} 测试完成', + testFinishedResultTip: '{name}\n测试完成: {success} 成功,{failed} 超时', + testFailedTip: '{name}\n测速超时', + updateFinishedTip: '{number} 更新完成', + independentLatencyTest: '独立延迟测试', + independentLatencyTestTip: + '开启独立延迟测试会在测速中尽可能的使用配置文件中的url覆盖面板设置的url,并展示根据策略组设置的url获取的延迟。', + search: '搜索', + searchMultiple: '多个关键词用空格分隔', + importing: '正在导入', + hideConnection: '隐藏连接', + showConnection: '显示连接', + hideConnectionRegex: '隐藏连接正则', + hideConnectionTip: '可通过不区分大小写的正则表达式来匹配并隐藏不需要看到的连接', + hideLog: '隐藏日志', + showLog: '显示日志', + hideLogRegex: '隐藏日志正则', + hideLogTip: '可通过不区分大小写的正则表达式来匹配并隐藏不需要看到的日志', + loadBalance: '负载均衡', + label: '标签', + optional: '可选', + swipeInTabs: '滑动切换页面二级标签', + swipeInPages: '滑动切换页面', + simpleCardPreset: '简洁预设', + detailedCardPreset: '详细预设', + refresh: '刷新', + reset: '重置', + minProxyCardWidth: '节点卡片最小宽度', + displayGlobalByMode: '根据模式显示 GLOBAL', + displaySelectedNode: '显示选中节点', + displayLatencyNumber: '显示延迟数字', + disconnectOnRuleDisable: '禁用规则时打断连接', + tipForFixed: '当前策略组被固定在了当前节点,点击测速来恢复{type}行为', + remoteAddress: '远端地址', + themeName: '主题名称', + save: '保存', + moreDetails: '更多详情', + moreSettings: '更多设置', + customIcon: '自定义图标', + disablePullToRefresh: '禁用下拉刷新', + disablePullToRefreshTip: + '下拉刷新和虚拟滚动的组件有时会有冲突,如果你在上下滚动的时候经常卡顿或者误触发下拉刷新,可以尝试禁用下拉刷新', + displayAllFeatures: '显示所有功能', + displayAllFeaturesTip: + '显示所有功能,包括sing-box官方版本不支持的功能,如果您使用了fork版本的sing-box支持其中的某些功能,可以尝试启用', + blurIntensity: '毛玻璃强度', + scrollAnimationEffect: '滚动动画效果', + importFromFile: '从文件导入', + importFromUrl: '从 URL 导入', + sync: '同步', + upgradeSuccess: '更新成功', + numberOfChartsInSidebar: '侧边栏图表数量', + flushSmartWeights: '清空Smart权重', + IPInfoAPI: 'IP信息API', + IPInfoAPITip: + '此API会用于IP检查中全球节点IP信息查询、连接详情中的IP地理信息查询、面板DNS查询中的IP地理信息查询。', + general: '通用', + groupProxiesByProvider: '节点根据提供商分组', + useSmartGroupSort: 'Smart组根据使用频率排序', + RarelyUsed: '很少使用', + OccasionalUsed: '偶尔使用', + MostUsed: '经常使用', + all: '全部', + autoDisconnectIdleUDP: '自动断开空闲UDP', + autoDisconnectIdleUDPTime: 'UDP空闲时间', + autoDisconnectIdleUDPTip: + '启用后,打开zashboard时将关闭持续时间超过配置分钟数的UDP连接,这可能对某些无法正常自动断开的UDP连接带来帮助。(这是作者自己的私货,你大概率不需要打开它)', + customGlobalNode: '自定义全局节点', + connectionTopology: '连接拓扑', + editBackend: '编辑后端', + editBackendTitle: '修改后端配置', + selectBackend: '选择后端', + backendConnectionFailed: '后端连接失败,请检查配置信息', + backendConfigSaved: '后端配置保存成功', + saveFailed: '保存失败', + checking: '检查中...', + copySuccess: '复制成功', + importFromBackend: '从后端导入', + importFromBackendTip: + '默认的./zashboard-settings.json位于ui文件夹下,导入前请确保ui文件夹下存在配置文件。', + importFailed: '导入失败,请检查url {url}', + autoImportFromUrl: '自动导入', + autoImportFromUrlTip: + '启用后,每次打开zashboard时会自动从url设置导入并计算hash,如果hash与上次导入的hash不同,则重新导入并刷新页面。', + getting: '获取中...', + mmdbSizeTip: 'mmdb等格式的geo文件无法统计数量,因此数量为0', + displayFinalOutbound: '显示最终出口节点', + groupTestUrls: '组测试链接', + groupTestUrlsTip: + '对于从面板手动触发的测试,此处设置测试链接 > 配置中的链接 > 面板全局测试链接,但是对于UrlTest/Fallback等具有核心内部的定时测速逻辑的组,仍然需要修改配置中的链接,此处仅推荐Selector等场景', + groupName: '组名', + noData: '暂无数据', + unknown: '未知', + sourceIPAddress: '源IP地址', + ruleMatch: '规则匹配', + proxyChainEntry: '代理链入口', + proxyChainExit: '代理链出口', + nodeType: '节点类型', + connectionCount: '连接数', + zashboardSettings: '面板设置', + backendSettings: '后端设置', + generalSettings: '常规设置', + overviewCard: '概览卡片', + overviewSettings: '概览设置', + overviewCardSettings: '卡片设置', + chartsCard: '图表卡片', + networkCard: '网络卡片', + providerTrafficOverview: '提供商流量概览', + topologyCharts: '拓扑图表', + connectionHistory: '连接历史', + ruleHitCountCard: '规则命中统计', + ruleHitChart: '命中统计', + ruleMissChart: '未命中统计', + latency: '延迟', + proxyStyle: '代理样式', + icon: '图标', + settingsVisibility: '设置项显示控制', + ports: '端口', + actions: '操作', + showAllPreset: '全部显示', + minimalPreset: '精简显示', + aggregateBy: '聚合方式', + aggregateBySourceIP: '按源IP', + aggregateByDestination: '按目标地址', + aggregateByProcess: '按进程', + aggregateByOutbound: '按出站节点', + aggregateByNode: '按节点', + totalTraffic: '总流量', + total: '总计', + clearConnectionHistory: '清空连接历史', + clearConnectionHistoryConfirm: '确定要清空所有连接历史数据吗?此操作不可恢复。', + clearConnectionHistorySuccess: '连接历史数据清空成功', + autoCleanupInterval: '自动清理间隔', + autoCleanupIntervalWeek: '每周', + autoCleanupIntervalMonth: '每月', + autoCleanupIntervalQuarter: '每季度', + autoCleanupIntervalNever: '永不', + remainingTraffic: '剩余流量', + usedTraffic: '已使用', + ruleHitCount: '命中: {count} 次', + ruleLastHit: '最后命中: {time}', + ruleMissCount: '未命中: {count} 次', + ruleLastMiss: '最后未命中: {time}', +} + +export default zh diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..ffa6dbc --- /dev/null +++ b/src/main.ts @@ -0,0 +1,20 @@ +import '@/helper/dayjs' +import 'tippy.js/animations/scale.css' +import 'tippy.js/dist/tippy.css' +import { createApp } from 'vue' +import App from './App.vue' +import { loadFonts } from './assets/load-fonts' +import './assets/main.css' +import './assets/theme.css' +import { applyCustomThemes } from './helper' +import { i18n } from './i18n' +import router from './router' + +applyCustomThemes() +loadFonts() + +const app = createApp(App) + +app.use(router) +app.use(i18n) +app.mount('#app') diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..6a6fe6a --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,108 @@ +import { ROUTE_NAME } from '@/constant' +import { renderRoutes } from '@/helper' +import { i18n } from '@/i18n' +import { language } from '@/store/settings' +import { activeBackend } from '@/store/setup' +import ConnectionsPage from '@/views/ConnectionsPage.vue' +import HomePage from '@/views/HomePage.vue' +import LogsPage from '@/views/LogsPage.vue' +import OverviewPage from '@/views/OverviewPage.vue' +import ProxiesPage from '@/views/ProxiesPage.vue' +import RulesPage from '@/views/RulesPage.vue' +import SettingsPage from '@/views/SettingsPage.vue' +import SetupPage from '@/views/SetupPage.vue' +import { useTitle } from '@vueuse/core' +import { watch } from 'vue' +import { createRouter, createWebHashHistory } from 'vue-router' + +const childrenRouter = [ + { + path: 'proxies', + name: ROUTE_NAME.proxies, + component: ProxiesPage, + }, + { + path: 'overview', + name: ROUTE_NAME.overview, + component: OverviewPage, + }, + { + path: 'connections', + name: ROUTE_NAME.connections, + component: ConnectionsPage, + }, + { + path: 'logs', + name: ROUTE_NAME.logs, + component: LogsPage, + }, + { + path: 'rules', + name: ROUTE_NAME.rules, + component: RulesPage, + }, + { + path: 'settings', + name: ROUTE_NAME.settings, + component: SettingsPage, + }, +] + +const router = createRouter({ + history: createWebHashHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/', + redirect: ROUTE_NAME.proxies, + component: HomePage, + children: childrenRouter, + }, + { + path: '/setup', + name: ROUTE_NAME.setup, + component: SetupPage, + }, + { + path: '/:catchAll(.*)', + redirect: ROUTE_NAME.proxies, + }, + ], +}) + +const title = useTitle('zashboard') +const setTitleByName = (name: string | symbol | undefined) => { + if (typeof name === 'string' && activeBackend.value) { + title.value = `zashboard | ${i18n.global.t(name)}` + } else { + title.value = 'zashboard' + } +} + +router.beforeEach((to, from) => { + const toIndex = renderRoutes.value.findIndex((item) => item === to.name) + const fromIndex = renderRoutes.value.findIndex((item) => item === from.name) + + if (toIndex === 0 && fromIndex === renderRoutes.value.length - 1) { + to.meta.transition = 'slide-left' + } else if (toIndex === renderRoutes.value.length - 1 && fromIndex === 0) { + to.meta.transition = 'slide-right' + } else if (toIndex !== fromIndex) { + to.meta.transition = toIndex < fromIndex ? 'slide-right' : 'slide-left' + } + + if (!activeBackend.value && to.name !== ROUTE_NAME.setup) { + router.push({ name: ROUTE_NAME.setup }) + } +}) + +router.afterEach((to) => { + setTitleByName(to.name) +}) + +watch(language, () => { + setTimeout(() => { + setTitleByName(router.currentRoute.value.name) + }) +}) + +export default router diff --git a/src/store/config.ts b/src/store/config.ts new file mode 100644 index 0000000..3a3efbf --- /dev/null +++ b/src/store/config.ts @@ -0,0 +1,28 @@ +import { getConfigsAPI, patchConfigsAPI } from '@/api' +import type { Config } from '@/types' +import { ref } from 'vue' + +export const configs = ref({ + port: 0, + 'socks-port': 0, + 'redir-port': 0, + 'tproxy-port': 0, + 'mixed-port': 0, + 'allow-lan': false, + 'bind-address': '', + mode: '', + 'mode-list': [], + modes: [], + 'log-level': '', + ipv6: false, + tun: { + enable: false, + }, +}) +export const fetchConfigs = async () => { + configs.value = (await getConfigsAPI()).data +} +export const updateConfigs = async (cfg: Record) => { + await patchConfigsAPI(cfg) + fetchConfigs() +} diff --git a/src/store/connHistory.ts b/src/store/connHistory.ts new file mode 100644 index 0000000..cee9ed5 --- /dev/null +++ b/src/store/connHistory.ts @@ -0,0 +1,144 @@ +import { getProcessFromConnection } from '@/helper' +import { + ConnectionHistoryType, + getConnectionHistoryFromIndexedDB, + saveConnectionHistoryToIndexedDB, + type ConnectionHistoryData, +} from '@/helper/indexeddb' +import type { Connection } from '@/types' +import ipaddr from 'ipaddr.js' +import { ref } from 'vue' +import { activeBackend } from './setup' + +const isInitializedPromise = ref( + new Promise((resolve) => { + resolve(false) + }), +) +const uuid = () => activeBackend.value?.uuid || '' +const allHistoryTypes = [ + ConnectionHistoryType.SourceIP, + ConnectionHistoryType.Destination, + ConnectionHistoryType.Process, + ConnectionHistoryType.Outbound, +] + +export const aggregatedDataMap = ref>({ + [ConnectionHistoryType.SourceIP]: [], + [ConnectionHistoryType.Destination]: [], + [ConnectionHistoryType.Process]: [], + [ConnectionHistoryType.Outbound]: [], +}) + +export const initAggregatedDataMap = () => { + aggregatedDataMap.value = { + [ConnectionHistoryType.SourceIP]: [], + [ConnectionHistoryType.Destination]: [], + [ConnectionHistoryType.Process]: [], + [ConnectionHistoryType.Outbound]: [], + } + isInitializedPromise.value = new Promise(async (resolve) => { + for (const type of allHistoryTypes) { + const historicalData = await getConnectionHistoryFromIndexedDB(uuid(), type) + + let finalData = historicalData + if (historicalData.length > 2000) { + finalData = historicalData.sort((a, b) => b.download - a.download).slice(0, 1500) + await saveConnectionHistoryToIndexedDB(uuid(), type, finalData) + } + + aggregatedDataMap.value[type] = finalData + } + resolve(true) + }) +} + +export const aggregateConnections = ( + connections: Connection[], + type: ConnectionHistoryType, +): ConnectionHistoryData[] => { + const map = new Map() + + connections.forEach((connection) => { + let key: string = '' + + if (type === ConnectionHistoryType.SourceIP) { + key = connection.metadata.sourceIP + } else if (type === ConnectionHistoryType.Destination) { + const hostkey = + connection.metadata.host || + connection.metadata.sniffHost || + connection.metadata.destinationIP + if (ipaddr.IPv4.isValid(hostkey) || ipaddr.IPv6.isValid(hostkey)) { + key = hostkey + } else { + key = hostkey.split('.').slice(-2).join('.') + } + } else if (type === ConnectionHistoryType.Process) { + key = getProcessFromConnection(connection) + } else if (type === ConnectionHistoryType.Outbound) { + key = connection.chains[0] || '-' + } + + if (map.has(key)) { + const existing = map.get(key)! + existing.download += connection.download + existing.upload += connection.upload + existing.count += 1 + } else { + map.set(key, { + key, + download: connection.download, + upload: connection.upload, + count: 1, + }) + } + }) + + return Array.from(map.values()) +} + +export const mergeAggregatedData = ( + historical: ConnectionHistoryData[], + newData: ConnectionHistoryData[], +): ConnectionHistoryData[] => { + const map = new Map() + + historical.forEach((item) => { + map.set(item.key, { ...item }) + }) + + newData.forEach((item) => { + if (map.has(item.key)) { + const existing = map.get(item.key)! + existing.download += item.download + existing.upload += item.upload + existing.count += item.count + } else { + map.set(item.key, { ...item }) + } + }) + + return Array.from(map.values()) +} + +export const saveConnectionHistory = async (newClosedConnections: Connection[]) => { + if (newClosedConnections.length === 0) { + return + } + + await isInitializedPromise.value + + for (const type of allHistoryTypes) { + try { + const newAggregatedData = aggregateConnections(newClosedConnections, type) + const historicalData = aggregatedDataMap.value[type] + const mergedData = mergeAggregatedData(historicalData, newAggregatedData) + + aggregatedDataMap.value[type] = mergedData + await saveConnectionHistoryToIndexedDB(uuid(), type, mergedData) + } catch (error) { + console.error(`Failed to save connection history for ${type}:`, error) + } + } +} diff --git a/src/store/connections.ts b/src/store/connections.ts new file mode 100644 index 0000000..a5526f1 --- /dev/null +++ b/src/store/connections.ts @@ -0,0 +1,229 @@ +import { disconnectByIdAPI, fetchConnectionsAPI } from '@/api' +import { CONNECTION_TAB_TYPE, SORT_DIRECTION, SORT_TYPE } from '@/constant' +import { getChainsStringFromConnection, getInboundUserFromConnection } from '@/helper' +import type { Connection, ConnectionRawMessage } from '@/types' +import { useStorage, watchOnce } from '@vueuse/core' +import dayjs from 'dayjs' +import { computed, ref, watch } from 'vue' +import { initAggregatedDataMap, saveConnectionHistory } from './connHistory' +import { autoDisconnectIdleUDP, autoDisconnectIdleUDPTime, useConnectionCard } from './settings' + +export const connectionTabShow = ref(CONNECTION_TAB_TYPE.ACTIVE) +export const connectionSortType = useStorage( + 'config/connection-sort-type', + SORT_TYPE.HOST, +) +export const connectionSortDirection = useStorage( + 'config/connection-sort-direction', + SORT_DIRECTION.ASC, +) + +export const quickFilterRegex = useStorage('config/quick-filter-regex', 'direct|dns-out') +export const quickFilterEnabled = useStorage('config/quick-filter-enabled', false) +export const connectionFilter = ref('') +export const sourceIPFilter = ref(null) + +export const activeConnections = ref([]) +export const closedConnections = ref([]) +export const isPaused = ref(false) + +export const downloadTotal = ref(0) +export const uploadTotal = ref(0) + +let cancel: () => void +let previousConnectionsMap = new Map() + +export const initConnections = () => { + cancel?.() + activeConnections.value = [] + closedConnections.value = [] + downloadTotal.value = 0 + uploadTotal.value = 0 + previousConnectionsMap.clear() + initAggregatedDataMap() + const ws = fetchConnectionsAPI<{ + connections: ConnectionRawMessage[] + downloadTotal: number + uploadTotal: number + memory: number + }>() + const unwatch = watch(ws.data, (data) => { + if (!data) return + + downloadTotal.value = data.downloadTotal + uploadTotal.value = data.uploadTotal + + if (isPaused.value) { + return + } + + const currentConnectionsMap = new Map() + + activeConnections.value = + data.connections?.map((conn) => { + const connection = conn as Connection + const preConnection = previousConnectionsMap.get(connection.id) + + if ( + (connection.metadata.destinationPort === '443' || connection.metadata.sniffHost) && + connection.metadata.network === 'udp' + ) { + connection.metadata.network = 'quic' + } + + if (!preConnection) { + connection.downloadSpeed = 0 + connection.uploadSpeed = 0 + } else { + connection.downloadSpeed = connection.download - preConnection.download + connection.uploadSpeed = connection.upload - preConnection.upload + } + + previousConnectionsMap.delete(connection.id) + currentConnectionsMap.set(connection.id, connection) + return connection + }) ?? [] + + const newlyClosedConnections = Array.from(previousConnectionsMap.values()) + closedConnections.value = closedConnections.value.concat(newlyClosedConnections).slice(-500) + + if (newlyClosedConnections.length > 0) { + saveConnectionHistory(newlyClosedConnections) + } + + previousConnectionsMap = currentConnectionsMap + }) + + if (autoDisconnectIdleUDP.value) { + watchOnce(activeConnections, () => { + activeConnections.value + .filter((conn) => conn.metadata.network !== 'tcp') + .forEach((conn) => { + const now = dayjs() + const start = dayjs(conn.start) + + if (now.diff(start, 'minute') > autoDisconnectIdleUDPTime.value) { + disconnectByIdAPI(conn.id) + } + }) + }) + } + + cancel = () => { + unwatch() + ws.close() + } +} + +const isDesc = computed(() => { + return connectionSortDirection.value === SORT_DIRECTION.DESC +}) + +const sortFunctionMap: Record number> = { + [SORT_TYPE.HOST]: (a: Connection, b: Connection) => { + return (a.metadata.host || a.metadata.destinationIP).localeCompare( + b.metadata.host || b.metadata.destinationIP, + ) + }, + [SORT_TYPE.RULE]: (a: Connection, b: Connection) => { + return a.rule.localeCompare(b.rule) + }, + [SORT_TYPE.CHAINS]: (a: Connection, b: Connection) => { + return getChainsStringFromConnection(a).localeCompare(getChainsStringFromConnection(b)) + }, + [SORT_TYPE.DOWNLOAD]: (a: Connection, b: Connection) => { + return a.download - b.download + }, + [SORT_TYPE.DOWNLOAD_SPEED]: (a: Connection, b: Connection) => { + return a.downloadSpeed - b.downloadSpeed + }, + [SORT_TYPE.UPLOAD]: (a: Connection, b: Connection) => { + return a.upload - b.upload + }, + [SORT_TYPE.UPLOAD_SPEED]: (a: Connection, b: Connection) => { + return a.uploadSpeed - b.uploadSpeed + }, + [SORT_TYPE.SOURCE_IP]: (a: Connection, b: Connection) => { + return a.metadata.sourceIP.localeCompare(b.metadata.sourceIP) + }, + [SORT_TYPE.TYPE]: (a: Connection, b: Connection) => { + return (a.metadata.type + a.metadata.network).localeCompare( + b.metadata.type + b.metadata.network, + ) + }, + [SORT_TYPE.CONNECT_TIME]: (a: Connection, b: Connection) => { + return dayjs(a.start).valueOf() - dayjs(b.start).valueOf() + }, + [SORT_TYPE.INBOUND_USER]: (a: Connection, b: Connection) => { + return getInboundUserFromConnection(a).localeCompare(getInboundUserFromConnection(b)) + }, +} + +export const connections = computed(() => { + return connectionTabShow.value === CONNECTION_TAB_TYPE.ACTIVE + ? activeConnections.value + : closedConnections.value +}) + +export const renderConnections = computed(() => { + const lowerCaseFilter = connectionFilter.value.split(' ').map((f) => f.toLowerCase().trim()) + + let regex: RegExp | null = null + + if (quickFilterEnabled.value && quickFilterRegex.value) { + regex = new RegExp(quickFilterRegex.value, 'i') + } + + return connections.value + .filter((conn) => { + const metadatas = [ + conn.metadata.host, + conn.metadata.destinationIP, + conn.metadata.destinationPort, + conn.metadata.sourceIP, + conn.metadata.sourcePort, + conn.metadata.sniffHost, + conn.metadata.processPath, + conn.metadata.type, + conn.metadata.network, + conn.chains.join(''), + conn.rule, + conn.rulePayload, + ] + + if ( + sourceIPFilter.value !== null && + sourceIPFilter.value.every((i) => i !== conn.metadata.sourceIP) + ) { + return false + } + + if (regex) { + const quickFilterMatch = metadatas.some((i) => regex.test(i)) + + if (quickFilterMatch) { + return false + } + } + + if (connectionFilter.value) { + return lowerCaseFilter.every((i) => metadatas.some((j) => j?.toLowerCase().includes(i))) + } + + return true + }) + .sort((a, b) => { + if (useConnectionCard.value && isDesc.value) { + ;[a, b] = [b, a] + } + const sortResult = useConnectionCard.value + ? sortFunctionMap[connectionSortType.value](a, b) + : sortFunctionMap[SORT_TYPE.HOST](a, b) + + if (sortResult === 0) { + return a.id.localeCompare(b.id) + } + + return sortResult + }) +}) diff --git a/src/store/logs.ts b/src/store/logs.ts new file mode 100644 index 0000000..02c561d --- /dev/null +++ b/src/store/logs.ts @@ -0,0 +1,90 @@ +import { fetchLogsAPI } from '@/api' +import { LOG_LEVEL } from '@/constant' +import type { Log, LogWithSeq } from '@/types' +import { useStorage } from '@vueuse/core' +import dayjs from 'dayjs' +import { throttle } from 'lodash' +import { ref, watch } from 'vue' +import { logRetentionLimit, sourceIPLabelList } from './settings' +import { activeBackend } from './setup' + +export const logs = ref([]) +export const logFilter = ref('') +export const logTypeFilter = ref('') +export const isPaused = ref(false) +export const logLevel = useStorage('config/log-level', LOG_LEVEL.Info) +export const logFilterRegex = useStorage('config/log-filter-regex', '') +export const logFilterEnabled = useStorage('config/log-filter-enabled', false) + +let cancel: () => void +let logsTemp: LogWithSeq[] = [] + +const sliceLogs = throttle(() => { + logs.value = logsTemp.concat(logs.value).slice(0, logRetentionLimit.value) + logsTemp = [] +}, 500) + +const ipSourceMatchs: [RegExp, string][] = [] +const restructMatchs = () => { + ipSourceMatchs.length = 0 + for (const { key, label, scope } of sourceIPLabelList.value) { + if (scope && !scope.includes(activeBackend.value?.uuid as string)) continue + if (key.startsWith('/')) continue + + if (key.includes(':')) { + const regex = new RegExp(`${key}]:`, 'ig') + ipSourceMatchs.push([regex, `${key}] (${label}) :`]) + } else { + const regex = new RegExp(`${key}:`, 'ig') + ipSourceMatchs.push([regex, `${key} (${label}) :`]) + } + } +} + +watch( + () => [sourceIPLabelList.value, activeBackend.value], + () => { + restructMatchs() + }, + { + immediate: true, + deep: true, + }, +) + +export const initLogs = () => { + cancel?.() + logs.value = [] + logsTemp = [] + + let idx = 1 + const ws = fetchLogsAPI({ + level: logLevel.value, + }) + + const unwatch = watch(ws.data, (data) => { + if (!data) return + + if (isPaused.value) { + idx++ + return + } + + for (const [regex, label] of ipSourceMatchs) { + data.payload = data.payload.replace(regex, label) + } + + logsTemp.unshift({ + ...data, + time: dayjs().format('HH:mm:ss'), + seq: idx++, + }) + + sliceLogs() + }) + + cancel = () => { + unwatch() + ws.close() + } +} diff --git a/src/store/overview.ts b/src/store/overview.ts new file mode 100644 index 0000000..7954f08 --- /dev/null +++ b/src/store/overview.ts @@ -0,0 +1,88 @@ +import { fetchMemoryAPI, fetchTrafficAPI } from '@/api' +import { ref, watch } from 'vue' +import { activeConnections } from './connections' + +export const timeSaved = 60 +const initValue = new Array(timeSaved).fill(0).map((v, i) => ({ name: i, value: v })) + +export const memory = ref(0) +export const memoryHistory = ref([...initValue]) +export const connectionsHistory = ref([...initValue]) + +export const downloadSpeed = ref(0) +export const uploadSpeed = ref(0) +export const downloadSpeedHistory = ref([...initValue]) +export const uploadSpeedHistory = ref([...initValue]) + +let cancel: () => void + +export const initSatistic = () => { + cancel?.() + + downloadSpeedHistory.value = [...initValue] + uploadSpeedHistory.value = [...initValue] + memoryHistory.value = [...initValue] + + const { data: memoryWsData, close: memoryWsClose } = fetchMemoryAPI<{ + inuse: number + }>() + const unwatchMemory = watch( + () => memoryWsData.value, + (data) => { + if (!data) return + const timestamp = Date.now().valueOf() + + if (data.inuse === 0) { + return + } + + memory.value = data.inuse + memoryHistory.value.push({ + value: data.inuse, + name: timestamp, + }) + connectionsHistory.value.push({ + value: activeConnections.value.length, + name: timestamp, + }) + + memoryHistory.value = memoryHistory.value.slice(-1 * timeSaved) + connectionsHistory.value = connectionsHistory.value.slice(-1 * timeSaved) + }, + ) + + const { data: trafficWsData, close: trafficWsClose } = fetchTrafficAPI<{ + down: number + up: number + }>() + const unwatchTraffic = watch( + () => trafficWsData.value, + (data) => { + if (!data) return + + const timestamp = Date.now().valueOf() + + downloadSpeed.value = data.down + uploadSpeed.value = data.up + + downloadSpeedHistory.value.push({ + value: data.down, + name: timestamp, + }) + uploadSpeedHistory.value.push({ + value: data.up, + name: timestamp, + }) + + downloadSpeedHistory.value = downloadSpeedHistory.value.slice(-1 * timeSaved) + uploadSpeedHistory.value = uploadSpeedHistory.value.slice(-1 * timeSaved) + }, + ) + + cancel = () => { + memoryWsClose() + trafficWsClose() + unwatchMemory() + unwatchTraffic() + } +} diff --git a/src/store/proxies.ts b/src/store/proxies.ts new file mode 100644 index 0000000..afc0edd --- /dev/null +++ b/src/store/proxies.ts @@ -0,0 +1,435 @@ +import { + deleteFixedProxyAPI, + disconnectByIdAPI, + fetchProxiesAPI, + fetchProxyGroupLatencyAPI, + fetchProxyLatencyAPI, + fetchProxyProviderAPI, + isSingBox, + selectProxyAPI, +} from '@/api' +import { + GLOBAL, + IPV6_TEST_URL, + NOT_CONNECTED, + PROXY_TAB_TYPE, + PROXY_TYPE, + TEST_URL, +} from '@/constant' +import { isProxyGroup } from '@/helper' +import { showNotification } from '@/helper/notification' +import type { Proxy, ProxyProvider } from '@/types' +import { useStorage } from '@vueuse/core' +import { last } from 'lodash' +import pLimit from 'p-limit' +import { computed, ref } from 'vue' +import { activeConnections } from './connections' +import { + automaticDisconnection, + groupTestUrls, + iconReflectList, + independentLatencyTest, + IPv6test, + speedtestTimeout, + speedtestUrl, +} from './settings' +import { initSmartWeights } from './smart' + +export const proxiesFilter = ref('') +export const proxiesTabShow = ref(PROXY_TAB_TYPE.PROXIES) + +export const proxyGroupList = ref([]) +export const proxyMap = ref>({}) +export const IPv6Map = useStorage>('config/ipv6-map', {}) +export const hiddenGroupMap = useStorage>('config/hidden-group-map', {}) +export const proxyProviederList = ref([]) + +const speedtestUrlWithDefault = computed(() => { + return speedtestUrl.value || TEST_URL +}) + +export const getTestUrl = (groupName?: string) => { + if (!groupName || !independentLatencyTest.value) { + return speedtestUrlWithDefault.value + } + + const groupTestUrl = groupTestUrls.value.find((item) => item.name === groupName) + + if (groupTestUrl) { + return groupTestUrl.url + } + + const proxyNode = + proxyMap.value[groupName] || proxyProviederList.value.find((p) => p.name === groupName) + + return proxyNode?.testUrl || speedtestUrlWithDefault.value +} + +export const getLatencyByName = (proxyName: string, groupName?: string) => { + const history = getHistoryByName(proxyName, groupName) + + return getLatencyFromHistory(history) +} + +export const getHistoryByName = (proxyName: string, groupName?: string) => { + if (independentLatencyTest.value && !isSingBox.value) { + const proxyNode = proxyMap.value[proxyName] + const url = getTestUrl(groupName) + + if (!proxyNode) { + return [] + } + + if (!proxyNode?.extra) { + proxyNode.extra = {} + } + + if (!proxyNode.extra?.[url]) { + proxyNode.extra[url] = { + history: [], + alive: true, + } + } + + return proxyNode?.extra?.[url]?.history + } + + const nowNode = proxyMap.value[getNowProxyNodeName(proxyName)] + + return nowNode?.history +} + +export const getIPv6ByName = (proxyName: string) => { + return IPv6Map.value[getNowProxyNodeName(proxyName)] +} + +let fetchTime = 0 + +export const fetchProxies = async () => { + const nowTime = Date.now() + + fetchTime = nowTime + + const [proxyRes, providerRes] = await Promise.all([fetchProxiesAPI(), fetchProxyProviderAPI()]) + const proxyData = proxyRes.data + const providerData = providerRes.data + + if (fetchTime !== nowTime) { + return + } + + const sortIndex = proxyData.proxies[GLOBAL].all ?? [] + const allProviderProxies: Record = {} + const providers = Object.values(providerData.providers).filter( + (provider) => provider.name !== 'default' && provider.vehicleType !== 'Compatible', + ) + + for (const provider of providers) { + for (const proxy of provider.proxies) { + allProviderProxies[proxy.name] = proxy + } + } + + proxyMap.value = { + ...allProviderProxies, + ...proxyData.proxies, + } + proxyGroupList.value = Object.values(proxyData.proxies) + .filter((proxy) => proxy.all?.length && proxy.name !== GLOBAL) + .sort((prev, next) => { + const prevIndex = sortIndex.indexOf(prev.name) + const nextIndex = sortIndex.indexOf(next.name) + + if (prevIndex === -1 && nextIndex === -1) { + return 0 + } + if (prevIndex === -1) { + return 1 + } + if (nextIndex === -1) { + return -1 + } + // 都在 sortIndex 中,按索引排序 + return prevIndex - nextIndex + }) + .map((proxy) => proxy.name) + + proxyProviederList.value = providers + + const smartGroups: string[] = [] + + Object.entries(proxyMap.value).forEach(([name, proxy]) => { + const iconReflect = iconReflectList.value.find((icon) => icon.name === name) + + if (iconReflect) { + proxyMap.value[name].icon = iconReflect.icon + } + if (IPv6test.value && getIPv6FromExtra(proxy)) { + IPv6Map.value[name] = true + } + + if (proxy.type.toLowerCase() === PROXY_TYPE.Smart) { + smartGroups.push(name) + } + }) + + if (smartGroups.length > 0) { + initSmartWeights(smartGroups) + } +} + +export const handlerProxySelect = async (proxyGroupName: string, proxyName: string) => { + const proxyGroup = proxyMap.value[proxyGroupName] + + if (proxyGroup.type.toLowerCase() === PROXY_TYPE.LoadBalance) return + if (proxyGroup.now === proxyName) { + await fetchProxies() + if (proxyGroup.now === proxyName) return + } + + await selectProxyAPI(proxyGroupName, proxyName) + proxyMap.value[proxyGroupName].now = proxyName + + if (automaticDisconnection.value) { + activeConnections.value + .filter((c) => c.chains.includes(proxyGroupName)) + .forEach((c) => disconnectByIdAPI(c.id)) + } + fetchProxies() +} + +const latencyTestForSingle = async (proxyName: string, url: string, timeout: number) => { + const now = getNowProxyNodeName(proxyName) + + if (IPv6test.value) { + try { + const { data: ipv6LatencyResult } = await fetchProxyLatencyAPI(now, IPV6_TEST_URL, 2000) + + IPv6Map.value[now] = ipv6LatencyResult.delay > NOT_CONNECTED + } catch { + IPv6Map.value[now] = false + } + } + + return await fetchProxyLatencyAPI(independentLatencyTest.value ? proxyName : now, url, timeout) +} + +const getNameForNotification = (name: string, url: string) => { + if (independentLatencyTest.value) { + return `${name}\n@${url}` + } + + return name +} + +export const proxyLatencyTest = async ( + proxyName: string, + url = speedtestUrlWithDefault.value, + timeout = speedtestTimeout.value, +) => { + const res = await latencyTestForSingle(proxyName, url, timeout) + await fetchProxies() + + if (res.status !== 200) { + showNotification({ + content: 'testFailedTip', + params: { + name: getNameForNotification(proxyName, url), + }, + type: 'alert-error', + }) + } +} + +const setHistory = (proxyName: string, delay: number) => { + const history = getHistoryByName(proxyName) + const now = new Date() + + history.push({ + time: now.toISOString(), + delay, + }) +} + +const TIP_KEY = 'testLatencyOneByOneWithTip' +const limiter = pLimit(5) +const testLatencyOneByOneWithTip = async ( + proxyGroupName: string, + nodes: string[], + url = speedtestUrlWithDefault.value, +) => { + const total = nodes.length + let testDone = 0 + let testFailed = 0 + + await Promise.allSettled( + nodes.map((name) => + limiter(async () => { + const res = await latencyTestForSingle(name, url, Math.min(1500, speedtestTimeout.value)) + + if (res.status !== 200) { + testFailed++ + setHistory(name, NOT_CONNECTED) + } else { + setHistory(name, res.data.delay) + } + testDone++ + showNotification({ + content: 'testFinishedTip', + key: TIP_KEY + proxyGroupName, + params: { + name: getNameForNotification(proxyGroupName, url), + total: total.toString(), + number: testDone.toString(), + }, + type: 'alert-info', + timeout: 0, + }) + }), + ), + ) + showNotification({ + content: 'testFinishedResultTip', + key: TIP_KEY + proxyGroupName, + params: { + name: getNameForNotification(proxyGroupName, url), + total: total.toString(), + success: `${total - testFailed}`, + failed: `${testFailed}`, + }, + type: testFailed ? 'alert-warning' : 'alert-success', + timeout: 3000, + }) + await fetchProxies() +} + +export const proxyGroupLatencyTest = async (proxyGroupName: string) => { + const proxyNode = proxyMap.value[proxyGroupName] + const all = proxyNode.all ?? [] + const url = getTestUrl(proxyGroupName) + + if ( + [PROXY_TYPE.Selector, PROXY_TYPE.LoadBalance, PROXY_TYPE.Smart].includes( + proxyNode.type.toLowerCase() as PROXY_TYPE, + ) + ) { + if (proxyNode.fixed) { + deleteFixedProxyAPI(proxyGroupName) + } + return testLatencyOneByOneWithTip(proxyGroupName, all, url) + } + + const timeout = Math.max(5000, speedtestTimeout.value) + + if (IPv6test.value) { + try { + const { data: ipv6LatencyResult } = await fetchProxyGroupLatencyAPI( + proxyGroupName, + IPV6_TEST_URL, + timeout, + ) + + all?.forEach((name) => { + IPv6Map.value[getNowProxyNodeName(name)] = ipv6LatencyResult[name] > NOT_CONNECTED + }) + } catch { + all?.forEach((name) => { + IPv6Map.value[getNowProxyNodeName(name)] = false + }) + } + } + await fetchProxyGroupLatencyAPI(proxyGroupName, url, timeout) + await fetchProxies() + + const total = all.length + const testFailed = all.filter( + (name) => getLatencyByName(name, proxyGroupName) === NOT_CONNECTED, + ).length + + showNotification({ + content: 'testFinishedResultTip', + key: TIP_KEY + proxyGroupName, + params: { + name: getNameForNotification(proxyGroupName, url), + total: total.toString(), + success: `${total - testFailed}`, + failed: `${testFailed}`, + }, + type: testFailed ? 'alert-warning' : 'alert-success', + timeout: 3000, + }) +} + +export const allProxiesLatencyTest = async () => { + if (independentLatencyTest.value) { + const limit = pLimit(3) + + return await Promise.all( + proxyGroupList.value.map((proxyGroupName) => + limit(async () => { + await proxyGroupLatencyTest(proxyGroupName) + }), + ), + ) + } + + const proxyNode = Object.keys(proxyMap.value).filter((proxy) => !isProxyGroup(proxy)) + + return testLatencyOneByOneWithTip('all', proxyNode) +} + +const getLatencyFromHistory = (history: Proxy['history']) => { + return last(history)?.delay ?? NOT_CONNECTED +} + +const getIPv6FromExtra = (proxy: Proxy) => { + const ipv6History = proxy.extra?.[IPV6_TEST_URL]?.history + + return (last(ipv6History)?.delay ?? NOT_CONNECTED) > NOT_CONNECTED +} + +export const getNowProxyNodeName = (name: string) => { + let node = proxyMap.value[name] + + if (!name || !node) { + return name + } + + while (node.now && node.now !== node.name) { + const nextNode = proxyMap.value[node.now] + + if (!nextNode) { + return node.name + } + + node = nextNode + } + + return node.name +} + +export const getProxyGroupChains = (name: string) => { + let proxyNode = proxyMap.value[name] + + if (!proxyNode) { + return [] + } + + const result = [name] + + while ( + proxyNode.now && + proxyNode.now !== proxyNode.name && + proxyGroupList.value.includes(proxyNode.now) + ) { + result.push(proxyNode.now) + proxyNode = proxyMap.value[proxyNode.now] + } + return result +} + +export const hasSmartGroup = computed(() => { + return Object.values(proxyMap.value).some( + (proxy) => proxy.type.toLowerCase() === PROXY_TYPE.Smart, + ) +}) diff --git a/src/store/rules.ts b/src/store/rules.ts new file mode 100644 index 0000000..2bbf41c --- /dev/null +++ b/src/store/rules.ts @@ -0,0 +1,60 @@ +import { fetchRuleProvidersAPI, fetchRulesAPI } from '@/api' +import { RULE_TAB_TYPE } from '@/constant' +import type { Rule, RuleProvider } from '@/types' +import { computed, ref } from 'vue' + +export const rulesFilter = ref('') +export const rulesTabShow = ref(RULE_TAB_TYPE.RULES) + +export const rules = ref([]) +export const ruleProviderList = ref([]) + +export const renderRules = computed(() => { + const rulesFilterValue = rulesFilter.value.split(' ').map((f) => f.toLowerCase().trim()) + + if (rulesFilter.value === '') { + return rules.value + } + + return rules.value.filter((rule) => { + return rulesFilterValue.every((f) => + [rule.type.toLowerCase(), rule.payload.toLowerCase(), rule.proxy.toLowerCase()].some((i) => + i.includes(f), + ), + ) + }) +}) + +export const renderRulesProvider = computed(() => { + const rulesFilterValue = rulesFilter.value.split(' ').map((f) => f.toLowerCase().trim()) + + if (rulesFilter.value === '') { + return ruleProviderList.value + } + + return ruleProviderList.value.filter((ruleProvider) => { + return rulesFilterValue.every((f) => + [ + ruleProvider.name.toLowerCase(), + ruleProvider.behavior.toLowerCase(), + ruleProvider.vehicleType.toLowerCase(), + ].some((i) => i.includes(f)), + ) + }) +}) + +export const fetchRules = async () => { + const { data: ruleData } = await fetchRulesAPI() + const { data: providerData } = await fetchRuleProvidersAPI() + + rules.value = ruleData.rules.map((rule) => { + const proxy = rule.proxy + const proxyName = proxy.startsWith('route(') ? proxy.substring(6, proxy.length - 1) : proxy + + return { + ...rule, + proxy: proxyName, + } + }) + ruleProviderList.value = Object.values(providerData.providers) +} diff --git a/src/store/settings.ts b/src/store/settings.ts new file mode 100644 index 0000000..28d5537 --- /dev/null +++ b/src/store/settings.ts @@ -0,0 +1,253 @@ +import { + CONNECTIONS_TABLE_ACCESSOR_KEY, + DETAILED_CARD_STYLE, + EMOJIS, + FONTS, + GLOBAL, + IP_INFO_API, + IS_APPLE_DEVICE, + LANG, + OVERVIEW_CARD, + PROXY_CARD_SIZE, + PROXY_CHAIN_DIRECTION, + PROXY_PREVIEW_TYPE, + PROXY_SORT_TYPE, + SETTINGS_MENU_KEY, + TABLE_SIZE, + TABLE_WIDTH_MODE, + TEST_URL, + type THEME, +} from '@/constant' +import { getMinCardWidth, isMiddleScreen, isPreferredDark } from '@/helper/utils' +import type { SourceIPLabel } from '@/types' +import { useStorage } from '@vueuse/core' +import { computed } from 'vue' + +// global +export const defaultTheme = useStorage('config/default-theme', 'light') +export const darkTheme = useStorage('config/dark-theme', 'dark') +export const autoTheme = useStorage('config/auto-theme', true) +export const theme = computed(() => { + if (autoTheme.value && isPreferredDark.value) { + return darkTheme.value + } + return defaultTheme.value +}) + +export const customThemes = useStorage('config/custom-themes', []) + +export const language = useStorage( + 'config/language', + Object.values(LANG).includes(navigator.language as LANG) + ? (navigator.language as LANG) + : LANG.EN_US, +) +export const isSidebarCollapsedConfig = useStorage('config/is-sidebar-collapsed', true) +export const isSidebarCollapsed = computed({ + get: () => { + if (isMiddleScreen.value) { + return true + } + + return isSidebarCollapsedConfig.value + }, + set: (value) => { + isSidebarCollapsedConfig.value = value + }, +}) +const fontConfig = useStorage('config/font', FONTS.MI_SANS) +export const font = computed({ + get: () => { + const mode = import.meta.env.MODE + if (Object.values(FONTS).includes(mode as FONTS)) { + return mode as FONTS + } + return fontConfig.value + }, + set: (val) => { + fontConfig.value = val + }, +}) +export const emoji = useStorage( + 'config/emoji', + IS_APPLE_DEVICE ? EMOJIS.TWEMOJI : EMOJIS.NOTO_COLOR_EMOJI, +) +export const customBackgroundURL = useStorage('config/custom-background-image', '') +export const dashboardTransparent = useStorage('config/dashboard-transparent', 90) +export const autoUpgrade = useStorage('config/auto-upgrade', false) +export const checkUpgradeCore = useStorage('config/check-upgrade-core', true) +export const autoUpgradeCore = useStorage('config/auto-upgrade-core', false) +export const swipeInPages = useStorage('config/swipe-in-pages', true) +export const swipeInTabs = useStorage('config/swipe-in-tabs', false) +export const disablePullToRefresh = useStorage('config/disable-pull-to-refresh', true) +export const displayAllFeatures = useStorage('config/display-all-features', false) +export const blurIntensity = useStorage('config/blur-intensity', 10) +export const scrollAnimationEffect = useStorage('config/scroll-animation-effect', true) +export const IPInfoAPI = useStorage('config/geoip-info-api', IP_INFO_API.IPSB) +export const autoDisconnectIdleUDP = useStorage('config/auto-disconnect-idle-udp', false) +export const autoDisconnectIdleUDPTime = useStorage('config/auto-disconnect-idle-udp-time', 300) + +// overview +export const splitOverviewPage = useStorage('config/split-overview-page', false) +export const autoIPCheck = useStorage('config/auto-ip-check', true) +export const autoConnectionCheck = useStorage('config/auto-connection-check', true) +export const showStatisticsWhenSidebarCollapsed = useStorage( + 'config/show-statistics-when-sidebar-collapsed', + true, +) +export const numberOfChartsInSidebar = useStorage<1 | 2 | 3>( + 'config/number-of-charts-in-sidebar', + 2, +) +const defaultOverviewCardOrder: { card: OVERVIEW_CARD; visible: boolean }[] = [ + { + card: OVERVIEW_CARD.ChartsCard, + visible: true, + }, + { + card: OVERVIEW_CARD.NetworkCard, + visible: true, + }, + { + card: OVERVIEW_CARD.ProviderTrafficOverview, + visible: true, + }, + { + card: OVERVIEW_CARD.TopologyCharts, + visible: true, + }, + { + card: OVERVIEW_CARD.ConnectionHistory, + visible: true, + }, + { + card: OVERVIEW_CARD.RuleHitCountCard, + visible: true, + }, +] + +export const overviewCardOrder = useStorage<{ card: OVERVIEW_CARD; visible: boolean }[]>( + 'config/overview-card-order', + defaultOverviewCardOrder, +) + +// 确保所有卡片都在配置中,缺失的卡片添加到末尾 +const allCardTypes = Object.values(OVERVIEW_CARD) +const existingCardTypes = new Set(overviewCardOrder.value.map((item) => item.card)) +const missingCards = allCardTypes.filter((card) => !existingCardTypes.has(card)) + +if (missingCards.length > 0) { + const newCards = missingCards.map((card) => ({ + card, + visible: true, + })) + overviewCardOrder.value = [...overviewCardOrder.value, ...newCards] +} + +// proxies +export const collapseGroupMap = useStorage>('config/collapse-group-map', {}) +export const displayFinalOutbound = useStorage('config/show-seleted-for-now-node', false) +export const twoColumnProxyGroup = useStorage('config/two-columns', true) +export const speedtestUrl = useStorage('config/speedtest-url', TEST_URL) +export const independentLatencyTest = useStorage('config/independent-latency-test', false) +export const speedtestTimeout = useStorage('config/speedtest-timeout', 5000) +export const proxySortType = useStorage( + 'config/proxy-sort-type', + PROXY_SORT_TYPE.DEFAULT, +) +export const automaticDisconnection = useStorage('config/automatic-disconnection', true) +export const truncateProxyName = useStorage('config/truncate-proxy-name', true) +export const proxyPreviewType = useStorage('config/proxy-preview-type', PROXY_PREVIEW_TYPE.AUTO) +export const hideUnavailableProxies = useStorage('config/hide-unavailable-proxies', false) +export const lowLatency = useStorage('config/low-latency', 400) +export const mediumLatency = useStorage('config/medium-latency', 800) +export const IPv6test = useStorage('config/ipv6-test', false) +export const proxyCardSize = useStorage( + 'config/proxy-card-size', + PROXY_CARD_SIZE.LARGE, +) +export const minProxyCardWidth = useStorage( + 'config/min-proxy-card-width', + getMinCardWidth(proxyCardSize.value), +) +export const manageHiddenGroup = useStorage('config/manage-hidden-group-mode', false) + +export const displayGlobalByMode = useStorage('config/display-global-by-mode', false) +export const customGlobalNode = useStorage('config/custom-global-node-name', GLOBAL) + +export const proxyGroupIconSize = useStorage('config/proxy-group-icon-size', 24) +export const proxyGroupIconMargin = useStorage('config/proxy-group-icon-margin', 6) +export const iconReflectList = useStorage< + { + icon: string + name: string + uuid: string + }[] +>('config/icon-reflect-list', []) +export const groupProxiesByProvider = useStorage('config/group-proxies-by-provider', false) +export const useSmartGroupSort = useStorage('config/use-smart-group-sort', false) +export const groupTestUrls = useStorage< + { + name: string + url: string + uuid: string + }[] +>('config/group-test-urls', []) + +// connections +export const useConnectionCard = useStorage('config/use-connecticon-card', window.innerWidth < 640) +export const proxyChainDirection = useStorage( + 'config/proxy-chain-direction', + PROXY_CHAIN_DIRECTION.NORMAL, +) +export const showFullProxyChain = useStorage('config/show-full-proxy-chain', true) +export const tableSize = useStorage('config/connecticon-table-size', TABLE_SIZE.SMALL) +export const tableWidthMode = useStorage('config/table-width-mode', TABLE_WIDTH_MODE.AUTO) +export const connectionTableColumns = useStorage( + 'config/connection-table-columns', + [ + CONNECTIONS_TABLE_ACCESSOR_KEY.Close, + CONNECTIONS_TABLE_ACCESSOR_KEY.Host, + CONNECTIONS_TABLE_ACCESSOR_KEY.Type, + CONNECTIONS_TABLE_ACCESSOR_KEY.Rule, + CONNECTIONS_TABLE_ACCESSOR_KEY.Chains, + CONNECTIONS_TABLE_ACCESSOR_KEY.DlSpeed, + CONNECTIONS_TABLE_ACCESSOR_KEY.UlSpeed, + CONNECTIONS_TABLE_ACCESSOR_KEY.Download, + CONNECTIONS_TABLE_ACCESSOR_KEY.Upload, + CONNECTIONS_TABLE_ACCESSOR_KEY.ConnectTime, + ], +) +export const connectionCardLines = useStorage( + 'config/connection-card-lines', + DETAILED_CARD_STYLE, +) + +export const sourceIPLabelList = useStorage('config/source-ip-label-list', []) + +// rules +export const displayNowNodeInRule = useStorage('config/display-now-node-in-rule', true) +export const displayLatencyInRule = useStorage('config/display-latency-in-rule', true) +export const disconnectOnRuleDisable = useStorage('config/disconnect-on-rule-disable', true) + +// logs +export const logRetentionLimit = useStorage('config/log-retention-limit', 1000) +export const logSearchHistory = useStorage('config/log-search-history', []) + +// settings visibility +// 使用扁平结构,key 格式为 "大设置项.小设置项" 或 "大设置项"(仅大设置项) +// 默认所有项都可见,只有隐藏的项才会记录在此对象中 +export const hiddenSettingsItems = useStorage>( + 'config/hidden-settings-items', + {}, +) + +// settings menu order +// 存储设置菜单项的顺序 +export const settingsMenuOrder = useStorage('config/settings-menu-order', [ + SETTINGS_MENU_KEY.general, + SETTINGS_MENU_KEY.overview, + SETTINGS_MENU_KEY.backend, + SETTINGS_MENU_KEY.proxies, + SETTINGS_MENU_KEY.connections, +]) diff --git a/src/store/setup.ts b/src/store/setup.ts new file mode 100644 index 0000000..f9ba9f0 --- /dev/null +++ b/src/store/setup.ts @@ -0,0 +1,53 @@ +import type { Backend } from '@/types' +import { useStorage } from '@vueuse/core' +import { isEqual, omit } from 'lodash' +import { v4 as uuid } from 'uuid' +import { computed } from 'vue' +import { sourceIPLabelList } from './settings' + +export const backendList = useStorage('setup/api-list', []) +export const activeUuid = useStorage('setup/active-uuid', '') +export const activeBackend = computed(() => + backendList.value.find((backend) => backend.uuid === activeUuid.value), +) + +export const addBackend = (backend: Omit) => { + const currentEnd = backendList.value.find((end) => { + return isEqual(omit(end, 'uuid'), backend) + }) + + if (currentEnd) { + activeUuid.value = currentEnd.uuid + return + } + + const id = uuid() + + backendList.value.push({ + ...backend, + uuid: id, + }) + activeUuid.value = id +} + +export const updateBackend = (uuid: string, backend: Omit) => { + const index = backendList.value.findIndex((end) => end.uuid === uuid) + if (index !== -1) { + backendList.value[index] = { + ...backend, + uuid, + } + } +} + +export const removeBackend = (uuid: string) => { + backendList.value = backendList.value.filter((end) => end.uuid !== uuid) + sourceIPLabelList.value.forEach((label) => { + if (label.scope && label.scope.includes(uuid)) { + label.scope = label.scope.filter((scope) => scope !== uuid) + if (!label.scope.length) { + delete label.scope + } + } + }) +} diff --git a/src/store/smart.ts b/src/store/smart.ts new file mode 100644 index 0000000..df56eda --- /dev/null +++ b/src/store/smart.ts @@ -0,0 +1,49 @@ +import { fetchSmartGroupWeightsAPI, fetchSmartWeightsAPI } from '@/api' +import type { NodeRank } from '@/types' +import { ref } from 'vue' + +export const smartWeightsMap = ref>>({}) +export const smartOrderMap = ref>>({}) + +const restructWeights = (proxyName: string, weights: NodeRank[]) => { + const smartWeights: Record = {} + const smartOrder: Record = {} + + weights.forEach((weight, index) => { + smartWeights[weight.Name] = weight.Rank + smartOrder[weight.Name] = index + }) + + smartWeightsMap.value[proxyName] = smartWeights + smartOrderMap.value[proxyName] = smartOrder +} + +// deprecated +const fetchSmartGroupWeights = async (proxyName: string) => { + const { data } = await fetchSmartGroupWeightsAPI(proxyName) + + if (!data.weights?.length) return + + restructWeights(proxyName, data.weights) +} + +export const initSmartWeights = async (smartGroups: string[]) => { + const { status, data: smartWeights } = await fetchSmartWeightsAPI() + + smartWeightsMap.value = {} + smartOrderMap.value = {} + + if (status !== 200) { + // deprecated fallback + smartGroups.forEach((name) => { + fetchSmartGroupWeights(name) + }) + return + } + + for (const [group, weights] of Object.entries(smartWeights.weights)) { + if (!weights?.length) continue + + restructWeights(group, weights) + } +} diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..3916b17 --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,16 @@ +declare const __APP_VERSION__: string +declare const __COMMIT_ID__: string + +declare module 'vue-virtual-scroller' +declare interface Navigator { + standalone?: boolean +} + +type ToolTipParams = { + data: { + value: number + name: number + } + seriesName: string + color: string +} diff --git a/src/types/index.d.ts b/src/types/index.d.ts new file mode 100644 index 0000000..6376f97 --- /dev/null +++ b/src/types/index.d.ts @@ -0,0 +1,184 @@ +export type Backend = { + host: string + port: string + secondaryPath: string + password: string + protocol: string + uuid: string + label?: string + disableUpgradeCore?: boolean +} + +export type Config = { + port: number + 'socks-port': number + 'redir-port': number + 'tproxy-port': number + 'mixed-port': number + 'allow-lan': boolean + 'bind-address': string + mode: string + 'mode-list': string[] + modes: string[] + 'log-level': string + ipv6: boolean + tun: { + enable: boolean + } +} + +export type History = { + time: string + delay: number +}[] + +export type Proxy = { + name: string + type: string + history: History + extra: Record< + string, + { + alive: boolean + history: History + } + > + all?: string[] + udp: boolean + xudp?: boolean + now: string + fixed?: string + icon: string + hidden?: boolean + testUrl?: string + 'dialer-proxy'?: string + 'provider-name'?: string +} + +export type SubscriptionInfo = { + Download?: number + Upload?: number + Total?: number + Expire?: number +} + +export type ProxyProvider = { + subscriptionInfo?: SubscriptionInfo + name: string + proxies: Proxy[] + testUrl: string + updatedAt: string + vehicleType: string +} + +export type Rule = { + type: string + payload: string + proxy: string + size: number + uuid: string + // sing-box-reFind + disabled?: boolean + // mihomo + index: number + extra?: { + disabled: false + hitAt: string + hitCount: number + missAt: string + missCount: number + } +} + +export type RuleProvider = { + behavior: string + format: string + name: string + ruleCount: number + type: string + updatedAt: string + vehicleType: string +} + +export type ConnectionRawMessage = { + id: string + download: number + upload: number + chains: string[] + rule: string + rulePayload: string + start: string + metadata: { + destinationGeoIP: string + destinationIP: string + destinationIPASN: string + destinationPort: string + dnsMode: string + dscp: number + host: string + inboundIP: string + inboundName: string + inboundPort: string + inboundUser: string + network: string + process: string + processPath: string + remoteDestination: string + sniffHost: string + sourceGeoIP: string + sourceIP: string + sourceIPASN: string + sourcePort: string + specialProxy: string + specialRules: string + type: string + uid: number + smartBlock: string + } +} + +export type Connection = ConnectionRawMessage & { + downloadSpeed: number + uploadSpeed: number +} + +export type Log = { + type: 'info' | 'warning' | 'error' | 'debug' + payload: string +} + +export type LogWithSeq = Log & { seq: number; time: string } + +export type DNSQuery = { + AD: boolean + CD: boolean + RA: boolean + RD: boolean + TC: boolean + status: number + Question: { + Name: string + Qtype: number + Qclass: number + }[] + Answer?: { + TTL: number + data: string + name: string + type: number + }[] +} + +export type SourceIPLabel = { + key: string + label: string + id: string + scope?: string[] +} + +// smart core +export interface NodeRank { + Name: string + Rank: string + Weight: number +} diff --git a/src/views/ConnectionsPage.vue b/src/views/ConnectionsPage.vue new file mode 100644 index 0000000..ede9609 --- /dev/null +++ b/src/views/ConnectionsPage.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue new file mode 100644 index 0000000..23e3470 --- /dev/null +++ b/src/views/HomePage.vue @@ -0,0 +1,205 @@ + + + diff --git a/src/views/LogsPage.vue b/src/views/LogsPage.vue new file mode 100644 index 0000000..6330ec4 --- /dev/null +++ b/src/views/LogsPage.vue @@ -0,0 +1,57 @@ + + + diff --git a/src/views/OverviewPage.vue b/src/views/OverviewPage.vue new file mode 100644 index 0000000..881d533 --- /dev/null +++ b/src/views/OverviewPage.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/views/ProxiesPage.vue b/src/views/ProxiesPage.vue new file mode 100644 index 0000000..48f58d8 --- /dev/null +++ b/src/views/ProxiesPage.vue @@ -0,0 +1,124 @@ + + + diff --git a/src/views/RulesPage.vue b/src/views/RulesPage.vue new file mode 100644 index 0000000..69f5a01 --- /dev/null +++ b/src/views/RulesPage.vue @@ -0,0 +1,66 @@ + + + diff --git a/src/views/SettingsPage.vue b/src/views/SettingsPage.vue new file mode 100644 index 0000000..740c974 --- /dev/null +++ b/src/views/SettingsPage.vue @@ -0,0 +1,215 @@ + + + diff --git a/src/views/SetupPage.vue b/src/views/SetupPage.vue new file mode 100644 index 0000000..7d33e94 --- /dev/null +++ b/src/views/SetupPage.vue @@ -0,0 +1,253 @@ + + + diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..98117ff --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,27 @@ +import plugin from 'tailwindcss/plugin' + +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], + theme: { + extend: { + colors: { + ['low-latency']: 'oklch(0.648 0.15 160)', + ['medium-latency']: 'rgb(250, 210, 75)', + ['high-latency']: 'rgb(244, 96, 108)', + }, + }, + }, + plugins: [ + plugin(({ addUtilities }) => { + addUtilities({ + '.scrollbar-hidden': { + 'scrollbar-width': 'none!important', + }, + '.scrollbar-thin': { + 'scrollbar-width': 'thin', + }, + }) + }), + ], +} diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..e31c618 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "noUncheckedIndexedAccess": false, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..66b5e57 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..5a0c6a5 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/node22/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*" + ], + "compilerOptions": { + "composite": true, + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..1846f17 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,76 @@ +import vue from '@vitejs/plugin-vue' +import vueJsx from '@vitejs/plugin-vue-jsx' +import { execSync } from 'child_process' +import { fileURLToPath, URL } from 'node:url' +import { defineConfig } from 'vite' +import { VitePWA } from 'vite-plugin-pwa' +import { version } from './package.json' + +const getGitCommitId = (): string => { + try { + const commitMessage = execSync('git log -1 --pretty=%B', { encoding: 'utf8' }).trim() + + if (commitMessage.includes('chore(main): release')) { + return '' + } + + return execSync('git rev-parse --short HEAD', { encoding: 'utf8' }).trim() + } catch (error) { + console.warn('无法获取git commit ID:', error) + return '' + } +} + +// https://vite.dev/config/ +export default defineConfig({ + define: { + __APP_VERSION__: JSON.stringify(version), + __COMMIT_ID__: JSON.stringify(getGitCommitId()), + }, + base: './', + plugins: [ + vue(), + vueJsx(), + VitePWA({ + registerType: 'autoUpdate', + includeAssets: ['favicon.svg', 'favicon-dark.svg'], + manifest: { + name: 'zashboard', + short_name: 'zashboard', + description: 'a dashboard using clash api', + theme_color: '#000000', + icons: [ + { + src: './pwa-192x192.png', + sizes: '192x192', + type: 'image/png', + purpose: 'any', + }, + { + src: './pwa-512x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'any', + }, + { + src: './pwa-maskable-192x192.png', + sizes: '192x192', + type: 'image/png', + purpose: 'maskable', + }, + { + src: './pwa-maskable-512x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'maskable', + }, + ], + }, + }), + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, +})