From 1d213eae57df2da813381d36a426a637a27138fd Mon Sep 17 00:00:00 2001 From: HoshinoSuzumi Date: Tue, 16 Jul 2024 21:14:07 +0800 Subject: [PATCH] feat: base frame --- components.d.ts | 1 + package.json | 5 + pnpm-lock.yaml | 1023 ++++++++++++++++- postcss.config.js | 6 + src-tauri/Cargo.lock | 11 + src-tauri/Cargo.toml | 2 +- src-tauri/src/main.rs | 265 +++-- src-tauri/tauri.conf.json | 4 +- src/App.vue | 27 +- src/assets/css/font.css | 109 ++ src/assets/css/style.css | 20 + ...-sc-v36-chinese-simplified_latin-200.woff2 | Bin 0 -> 1085220 bytes ...-sc-v36-chinese-simplified_latin-600.woff2 | Bin 0 -> 1153980 bytes ...-sc-v36-chinese-simplified_latin-800.woff2 | Bin 0 -> 1164324 bytes ...v36-chinese-simplified_latin-regular.woff2 | Bin 0 -> 1133092 bytes src/assets/fonts/rubik-v28-latin-300.woff2 | Bin 0 -> 17556 bytes .../fonts/rubik-v28-latin-300italic.woff2 | Bin 0 -> 18048 bytes src/assets/fonts/rubik-v28-latin-600.woff2 | Bin 0 -> 19096 bytes .../fonts/rubik-v28-latin-600italic.woff2 | Bin 0 -> 19460 bytes src/assets/fonts/rubik-v28-latin-italic.woff2 | Bin 0 -> 19196 bytes .../fonts/rubik-v28-latin-regular.woff2 | Bin 0 -> 18856 bytes src/components/DrawerContainer.vue | 51 +- src/components/Greet.vue | 2 +- src/components/icons/TablerDeviceWatch.vue | 10 + src/main.ts | 9 +- src/pages/charts.vue | 13 + src/pages/index.vue | 2 +- src/pages/settings.vue | 12 + src/pages/streaming-plugins.vue | 13 + src/pages/widgets.vue | 13 + tailwind.config.js | 8 + tsconfig.json | 2 +- vite.config.ts | 8 +- 33 files changed, 1493 insertions(+), 123 deletions(-) create mode 100644 postcss.config.js create mode 100644 src/assets/css/font.css create mode 100644 src/assets/css/style.css create mode 100644 src/assets/fonts/noto-sans-sc-v36-chinese-simplified_latin-200.woff2 create mode 100644 src/assets/fonts/noto-sans-sc-v36-chinese-simplified_latin-600.woff2 create mode 100644 src/assets/fonts/noto-sans-sc-v36-chinese-simplified_latin-800.woff2 create mode 100644 src/assets/fonts/noto-sans-sc-v36-chinese-simplified_latin-regular.woff2 create mode 100644 src/assets/fonts/rubik-v28-latin-300.woff2 create mode 100644 src/assets/fonts/rubik-v28-latin-300italic.woff2 create mode 100644 src/assets/fonts/rubik-v28-latin-600.woff2 create mode 100644 src/assets/fonts/rubik-v28-latin-600italic.woff2 create mode 100644 src/assets/fonts/rubik-v28-latin-italic.woff2 create mode 100644 src/assets/fonts/rubik-v28-latin-regular.woff2 create mode 100644 src/components/icons/TablerDeviceWatch.vue create mode 100644 src/pages/charts.vue create mode 100644 src/pages/settings.vue create mode 100644 src/pages/streaming-plugins.vue create mode 100644 src/pages/widgets.vue create mode 100644 tailwind.config.js diff --git a/components.d.ts b/components.d.ts index 2c4a672..bb69fa8 100644 --- a/components.d.ts +++ b/components.d.ts @@ -11,5 +11,6 @@ declare module 'vue' { Greet: typeof import('./src/components/Greet.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + TablerDeviceWatch: typeof import('./src/components/icons/TablerDeviceWatch.vue')['default'] } } diff --git a/package.json b/package.json index b53fbea..2197999 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,12 @@ "devDependencies": { "@tauri-apps/cli": "^1", "@vitejs/plugin-vue": "^5.0.5", + "autoprefixer": "^10.4.19", + "postcss": "^8.4.39", + "sass": "^1.77.8", + "tailwindcss": "^3.4.4", "typescript": "^5.2.2", + "unplugin-vue-components": "^0.27.2", "vite": "^5.3.1", "vue-tsc": "^2.0.22" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 227ae8c..c155df6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,19 +23,41 @@ importers: version: 1.6.0 '@vitejs/plugin-vue': specifier: ^5.0.5 - version: 5.0.5(vite@5.3.3)(vue@3.4.31(typescript@5.5.3)) + version: 5.0.5(vite@5.3.3(sass@1.77.8))(vue@3.4.31(typescript@5.5.3)) + autoprefixer: + specifier: ^10.4.19 + version: 10.4.19(postcss@8.4.39) + postcss: + specifier: ^8.4.39 + version: 8.4.39 + sass: + specifier: ^1.77.8 + version: 1.77.8 + tailwindcss: + specifier: ^3.4.4 + version: 3.4.4 typescript: specifier: ^5.2.2 version: 5.5.3 + unplugin-vue-components: + specifier: ^0.27.2 + version: 0.27.2(@babel/parser@7.24.8)(rollup@4.18.1)(vue@3.4.31(typescript@5.5.3)) vite: specifier: ^5.3.1 - version: 5.3.3 + version: 5.3.3(sass@1.77.8) vue-tsc: specifier: ^2.0.22 version: 2.0.26(typescript@5.5.3) packages: + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@antfu/utils@0.7.10': + resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} + '@babel/helper-string-parser@7.24.8': resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} engines: {node: '>=6.9.0'} @@ -191,9 +213,53 @@ packages: cpu: [x64] os: [win32] + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@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'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@rollup/pluginutils@5.1.0': + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + 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.18.1': resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} cpu: [arm] @@ -402,21 +468,133 @@ packages: '@vue/shared@3.4.31': resolution: {integrity: sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==} + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + autoprefixer@10.4.19: + resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.23.2: + resolution: {integrity: sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + caniuse-lite@1.0.30001642: + resolution: {integrity: sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + 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==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + computeds@0.0.1: resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.4.827: + resolution: {integrity: sha512-VY+J0e4SFcNfQy19MEoMdaIcZLmDCprqvBtkii1WTCTQHpRvf5N8+3kTYCgL/PcntvwQvmMJWTuDPsq+IlhWKQ==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -426,66 +604,382 @@ packages: engines: {node: '>=12'} hasBin: true + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + 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==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + immutable@4.3.6: + resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + 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==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.0.1: + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.1: + resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.4.39: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rollup@4.18.1: resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + sass@1.77.8: + resolution: {integrity: sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==} + engines: {node: '>=14.0.0'} + hasBin: true + semver@7.6.2: resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} hasBin: true + 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'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwindcss@3.4.4: + resolution: {integrity: sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==} + engines: {node: '>=14.0.0'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + typescript@5.5.3: resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} engines: {node: '>=14.17'} hasBin: true + ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + + unplugin-vue-components@0.27.2: + resolution: {integrity: sha512-YifnsmslMRNt+JRQiCG4ZX1+xUQuubUZm76K7Qtg8dmchZJkHIDxZSyfZb5/jqrLWMTm/TUjGJ3ZDlzO6SFnSQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/parser': ^7.15.8 + '@nuxt/kit': ^3.2.2 + vue: 2 || 3 + peerDependenciesMeta: + '@babel/parser': + optional: true + '@nuxt/kit': + optional: true + + unplugin@1.11.0: + resolution: {integrity: sha512-3r7VWZ/webh0SGgJScpWl2/MRCZK5d3ZYFcNaeci/GQ7Teop7zf0Nl2pUuz7G21BwPd9pcUPOC5KmJ2L3WgC5g==} + engines: {node: '>=14.0.0'} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vite@5.3.3: resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} engines: {node: ^18.0.0 || >=20.0.0} @@ -539,8 +1033,37 @@ packages: typescript: optional: true + webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + snapshots: + '@alloc/quick-lru@5.2.0': {} + + '@antfu/utils@0.7.10': {} + '@babel/helper-string-parser@7.24.8': {} '@babel/helper-validator-identifier@7.24.7': {} @@ -624,8 +1147,55 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@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.17.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@rollup/pluginutils@5.1.0(rollup@4.18.1)': + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 4.18.1 + '@rollup/rollup-android-arm-eabi@4.18.1': optional: true @@ -721,9 +1291,9 @@ snapshots: '@types/estree@1.0.5': {} - '@vitejs/plugin-vue@5.0.5(vite@5.3.3)(vue@3.4.31(typescript@5.5.3))': + '@vitejs/plugin-vue@5.0.5(vite@5.3.3(sass@1.77.8))(vue@3.4.31(typescript@5.5.3))': dependencies: - vite: 5.3.3 + vite: 5.3.3(sass@1.77.8) vue: 3.4.31(typescript@5.5.3) '@volar/language-core@2.4.0-alpha.15': @@ -807,18 +1377,112 @@ snapshots: '@vue/shared@3.4.31': {} + acorn@8.12.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + autoprefixer@10.4.19(postcss@8.4.39): + dependencies: + browserslist: 4.23.2 + caniuse-lite: 1.0.30001642 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.1 + postcss: 8.4.39 + postcss-value-parser: 4.2.0 + balanced-match@1.0.2: {} + binary-extensions@2.3.0: {} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.23.2: + dependencies: + caniuse-lite: 1.0.30001642 + electron-to-chromium: 1.4.827 + node-releases: 2.0.14 + update-browserslist-db: 1.1.0(browserslist@4.23.2) + + camelcase-css@2.0.1: {} + + caniuse-lite@1.0.30001642: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@4.1.1: {} + computeds@0.0.1: {} + confbox@0.1.7: {} + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + csstype@3.1.3: {} de-indent@1.0.2: {} + debug@4.3.5: + dependencies: + ms: 2.1.2 + + didyoumean@1.2.2: {} + + dlv@1.1.3: {} + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.4.827: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + entities@4.5.0: {} esbuild@0.21.5: @@ -847,35 +1511,234 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + escalade@3.1.2: {} + estree-walker@2.0.2: {} + fast-glob@3.3.2: + 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.7 + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + foreground-child@3.2.1: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + fraction.js@4.3.7: {} + fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.2.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + he@1.2.0: {} + immutable@4.3.6: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.14.0: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.6: {} + + lilconfig@2.1.0: {} + + lilconfig@3.1.2: {} + + lines-and-columns@1.2.4: {} + + local-pkg@0.5.0: + dependencies: + mlly: 1.7.1 + pkg-types: 1.1.3 + + lru-cache@10.4.3: {} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + merge2@1.4.1: {} + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 + minipass@7.1.2: {} + + mlly@1.7.1: + dependencies: + acorn: 8.12.1 + pathe: 1.1.2 + pkg-types: 1.1.3 + ufo: 1.5.3 + + ms@2.1.2: {} + muggle-string@0.4.1: {} + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.7: {} + node-releases@2.0.14: {} + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + package-json-from-dist@1.0.0: {} + path-browserify@1.0.1: {} + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + pathe@1.1.2: {} + picocolors@1.0.1: {} + picomatch@2.3.1: {} + + pify@2.3.0: {} + + pirates@4.0.6: {} + + pkg-types@1.1.3: + dependencies: + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + + postcss-import@15.1.0(postcss@8.4.39): + dependencies: + postcss: 8.4.39 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.4.39): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.39 + + postcss-load-config@4.0.2(postcss@8.4.39): + dependencies: + lilconfig: 3.1.2 + yaml: 2.4.5 + optionalDependencies: + postcss: 8.4.39 + + postcss-nested@6.0.1(postcss@8.4.39): + dependencies: + postcss: 8.4.39 + postcss-selector-parser: 6.1.1 + + postcss-selector-parser@6.1.1: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 + queue-microtask@1.2.3: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + resolve@1.22.8: + dependencies: + is-core-module: 2.14.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.0.4: {} + rollup@4.18.1: dependencies: '@types/estree': 1.0.5 @@ -898,21 +1761,149 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + sass@1.77.8: + dependencies: + chokidar: 3.6.0 + immutable: 4.3.6 + source-map-js: 1.2.0 + semver@7.6.2: {} + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + source-map-js@1.2.0: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwindcss@3.4.4: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.7 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.1 + postcss: 8.4.39 + postcss-import: 15.1.0(postcss@8.4.39) + postcss-js: 4.0.1(postcss@8.4.39) + postcss-load-config: 4.0.2(postcss@8.4.39) + postcss-nested: 6.0.1(postcss@8.4.39) + postcss-selector-parser: 6.1.1 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + to-fast-properties@2.0.0: {} + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-interface-checker@0.1.13: {} + typescript@5.5.3: {} - vite@5.3.3: + ufo@1.5.3: {} + + unplugin-vue-components@0.27.2(@babel/parser@7.24.8)(rollup@4.18.1)(vue@3.4.31(typescript@5.5.3)): + dependencies: + '@antfu/utils': 0.7.10 + '@rollup/pluginutils': 5.1.0(rollup@4.18.1) + chokidar: 3.6.0 + debug: 4.3.5 + fast-glob: 3.3.2 + local-pkg: 0.5.0 + magic-string: 0.30.10 + minimatch: 9.0.5 + mlly: 1.7.1 + unplugin: 1.11.0 + vue: 3.4.31(typescript@5.5.3) + optionalDependencies: + '@babel/parser': 7.24.8 + transitivePeerDependencies: + - rollup + - supports-color + + unplugin@1.11.0: + dependencies: + acorn: 8.12.1 + chokidar: 3.6.0 + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.6.2 + + update-browserslist-db@1.1.0(browserslist@4.23.2): + dependencies: + browserslist: 4.23.2 + escalade: 3.1.2 + picocolors: 1.0.1 + + util-deprecate@1.0.2: {} + + vite@5.3.3(sass@1.77.8): dependencies: esbuild: 0.21.5 postcss: 8.4.39 rollup: 4.18.1 optionalDependencies: fsevents: 2.3.3 + sass: 1.77.8 vscode-uri@3.0.8: {} @@ -942,3 +1933,25 @@ snapshots: '@vue/shared': 3.4.31 optionalDependencies: typescript: 5.5.3 + + webpack-sources@3.2.3: {} + + webpack-virtual-modules@0.6.2: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + yaml@2.4.5: {} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 8b3e18e..86e638d 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2670,6 +2670,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -3216,7 +3225,9 @@ dependencies = [ "libc", "mio", "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.48.0", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 0260db0..431743c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -15,7 +15,7 @@ tauri = { version = "1", features = [ "window-show", "window-hide", "window-maxi serde = { version = "1", features = ["derive"] } serde_json = "1" btleplug = { version = "0.11.5", features = ["serde"] } -tokio = { version = "1.38.0", features = ["rt", "rt-multi-thread", "macros"] } +tokio = { version = "1.38.0", features = ["full"] } futures = "0.3.30" uuid = "1.10.0" lazy_static = "1.5.0" diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ebef56a..e1e5f02 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,132 +1,219 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use tauri::{AppHandle, Manager, State}; -use tokio; -use tokio::sync::{Mutex}; -use btleplug::api::{Central, ScanFilter, Manager as _, Peripheral as _}; use btleplug::api::bleuuid::uuid_from_u16; +use btleplug::api::CentralEvent::{ + DeviceConnected, DeviceDisconnected, DeviceDiscovered, DeviceUpdated, + ManufacturerDataAdvertisement, ServiceDataAdvertisement, ServicesAdvertisement, +}; +use btleplug::api::{Central, Manager as _, Peripheral as _, ScanFilter}; use btleplug::platform::{Adapter, Manager as BtleManager, Peripheral}; use futures::StreamExt; +use tauri::{AppHandle, Manager, State}; +use tokio; +use tokio::sync::Mutex; #[derive(serde::Serialize, Clone)] struct BleDevice { - name: String, - address: String, + name: String, + address: String, } -struct BleConnection { - manager: Mutex>, - adapter: Mutex>, - peripheral: Mutex>, +struct BleConnection<'a> { + central: Mutex>, + peripheral: &'a Mutex>, + scan_devices: Mutex>, } #[tauri::command] -async fn scan_devices( - connection: State<'_, BleConnection> -) -> Result { - let adapter = connection.adapter.lock().await; - let adapter = adapter.as_ref().unwrap(); - adapter.start_scan(ScanFilter::default()).await.unwrap(); - tokio::time::sleep(std::time::Duration::from_secs(2)).await; +async fn request_central_events<'a>( + app_handle: AppHandle, + connection: State<'a, BleConnection<'a>>, +) -> Result { + let central = connection.central.lock().await; + let peripheral = connection.peripheral.lock().await; + let central = central.as_ref().unwrap(); + let central = central.clone(); - let mut devices = vec![]; - for p in adapter.peripherals().await.unwrap() { - devices.push(BleDevice { - name: p.properties().await.unwrap().unwrap().local_name.unwrap_or("Unknown".to_string()), - address: p.address().to_string(), + let mut event_stream = central.events().await.unwrap(); + + tauri::async_runtime::spawn(async move { + while let Some(event) = event_stream.next().await { + if let DeviceDiscovered(_) | DeviceUpdated(_) = event.clone() { + let mut devices = vec![]; + for p in central.peripherals().await.unwrap() { + devices.push(BleDevice { + name: p + .properties() + .await + .unwrap() + .unwrap() + .local_name + .unwrap_or("Unknown".to_string()), + address: p.address().to_string(), + }); + } + println!("Devices: {:?}", serde_json::to_string(&devices).unwrap()); + app_handle + .emit_all("scan-list-update", serde_json::to_string(&devices).unwrap()) + .unwrap(); + } + if let DeviceConnected(_) = event.clone() { + let peripheral = peripheral.as_ref().unwrap().clone(); + app_handle + .emit_all( + "device-connected", + serde_json::to_string(&BleDevice { + name: peripheral + .properties() + .await + .unwrap() + .unwrap() + .local_name + .unwrap_or("Unknown".to_string()), + address: peripheral.address().to_string(), + }) + .unwrap(), + ) + .unwrap(); + } + } }); - } - Ok(serde_json::to_string(&devices).unwrap()) + Ok(true) +} +#[tauri::command] +async fn start_scan(connection: State<'_, BleConnection>) -> Result { + let central = connection.central.lock().await; + let central = central.as_ref().unwrap(); + central + .start_scan(ScanFilter::default()) + .await + .unwrap_or_else(|_| { + println!("Failed to start scan"); + }); + Ok(true) +} + +#[tauri::command] +async fn stop_scan(connection: State<'_, BleConnection>) -> Result { + let central = connection.central.lock().await; + let central = central.as_ref().unwrap(); + central.stop_scan().await.unwrap_or_else(|_| { + println!("Failed to stop scan"); + }); + Ok(true) } #[tauri::command] async fn connect( - address: String, - connection: State<'_, BleConnection>, - app_handle: AppHandle, + address: String, + connection: State<'_, BleConnection>, + app_handle: AppHandle, ) -> Result { - let adapter = connection.adapter.lock().await; - let adapter = adapter.as_ref().unwrap(); - // adapter.start_scan(ScanFilter::default()).await.unwrap(); - // tokio::time::sleep(std::time::Duration::from_secs(2)).await; + let central = connection.central.lock().await; + let central = central.as_ref().unwrap(); - let peripheral = adapter.peripherals().await.unwrap().into_iter().find(|p| p.address().to_string() == address).unwrap().clone(); - peripheral.connect().await.unwrap(); + let peripheral = central + .peripherals() + .await + .unwrap() + .into_iter() + .find(|p| p.address().to_string() == address) + .unwrap() + .clone(); + peripheral.connect().await.unwrap(); - *connection.peripheral.lock().await = Some(peripheral.clone()); + *connection.peripheral.lock().await = Some(peripheral.clone()); - peripheral.discover_services().await.unwrap_or_else(|_| { - println!("Failed to discover services"); - }); - let service = peripheral.services() - .into_iter() - .find(|s| s.uuid == uuid_from_u16(0x180D)) - .unwrap(); - let characteristic = service.characteristics - .into_iter() - .find(|c| c.uuid == uuid_from_u16(0x2A37)) - .unwrap(); + peripheral.discover_services().await.unwrap_or_else(|_| { + println!("Failed to discover services"); + }); + let service = peripheral + .services() + .into_iter() + .find(|s| s.uuid == uuid_from_u16(0x180D)) + .unwrap(); + let characteristic = service + .characteristics + .into_iter() + .find(|c| c.uuid == uuid_from_u16(0x2A37)) + .unwrap(); - peripheral.subscribe(&characteristic).await.unwrap_or_else(|_| { - println!("Failed to subscribe to characteristic"); - }); + peripheral + .subscribe(&characteristic) + .await + .unwrap_or_else(|_| { + println!("Failed to subscribe to characteristic"); + }); - tokio::spawn(async move { - let mut notification_stream = peripheral.notifications().await.unwrap(); - while let Some(notification) = notification_stream.next().await { - if notification.uuid == uuid_from_u16(0x2A37) { - app_handle.emit_all("heart-rate", notification.value[1]).unwrap(); - } - } - }); + tokio::spawn(async move { + let mut notification_stream = peripheral.notifications().await.unwrap(); + while let Some(notification) = notification_stream.next().await { + if notification.uuid == uuid_from_u16(0x2A37) { + app_handle + .emit_all("heart-rate", notification.value[1]) + .unwrap(); + } + } + }); - Ok(true) + Ok(true) } #[tauri::command] -async fn disconnect( - connection: State<'_, BleConnection> -) -> Result { - let connection = connection.peripheral.lock().await; - let connection = connection.as_ref().unwrap(); +async fn disconnect(connection: State<'_, BleConnection>) -> Result { + let connection = connection.peripheral.lock().await; + let connection = connection.as_ref().unwrap(); - connection.disconnect().await.unwrap(); + connection.disconnect().await.unwrap(); - Ok(true) + Ok(true) } #[tauri::command] -async fn get_connected_device( - connection: State<'_, BleConnection> -) -> Result { - let connection = connection.peripheral.lock().await; - let connection = connection.as_ref().unwrap(); +async fn get_connected_device(connection: State<'_, BleConnection>) -> Result { + let connection = connection.peripheral.lock().await; + let connection = connection.as_ref().unwrap(); - Ok(serde_json::to_string(&BleDevice { - name: connection.properties().await.unwrap().unwrap().local_name.unwrap_or("Unknown".to_string()), - address: connection.address().to_string(), - }).unwrap()) + Ok(serde_json::to_string(&BleDevice { + name: connection + .properties() + .await + .unwrap() + .unwrap() + .local_name + .unwrap_or("Unknown".to_string()), + address: connection.address().to_string(), + }) + .unwrap()) } #[tokio::main] async fn main() { - let ble_manager = BtleManager::new().await.unwrap(); - let adapter = ble_manager.adapters().await.unwrap().into_iter().next().unwrap(); + let ble_manager = BtleManager::new().await.unwrap(); + let central = ble_manager + .adapters() + .await + .unwrap() + .into_iter() + .next() + .unwrap(); - tauri::Builder::default() - .manage(BleConnection { - manager: Mutex::new(Some(ble_manager)), - adapter: Mutex::new(Some(adapter)), - peripheral: Default::default(), - }) - .invoke_handler(tauri::generate_handler![ - scan_devices, - connect, - disconnect, - get_connected_device - ]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + tauri::Builder::default() + .manage(BleConnection { + central: Mutex::new(Some(central)), + peripheral: Default::default(), + scan_devices: Default::default(), + }) + .invoke_handler(tauri::generate_handler![ + request_central_events, + start_scan, + stop_scan, + connect, + disconnect, + get_connected_device + ]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1f492bd..b28fd5a 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -32,9 +32,9 @@ { "title": "Heartbeat Cat", "minWidth": 800, - "minHeight": 600, + "minHeight": 500, "width": 800, - "height": 600 + "height": 500 } ], "security": { diff --git a/src/App.vue b/src/App.vue index 4731741..ba6767e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,22 @@ diff --git a/src/assets/css/font.css b/src/assets/css/font.css new file mode 100644 index 0000000..4f53ce7 --- /dev/null +++ b/src/assets/css/font.css @@ -0,0 +1,109 @@ +/* noto-sans-sc-200 - chinese-simplified_latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Noto Sans SC'; + font-style: normal; + font-weight: 200; + src: url('../fonts/noto-sans-sc-v36-chinese-simplified_latin-200.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +/* noto-sans-sc-regular - chinese-simplified_latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Noto Sans SC'; + font-style: normal; + font-weight: 400; + src: url('../fonts/noto-sans-sc-v36-chinese-simplified_latin-regular.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +/* noto-sans-sc-600 - chinese-simplified_latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Noto Sans SC'; + font-style: normal; + font-weight: 600; + src: url('../fonts/noto-sans-sc-v36-chinese-simplified_latin-600.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +/* noto-sans-sc-800 - chinese-simplified_latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Noto Sans SC'; + font-style: normal; + font-weight: 800; + src: url('../fonts/noto-sans-sc-v36-chinese-simplified_latin-800.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +/* rubik-300 - latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Rubik'; + font-style: normal; + font-weight: 300; + src: url('../fonts/rubik-v28-latin-300.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +/* rubik-300italic - latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Rubik'; + font-style: italic; + font-weight: 300; + src: url('../fonts/rubik-v28-latin-300italic.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +/* rubik-regular - latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Rubik'; + font-style: normal; + font-weight: 400; + src: url('../fonts/rubik-v28-latin-regular.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +/* rubik-italic - latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Rubik'; + font-style: italic; + font-weight: 400; + src: url('../fonts/rubik-v28-latin-italic.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +/* rubik-600 - latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Rubik'; + font-style: normal; + font-weight: 600; + src: url('../fonts/rubik-v28-latin-600.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +/* rubik-600italic - latin */ +@font-face { + font-display: swap; + /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Rubik'; + font-style: italic; + font-weight: 600; + src: url('../fonts/rubik-v28-latin-600italic.woff2') format('woff2'); + /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} \ No newline at end of file diff --git a/src/assets/css/style.css b/src/assets/css/style.css new file mode 100644 index 0000000..d93f3d4 --- /dev/null +++ b/src/assets/css/style.css @@ -0,0 +1,20 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + * { + -webkit-user-drag: none; + @apply select-none; + } +} + +@layer utilities { + .allow-drag { + -webkit-user-drag: auto; + } + + .allow-select { + @apply select-text; + } +} \ No newline at end of file diff --git a/src/assets/fonts/noto-sans-sc-v36-chinese-simplified_latin-200.woff2 b/src/assets/fonts/noto-sans-sc-v36-chinese-simplified_latin-200.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..571efc08c44c50e391d0bed459a094581cf18f95 GIT binary patch literal 1085220 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