├── .env ├── .npmrc ├── docs └── video.png ├── .vscode └── extensions.json ├── src ├── composables │ ├── index.ts │ ├── dark.ts │ ├── global.ts │ ├── actions.ts │ └── detect.ts ├── styles │ └── main.css ├── utils │ ├── gpu.ts │ ├── labels.json │ └── renderBox.ts ├── components │ ├── FullScreenBtn.vue │ ├── Hello.vue │ ├── Tabs │ │ ├── TabsWebRTC.vue │ │ ├── TabsPicture.vue │ │ ├── TabsVideo.vue │ │ └── TabsWebCam.vue │ └── VideoRtc.vue ├── App.vue ├── main.ts └── pages │ └── index.vue ├── .env.development ├── public ├── yolov8n_web_model │ ├── group1-shard1of4.bin │ ├── group1-shard2of4.bin │ ├── group1-shard3of4.bin │ ├── group1-shard4of4.bin │ └── metadata.yaml └── vite.svg ├── shims.d.ts ├── .editorconfig ├── netlify.toml ├── .gitignore ├── eslint.config.js ├── tsconfig.json ├── index.html ├── components.d.ts ├── unocss.config.ts ├── LICENSE ├── package.json ├── README.md ├── vite.config.ts └── auto-imports.d.ts /.env: -------------------------------------------------------------------------------- 1 | # open url 2 | VITE_HOST=0.0.0.0 3 | VITE_PORT=3333 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /docs/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanseria/yolov8-tfjs-vue-webrtc-demo/HEAD/docs/video.png -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /src/composables/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dark' 2 | export * from './global' 3 | export * from './actions' 4 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | # proxy url 3 | VITE_PROXY_URL= 4 | # open url 5 | VITE_HOST=0.0.0.0 6 | VITE_PORT=3333 7 | -------------------------------------------------------------------------------- /public/yolov8n_web_model/group1-shard1of4.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanseria/yolov8-tfjs-vue-webrtc-demo/HEAD/public/yolov8n_web_model/group1-shard1of4.bin -------------------------------------------------------------------------------- /public/yolov8n_web_model/group1-shard2of4.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanseria/yolov8-tfjs-vue-webrtc-demo/HEAD/public/yolov8n_web_model/group1-shard2of4.bin -------------------------------------------------------------------------------- /public/yolov8n_web_model/group1-shard3of4.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanseria/yolov8-tfjs-vue-webrtc-demo/HEAD/public/yolov8n_web_model/group1-shard3of4.bin -------------------------------------------------------------------------------- /public/yolov8n_web_model/group1-shard4of4.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanseria/yolov8-tfjs-vue-webrtc-demo/HEAD/public/yolov8n_web_model/group1-shard4of4.bin -------------------------------------------------------------------------------- /shims.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import type { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /src/styles/main.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | #app { 4 | height: 100%; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | html.dark { 10 | background: #121212; 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/utils/gpu.ts: -------------------------------------------------------------------------------- 1 | export function isWebGPUSupported(): boolean { 2 | return ((typeof window !== 'undefined') || 3 | //@ts-ignore 4 | (typeof WorkerGlobalScope !== 'undefined')) && 5 | !!navigator.gpu; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/FullScreenBtn.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NODE_VERSION = "18" 3 | 4 | [build] 5 | publish = "dist" 6 | command = "pnpm run build" 7 | 8 | [[redirects]] 9 | from = "/*" 10 | to = "/index.html" 11 | status = 200 12 | 13 | [[headers]] 14 | for = "/manifest.webmanifest" 15 | [headers.values] 16 | Content-Type = "application/manifest+json" 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | // eslint.config.js 2 | import antfu from '@antfu/eslint-config' 3 | 4 | export default antfu({ 5 | unocss: true, 6 | formatters: { 7 | css: true, // by default use Prettier 8 | html: true, // by default use Prettier 9 | toml: 'dprint', // use dprint for TOML 10 | markdown: 'prettier', // use prettier for markdown 11 | }, 12 | ignores: [ 13 | 'node_modules/', 14 | 'public/', 15 | 'dist/', 16 | ], 17 | }) 18 | -------------------------------------------------------------------------------- /src/components/Hello.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/composables/dark.ts: -------------------------------------------------------------------------------- 1 | export const isDark = useDark({ 2 | onChanged(dark: boolean) { 3 | if (dark) { 4 | // 设置为暗黑主题 5 | document.documentElement.classList.remove('light') 6 | document.documentElement.classList.add('dark') 7 | document.body.setAttribute('arco-theme', 'dark') 8 | } 9 | else { 10 | // 恢复亮色主题 11 | document.documentElement.classList.remove('dark') 12 | document.documentElement.classList.add('light') 13 | document.body.removeAttribute('arco-theme') 14 | } 15 | }, 16 | }) 17 | export const toggleDark = useToggle(isDark) 18 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createRouter, createWebHistory } from 'vue-router' 3 | import routes from 'virtual:generated-pages' 4 | import ArcoVue from '@arco-design/web-vue' 5 | import ArcoVueIcon from '@arco-design/web-vue/es/icon' 6 | import App from './App.vue' 7 | 8 | import '@arco-design/web-vue/dist/arco.css' 9 | import '@unocss/reset/tailwind.css' 10 | import './styles/main.css' 11 | import 'uno.css' 12 | 13 | const app = createApp(App) 14 | const router = createRouter({ 15 | history: createWebHistory(import.meta.env.BASE_URL), 16 | routes, 17 | }) 18 | app.use(ArcoVue, {}) 19 | app.use(ArcoVueIcon) 20 | app.use(router) 21 | app.mount('#app') 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "ESNext", 5 | "target": "es2016", 6 | "lib": ["DOM", "ESNext"], 7 | "strict": true, 8 | "jsx": "preserve", 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "noUnusedLocals": true, 14 | "strictNullChecks": true, 15 | "allowJs": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "types": [ 18 | "vite/client", 19 | "vite-plugin-pages/client" 20 | ], 21 | "paths": { 22 | "~/*": ["src/*"] 23 | } 24 | }, 25 | "exclude": ["dist", "node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /src/composables/global.ts: -------------------------------------------------------------------------------- 1 | import type { GraphModel, io } from '@tensorflow/tfjs' 2 | // import { isWebGPUSupported } from '~/utils/gpu'; 3 | // import '@tensorflow/tfjs-backend-webgpu'; 4 | import '@tensorflow/tfjs-backend-webgl'; 5 | 6 | export * as tf from '@tensorflow/tfjs' 7 | 8 | export const model: { 9 | net: GraphModel | null 10 | } = { 11 | net: null, 12 | } 13 | export const inputShape = ref([1, 0, 0, 3]) 14 | // model configs 15 | export const modelName = 'yolov8n' 16 | 17 | export const tfjsLoading = ref(true) 18 | export const tfjsProgress = ref(0) 19 | export const tfjsSpinTip = computed(() => { 20 | return `Loading model... ${(tfjsProgress.value * 100).toFixed(2)}%` 21 | }) 22 | 23 | export const globalActiveKey = ref('1') 24 | -------------------------------------------------------------------------------- /src/utils/labels.json: -------------------------------------------------------------------------------- 1 | ["person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"] 2 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | yolov8-tfjs-vue-webrtc-demo 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-vue-components 5 | // Read more: https://github.com/vuejs/core/pull/3399 6 | export {} 7 | 8 | declare module 'vue' { 9 | export interface GlobalComponents { 10 | FullScreenBtn: typeof import('./src/components/FullScreenBtn.vue')['default'] 11 | Hello: typeof import('./src/components/Hello.vue')['default'] 12 | RouterLink: typeof import('vue-router')['RouterLink'] 13 | RouterView: typeof import('vue-router')['RouterView'] 14 | TabsPicture: typeof import('./src/components/Tabs/TabsPicture.vue')['default'] 15 | TabsVideo: typeof import('./src/components/Tabs/TabsVideo.vue')['default'] 16 | TabsWebCam: typeof import('./src/components/Tabs/TabsWebCam.vue')['default'] 17 | TabsWebRTC: typeof import('./src/components/Tabs/TabsWebRTC.vue')['default'] 18 | VideoRtc: typeof import('./src/components/VideoRtc.vue')['default'] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | yolov8-tfjs-vue-webrtc-demo 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /unocss.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | presetAttributify, 4 | presetIcons, 5 | presetUno, 6 | presetWebFonts, 7 | // transformerDirectives, 8 | // transformerVariantGroup, 9 | } from 'unocss' 10 | 11 | export default defineConfig({ 12 | shortcuts: [ 13 | ['btn', 'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'], 14 | ['icon-btn', 'text-[0.9em] inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600 !outline-none'], 15 | ], 16 | presets: [ 17 | presetUno(), 18 | presetAttributify(), 19 | presetIcons({ 20 | scale: 1.2, 21 | warn: true, 22 | }), 23 | presetWebFonts({ 24 | fonts: { 25 | sans: 'DM Sans', 26 | serif: 'DM Serif Display', 27 | mono: 'DM Mono', 28 | }, 29 | }), 30 | ], 31 | // transformers: [ 32 | // transformerDirectives(), 33 | // transformerVariantGroup(), 34 | // ], 35 | }) 36 | -------------------------------------------------------------------------------- /src/composables/actions.ts: -------------------------------------------------------------------------------- 1 | import * as tf from '@tensorflow/tfjs' 2 | 3 | import '@tensorflow/tfjs-backend-webgl' 4 | import { tfjsLoading, tfjsProgress } from './global' 5 | 6 | export function loadTfModels() { 7 | tf.ready().then(async () => { 8 | const yolov8 = await tf.loadGraphModel( 9 | `${window.location.href}/${modelName}_web_model/model.json`, 10 | { 11 | onProgress: (fractions) => { 12 | tfjsLoading.value = true 13 | tfjsProgress.value = fractions 14 | }, 15 | }, 16 | ) // load model 17 | 18 | // warming up model 19 | if (yolov8.inputs[0].shape) { 20 | const dummyInput = tf.ones(yolov8.inputs[0].shape) 21 | const warmupResults = yolov8.execute(dummyInput) 22 | tfjsLoading.value = false 23 | tfjsProgress.value = 1 24 | model.net = yolov8 25 | inputShape.value = yolov8.inputs[0].shape 26 | tf.dispose([warmupResults, dummyInput]) // cleanup memory 27 | } 28 | }) 29 | } 30 | 31 | export function initNet() { 32 | tf.disposeVariables() 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-PRESENT lanseria 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/components/Tabs/TabsWebRTC.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Play / Pause 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arco-uno-basic-template", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite --host", 8 | "build": "vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview", 11 | "up": "taze major -I" 12 | }, 13 | "dependencies": { 14 | "@arco-design/web-vue": "2.54.2", 15 | "@tensorflow/tfjs": "^4.16.0", 16 | "@tensorflow/tfjs-backend-webgl": "^4.16.0", 17 | "@tensorflow/tfjs-backend-webgpu": "4.16.0", 18 | "@vueuse/core": "^10.7.2", 19 | "vue": "^3.4.13", 20 | "vue-router": "^4.2.5" 21 | }, 22 | "devDependencies": { 23 | "@antfu/eslint-config": "^2.6.2", 24 | "@iconify-json/carbon": "^1.1.27", 25 | "@types/node": "^18.19.7", 26 | "@unocss/eslint-plugin": "^0.58.3", 27 | "@unocss/reset": "^0.58.3", 28 | "@vitejs/plugin-vue": "^5.0.3", 29 | "eslint": "^8.56.0", 30 | "eslint-plugin-format": "^0.1.0", 31 | "pnpm": "^8.14.1", 32 | "taze": "^0.13.1", 33 | "typescript": "^5.3.3", 34 | "unocss": "^0.58.3", 35 | "unplugin-auto-import": "^0.17.3", 36 | "unplugin-vue-components": "^0.26.0", 37 | "vite": "^5.0.11", 38 | "vite-plugin-pages": "^0.32.0", 39 | "vue-tsc": "^1.8.27" 40 | }, 41 | "eslintConfig": { 42 | "extends": "@antfu" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Tabs/TabsPicture.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 37 | 38 | 39 | 40 | 选择本地文件 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/components/Tabs/TabsVideo.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 40 | 41 | 42 | 43 | 选择本地文件 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /public/yolov8n_web_model/metadata.yaml: -------------------------------------------------------------------------------- 1 | description: Ultralytics YOLOv8n model trained on coco.yaml 2 | author: Ultralytics 3 | license: GPL-3.0 https://ultralytics.com/license 4 | version: 8.0.57 5 | stride: 32 6 | task: detect 7 | batch: 1 8 | imgsz: 9 | - 640 10 | - 640 11 | names: 12 | 0: person 13 | 1: bicycle 14 | 2: car 15 | 3: motorcycle 16 | 4: airplane 17 | 5: bus 18 | 6: train 19 | 7: truck 20 | 8: boat 21 | 9: traffic light 22 | 10: fire hydrant 23 | 11: stop sign 24 | 12: parking meter 25 | 13: bench 26 | 14: bird 27 | 15: cat 28 | 16: dog 29 | 17: horse 30 | 18: sheep 31 | 19: cow 32 | 20: elephant 33 | 21: bear 34 | 22: zebra 35 | 23: giraffe 36 | 24: backpack 37 | 25: umbrella 38 | 26: handbag 39 | 27: tie 40 | 28: suitcase 41 | 29: frisbee 42 | 30: skis 43 | 31: snowboard 44 | 32: sports ball 45 | 33: kite 46 | 34: baseball bat 47 | 35: baseball glove 48 | 36: skateboard 49 | 37: surfboard 50 | 38: tennis racket 51 | 39: bottle 52 | 40: wine glass 53 | 41: cup 54 | 42: fork 55 | 43: knife 56 | 44: spoon 57 | 45: bowl 58 | 46: banana 59 | 47: apple 60 | 48: sandwich 61 | 49: orange 62 | 50: broccoli 63 | 51: carrot 64 | 52: hot dog 65 | 53: pizza 66 | 54: donut 67 | 55: cake 68 | 56: chair 69 | 57: couch 70 | 58: potted plant 71 | 59: bed 72 | 60: dining table 73 | 61: toilet 74 | 62: tv 75 | 63: laptop 76 | 64: mouse 77 | 65: remote 78 | 66: keyboard 79 | 67: cell phone 80 | 68: microwave 81 | 69: oven 82 | 70: toaster 83 | 71: sink 84 | 72: refrigerator 85 | 73: book 86 | 74: clock 87 | 75: vase 88 | 76: scissors 89 | 77: teddy bear 90 | 78: hair drier 91 | 79: toothbrush 92 | -------------------------------------------------------------------------------- /src/components/Tabs/TabsWebCam.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 | 31 | 32 | {{ enabled ? 'Stop' : 'Start' }} 33 | 34 | 35 | 36 | 43 | {{ camera.label }} 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yolov8-tfjs-vue-webrtc-demo 2 | 3 | ## Description 4 | 5 | "yolov8-tfjs-vue-webrtc-demo"是一个利用深度学习目标检测模型YOLOv8、TensorFlow.js框架、Vue.js框架和WebRTC技术实现的演示项目。该项目可以通过前端界面实时展示视频流,并对视频中的目标进行实时检测和识别。借助GPU加速的WebGL后端,该项目能够实现快速高效的模型推理,同时通过WebRTC技术,还能够实现点对点的视频传输,为实时应用提供了强大的支持。" 6 | 7 | "yolov8-tfjs-vue-webrtc-demo" is a demonstration project that utilizes the YOLOv8 deep learning object detection model, the TensorFlow.js framework, the Vue.js framework, and WebRTC technology. This project can display video streams in real-time through a front-end interface, and perform real-time detection and recognition of objects in the video. With the GPU-accelerated WebGL backend, this project can achieve fast and efficient model inference. Additionally, using WebRTC technology, it can enable peer-to-peer video transmission, providing powerful support for real-time applications. 8 | 9 | ## Demo 10 | 11 | link: https://yolov8-tfjs-vue-webrtc-demo.netlify.app/ 12 | 13 |  14 | 15 | ## how to use 如何使用 16 | 17 | 将 YOLOv8 转换为 tfjs 模型,并将其放入 public 文件夹中。 18 | 打开 [global.ts](src/composables/global.ts) 文件并将以下代码块: 19 | ```ts 20 | // if chrome version 113 above you can use webgpu 21 | import '@tensorflow/tfjs-backend-webgpu' 22 | 23 | export const modelName = 'yolov8n' 24 | // replace 25 | export const modelName = 'your model prefix name ' 26 | 27 | // import '@tensorflow/tfjs-backend-webgl'; 28 | ``` 29 | 编辑 labels.json 文件以匹配您的模型输出标签。 30 | 31 | [labels.json](src/utils/labels.json)文件 32 | 33 | 在终端中运行以下命令: 34 | 35 | ```bash 36 | pnpm i && pnpm dev 37 | ``` 38 | 39 | ## useable models 可用模型 40 | 41 | - 安全帽检测 https://huggingface.co/lanseria/yolov8n-hard-hat-detection_web_model 42 | 43 | ## webRTC 依赖项目请使用 go2RTC 44 | 45 | - go2rtc https://github.com/AlexxIT/go2rtc 46 | 47 | ## yolov8 model 转为 tfjs model 方式 48 | 49 | - reference https://github.com/Hyuto/yolov8-tfjs 50 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { ConfigEnv, UserConfig, loadEnv } from 'vite' 3 | import vue from '@vitejs/plugin-vue' 4 | import Pages from 'vite-plugin-pages' 5 | import Components from 'unplugin-vue-components/vite' 6 | import AutoImport from 'unplugin-auto-import/vite' 7 | import Unocss from 'unocss/vite' 8 | import { ArcoResolver } from 'unplugin-vue-components/resolvers' 9 | 10 | export default ({ mode }: ConfigEnv): UserConfig => { 11 | const root = process.cwd() 12 | const env = loadEnv(mode, root) 13 | return { 14 | server: { 15 | host: env.VITE_HOST, 16 | port: +env.VITE_PORT, 17 | // proxy: { 18 | // '^\/api\/': { 19 | // target: env.VITE_PROXY_URL, 20 | // changeOrigin: true, 21 | // xfwd: true, 22 | // rewrite: (path) => { 23 | // const replacePath = path.replace(/^\/api/, '') 24 | // return replacePath 25 | // }, 26 | // }, 27 | // }, 28 | }, 29 | resolve: { 30 | alias: { 31 | '~/': `${path.resolve(__dirname, 'src')}/`, 32 | 'vue': 'vue/dist/vue.esm-bundler.js', 33 | }, 34 | }, 35 | build: { 36 | sourcemap: true, 37 | }, 38 | plugins: [ 39 | vue({ 40 | reactivityTransform: true, 41 | }), 42 | 43 | // https://github.com/hannoeru/vite-plugin-pages 44 | Pages(), 45 | 46 | // https://github.com/antfu/unplugin-auto-import 47 | AutoImport({ 48 | resolvers: [ArcoResolver()], 49 | imports: [ 50 | 'vue', 51 | 'vue/macros', 52 | 'vue-router', 53 | '@vueuse/core', 54 | ], 55 | dts: true, 56 | dirs: [ 57 | './src/composables', 58 | ], 59 | vueTemplate: true, 60 | }), 61 | 62 | // https://github.com/antfu/vite-plugin-components 63 | Components({ 64 | dts: true, 65 | }), 66 | 67 | // https://github.com/antfu/unocss 68 | // see unocss.config.ts for config 69 | Unocss(), 70 | ], 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/utils/renderBox.ts: -------------------------------------------------------------------------------- 1 | import labels from './labels.json' 2 | 3 | /** 4 | * Render prediction boxes 5 | * @param {HTMLCanvasElement} canvasRef canvas tag reference 6 | * @param {Array} boxes_data boxes array 7 | * @param {Array} scores_data scores array 8 | * @param {Array} classes_data class array 9 | * @param {Array[Number]} ratios boxes ratio [xRatio, yRatio] 10 | */ 11 | export function renderBoxes(canvasRef: HTMLCanvasElement, boxes_data: Float32Array | Int32Array | Uint8Array, scores_data: Float32Array | Int32Array | Uint8Array, classes_data: Float32Array | Int32Array | Uint8Array, ratios: any[]) { 12 | const ctx = canvasRef.getContext('2d') 13 | if (ctx) { 14 | ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) // clean canvas 15 | 16 | const colors = new Colors() 17 | 18 | // font configs 19 | const font = `${Math.max( 20 | Math.round(Math.max(ctx.canvas.width, ctx.canvas.height) / 40), 21 | 14, 22 | )}px Arial` 23 | ctx.font = font 24 | ctx.textBaseline = 'top' 25 | 26 | for (let i = 0; i < scores_data.length; ++i) { 27 | // filter based on class threshold 28 | const klass = labels[classes_data[i]] 29 | const color = colors.get(classes_data[i]) 30 | const score = (scores_data[i] * 100).toFixed(1) 31 | 32 | let [y1, x1, y2, x2] = boxes_data.slice(i * 4, (i + 1) * 4) 33 | x1 *= ratios[0] 34 | x2 *= ratios[0] 35 | y1 *= ratios[1] 36 | y2 *= ratios[1] 37 | const width = x2 - x1 38 | const height = y2 - y1 39 | 40 | // draw box. 41 | ctx.fillStyle = Colors.hexToRgba(color, 0.2)! 42 | ctx.fillRect(x1, y1, width, height) 43 | 44 | // draw border box. 45 | ctx.strokeStyle = color 46 | ctx.lineWidth = Math.max(Math.min(ctx.canvas.width, ctx.canvas.height) / 200, 2.5) 47 | ctx.strokeRect(x1, y1, width, height) 48 | 49 | // Draw the label background. 50 | ctx.fillStyle = color 51 | const textWidth = ctx.measureText(`${klass} - ${score}%`).width 52 | const textHeight = parseInt(font, 10) // base 10 53 | const yText = y1 - (textHeight + ctx.lineWidth) 54 | ctx.fillRect( 55 | x1 - 1, 56 | yText < 0 ? 0 : yText, // handle overflow label box 57 | textWidth + ctx.lineWidth, 58 | textHeight + ctx.lineWidth, 59 | ) 60 | 61 | // Draw labels 62 | ctx.fillStyle = '#ffffff' 63 | ctx.fillText(`${klass} - ${score}%`, x1 - 1, yText < 0 ? 0 : yText) 64 | } 65 | } 66 | } 67 | 68 | class Colors { 69 | palette: string[] 70 | n: number 71 | // ultralytics color palette https://ultralytics.com/ 72 | constructor() { 73 | this.palette = [ 74 | '#FF3838', 75 | '#FF9D97', 76 | '#FF701F', 77 | '#FFB21D', 78 | '#CFD231', 79 | '#48F90A', 80 | '#92CC17', 81 | '#3DDB86', 82 | '#1A9334', 83 | '#00D4BB', 84 | '#2C99A8', 85 | '#00C2FF', 86 | '#344593', 87 | '#6473FF', 88 | '#0018EC', 89 | '#8438FF', 90 | '#520085', 91 | '#CB38FF', 92 | '#FF95C8', 93 | '#FF37C7', 94 | ] 95 | this.n = this.palette.length 96 | } 97 | 98 | get = (i: number) => this.palette[Math.floor(i) % this.n] 99 | 100 | static hexToRgba = (hex: string, alpha: number) => { 101 | const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) 102 | return result 103 | ? `rgba(${[parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)].join( 104 | ', ', 105 | )}, ${alpha})` 106 | : null 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/composables/detect.ts: -------------------------------------------------------------------------------- 1 | import type { Rank, Tensor, Tensor1D, Tensor2D, Tensor3D } from '@tensorflow/tfjs' 2 | import labels from '~/utils/labels.json' 3 | import { renderBoxes } from '~/utils/renderBox' 4 | 5 | const numClass = labels.length 6 | let animationId = -1 7 | /** 8 | * Preprocess image / frame before forwarded into the model 9 | * @param {HTMLVideoElement|HTMLImageElement} source 10 | * @param {Number} modelWidth 11 | * @param {Number} modelHeight 12 | * @returns input tensor, xRatio and yRatio 13 | */ 14 | function preprocess(source: HTMLVideoElement | HTMLImageElement, modelWidth: number, modelHeight: number): [Tensor, number, number] { 15 | let xRatio = 0 16 | let yRatio = 0 17 | // ratios for boxes 18 | 19 | const input = tf.tidy(() => { 20 | const img = tf.browser.fromPixels(source) 21 | 22 | // padding image to square => [n, m] to [n, n], n > m 23 | const [h, w] = img.shape.slice(0, 2) // get source width and height 24 | const maxSize = Math.max(w, h) // get max size 25 | const imgPadded = img.pad([ 26 | [0, maxSize - h], // padding y [bottom only] 27 | [0, maxSize - w], // padding x [right only] 28 | [0, 0], 29 | ]) as Tensor3D 30 | 31 | xRatio = maxSize / w // update xRatio 32 | yRatio = maxSize / h // update yRatio 33 | 34 | return tf.image 35 | .resizeBilinear(imgPadded, [modelWidth, modelHeight]) // resize frame 36 | .div(255.0) // normalize 37 | .expandDims(0) // add batch 38 | }) 39 | 40 | return [input, xRatio, yRatio] 41 | } 42 | 43 | /** 44 | * Function run inference and do detection from source. 45 | * @param {HTMLImageElement|HTMLVideoElement} source 46 | * @param {tf.GraphModel} model loaded YOLOv8 tensorflow.js model 47 | * @param {HTMLCanvasElement} canvasRef canvas reference 48 | * @param {VoidFunction} callback function to run after detection process 49 | */ 50 | export async function detect(source: HTMLImageElement | HTMLVideoElement, canvasRef: HTMLCanvasElement, callback = () => {}) { 51 | tf.engine().startScope() // start scoping tf engine 52 | const [modelWidth, modelHeight] = inputShape.value.slice(1, 3) // get model width and height 53 | // console.log(modelWidth, modelHeight) 54 | const [input, xRatio, yRatio] = preprocess(source, modelWidth, modelHeight) // preprocess image 55 | // console.log(model.net) 56 | 57 | const res = model.net!.execute(input!) as Tensor // inference model 58 | const transRes = res.transpose([0, 2, 1]) // transpose result [b, det, n] => [b, n, det] 59 | const boxes = tf.tidy(() => { 60 | const w = transRes.slice([0, 0, 2], [-1, -1, 1]) // get width 61 | const h = transRes.slice([0, 0, 3], [-1, -1, 1]) // get height 62 | const x1 = tf.sub(transRes.slice([0, 0, 0], [-1, -1, 1]), tf.div(w, 2)) // x1 63 | const y1 = tf.sub(transRes.slice([0, 0, 1], [-1, -1, 1]), tf.div(h, 2)) // y1 64 | return tf 65 | .concat( 66 | [ 67 | y1, 68 | x1, 69 | tf.add(y1, h), // y2 70 | tf.add(x1, w), // x2 71 | ], 72 | 2, 73 | ) 74 | .squeeze() 75 | }) as Tensor2D // process boxes [y1, x1, y2, x2] 76 | 77 | const [scores, classes] = tf.tidy(() => { 78 | const rawScores = transRes.slice([0, 0, 4], [-1, -1, numClass]).squeeze() // class scores 79 | return [rawScores.max(1), rawScores.argMax(1)] 80 | }) as [Tensor1D, Tensor2D ] // get max scores and classes index 81 | 82 | const nms = await tf.image.nonMaxSuppressionAsync(boxes, scores, 500, 0.45, 0.2) // NMS to filter boxes 83 | 84 | const boxes_data = await boxes.gather(nms, 0).data() 85 | const scores_data = await scores.gather(nms, 0).data() 86 | const classes_data = await classes.gather(nms, 0).data() 87 | 88 | renderBoxes(canvasRef, boxes_data, scores_data, classes_data, [xRatio, yRatio]) 89 | 90 | tf.dispose([res, transRes, boxes, scores, classes, nms]) 91 | callback() 92 | tf.engine().endScope() 93 | } 94 | 95 | /** 96 | * Function to detect video from every source. 97 | * @param {HTMLVideoElement} vidSource video source 98 | * @param {tf.GraphModel} model loaded YOLOv8 tensorflow.js model 99 | * @param {HTMLCanvasElement} canvasRef canvas reference 100 | */ 101 | export function detectVideo(vidSource: HTMLVideoElement, canvasRef: HTMLCanvasElement) { 102 | /** 103 | * Function to detect every frame from video 104 | */ 105 | // console.log(vidSource, canvasRef) 106 | const detectFrame = async () => { 107 | if (vidSource.videoWidth === 0 && vidSource.srcObject === null) { 108 | console.warn('vidSource.srcObject === null') 109 | const ctx = canvasRef.getContext('2d') 110 | ctx && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) // clean canvas 111 | return // handle if source is closed 112 | } 113 | 114 | detect(vidSource, canvasRef, () => { 115 | animationId = requestAnimationFrame(detectFrame) // get another frame 116 | }) 117 | } 118 | 119 | detectFrame() // initialize to detect every frame 120 | } 121 | export function unDetectVideo() { 122 | console.warn('unDetectVideo') 123 | cancelAnimationFrame(animationId) 124 | } 125 | -------------------------------------------------------------------------------- /src/components/VideoRtc.vue: -------------------------------------------------------------------------------- 1 | 284 | 285 | 286 | 287 | 288 | 289 | {{ errorMsg }} 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 307 | -------------------------------------------------------------------------------- /auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // noinspection JSUnusedGlobalSymbols 5 | // Generated by unplugin-auto-import 6 | export {} 7 | declare global { 8 | const $: typeof import('vue/macros')['$'] 9 | const $$: typeof import('vue/macros')['$$'] 10 | const $computed: typeof import('vue/macros')['$computed'] 11 | const $customRef: typeof import('vue/macros')['$customRef'] 12 | const $ref: typeof import('vue/macros')['$ref'] 13 | const $shallowRef: typeof import('vue/macros')['$shallowRef'] 14 | const $toRef: typeof import('vue/macros')['$toRef'] 15 | const EffectScope: typeof import('vue')['EffectScope'] 16 | const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] 17 | const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] 18 | const computed: typeof import('vue')['computed'] 19 | const computedAsync: typeof import('@vueuse/core')['computedAsync'] 20 | const computedEager: typeof import('@vueuse/core')['computedEager'] 21 | const computedInject: typeof import('@vueuse/core')['computedInject'] 22 | const computedWithControl: typeof import('@vueuse/core')['computedWithControl'] 23 | const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] 24 | const controlledRef: typeof import('@vueuse/core')['controlledRef'] 25 | const createApp: typeof import('vue')['createApp'] 26 | const createEventHook: typeof import('@vueuse/core')['createEventHook'] 27 | const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] 28 | const createInjectionState: typeof import('@vueuse/core')['createInjectionState'] 29 | const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] 30 | const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate'] 31 | const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] 32 | const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise'] 33 | const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] 34 | const customRef: typeof import('vue')['customRef'] 35 | const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] 36 | const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] 37 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 38 | const defineComponent: typeof import('vue')['defineComponent'] 39 | const detect: typeof import('./src/composables/detect')['detect'] 40 | const detectVideo: typeof import('./src/composables/detect')['detectVideo'] 41 | const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] 42 | const effectScope: typeof import('vue')['effectScope'] 43 | const extendRef: typeof import('@vueuse/core')['extendRef'] 44 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 45 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 46 | const globalActiveKey: typeof import('./src/composables/global')['globalActiveKey'] 47 | const h: typeof import('vue')['h'] 48 | const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] 49 | const initNet: typeof import('./src/composables/actions')['initNet'] 50 | const inject: typeof import('vue')['inject'] 51 | const injectLocal: typeof import('@vueuse/core')['injectLocal'] 52 | const inputShape: typeof import('./src/composables/global')['inputShape'] 53 | const isDark: typeof import('./src/composables/dark')['isDark'] 54 | const isDefined: typeof import('@vueuse/core')['isDefined'] 55 | const isProxy: typeof import('vue')['isProxy'] 56 | const isReactive: typeof import('vue')['isReactive'] 57 | const isReadonly: typeof import('vue')['isReadonly'] 58 | const isRef: typeof import('vue')['isRef'] 59 | const loadTfModels: typeof import('./src/composables/actions')['loadTfModels'] 60 | const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] 61 | const markRaw: typeof import('vue')['markRaw'] 62 | const model: typeof import('./src/composables/global')['model'] 63 | const modelName: typeof import('./src/composables/global')['modelName'] 64 | const nextTick: typeof import('vue')['nextTick'] 65 | const onActivated: typeof import('vue')['onActivated'] 66 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 67 | const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] 68 | const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] 69 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 70 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 71 | const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] 72 | const onDeactivated: typeof import('vue')['onDeactivated'] 73 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 74 | const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke'] 75 | const onLongPress: typeof import('@vueuse/core')['onLongPress'] 76 | const onMounted: typeof import('vue')['onMounted'] 77 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 78 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 79 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 80 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 81 | const onStartTyping: typeof import('@vueuse/core')['onStartTyping'] 82 | const onUnmounted: typeof import('vue')['onUnmounted'] 83 | const onUpdated: typeof import('vue')['onUpdated'] 84 | const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] 85 | const provide: typeof import('vue')['provide'] 86 | const provideLocal: typeof import('@vueuse/core')['provideLocal'] 87 | const reactify: typeof import('@vueuse/core')['reactify'] 88 | const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] 89 | const reactive: typeof import('vue')['reactive'] 90 | const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed'] 91 | const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit'] 92 | const reactivePick: typeof import('@vueuse/core')['reactivePick'] 93 | const readonly: typeof import('vue')['readonly'] 94 | const ref: typeof import('vue')['ref'] 95 | const refAutoReset: typeof import('@vueuse/core')['refAutoReset'] 96 | const refDebounced: typeof import('@vueuse/core')['refDebounced'] 97 | const refDefault: typeof import('@vueuse/core')['refDefault'] 98 | const refThrottled: typeof import('@vueuse/core')['refThrottled'] 99 | const refWithControl: typeof import('@vueuse/core')['refWithControl'] 100 | const resolveComponent: typeof import('vue')['resolveComponent'] 101 | const resolveRef: typeof import('@vueuse/core')['resolveRef'] 102 | const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] 103 | const shallowReactive: typeof import('vue')['shallowReactive'] 104 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 105 | const shallowRef: typeof import('vue')['shallowRef'] 106 | const syncRef: typeof import('@vueuse/core')['syncRef'] 107 | const syncRefs: typeof import('@vueuse/core')['syncRefs'] 108 | const templateRef: typeof import('@vueuse/core')['templateRef'] 109 | const tf: typeof import('./src/composables/global')['tf'] 110 | const tfjsLoading: typeof import('./src/composables/global')['tfjsLoading'] 111 | const tfjsProgress: typeof import('./src/composables/global')['tfjsProgress'] 112 | const tfjsSpinTip: typeof import('./src/composables/global')['tfjsSpinTip'] 113 | const throttledRef: typeof import('@vueuse/core')['throttledRef'] 114 | const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] 115 | const toRaw: typeof import('vue')['toRaw'] 116 | const toReactive: typeof import('@vueuse/core')['toReactive'] 117 | const toRef: typeof import('vue')['toRef'] 118 | const toRefs: typeof import('vue')['toRefs'] 119 | const toValue: typeof import('vue')['toValue'] 120 | const toggleDark: typeof import('./src/composables/dark')['toggleDark'] 121 | const triggerRef: typeof import('vue')['triggerRef'] 122 | const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] 123 | const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] 124 | const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] 125 | const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] 126 | const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted'] 127 | const unDetectVideo: typeof import('./src/composables/detect')['unDetectVideo'] 128 | const unref: typeof import('vue')['unref'] 129 | const unrefElement: typeof import('@vueuse/core')['unrefElement'] 130 | const until: typeof import('@vueuse/core')['until'] 131 | const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] 132 | const useAnimate: typeof import('@vueuse/core')['useAnimate'] 133 | const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference'] 134 | const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery'] 135 | const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] 136 | const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] 137 | const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] 138 | const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] 139 | const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes'] 140 | const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin'] 141 | const useArrayMap: typeof import('@vueuse/core')['useArrayMap'] 142 | const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce'] 143 | const useArraySome: typeof import('@vueuse/core')['useArraySome'] 144 | const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique'] 145 | const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] 146 | const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] 147 | const useAttrs: typeof import('vue')['useAttrs'] 148 | const useBase64: typeof import('@vueuse/core')['useBase64'] 149 | const useBattery: typeof import('@vueuse/core')['useBattery'] 150 | const useBluetooth: typeof import('@vueuse/core')['useBluetooth'] 151 | const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints'] 152 | const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel'] 153 | const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] 154 | const useCached: typeof import('@vueuse/core')['useCached'] 155 | const useClipboard: typeof import('@vueuse/core')['useClipboard'] 156 | const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems'] 157 | const useCloned: typeof import('@vueuse/core')['useCloned'] 158 | const useColorMode: typeof import('@vueuse/core')['useColorMode'] 159 | const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] 160 | const useCounter: typeof import('@vueuse/core')['useCounter'] 161 | const useCssModule: typeof import('vue')['useCssModule'] 162 | const useCssVar: typeof import('@vueuse/core')['useCssVar'] 163 | const useCssVars: typeof import('vue')['useCssVars'] 164 | const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] 165 | const useCycleList: typeof import('@vueuse/core')['useCycleList'] 166 | const useDark: typeof import('@vueuse/core')['useDark'] 167 | const useDateFormat: typeof import('@vueuse/core')['useDateFormat'] 168 | const useDebounce: typeof import('@vueuse/core')['useDebounce'] 169 | const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] 170 | const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] 171 | const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion'] 172 | const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation'] 173 | const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio'] 174 | const useDevicesList: typeof import('@vueuse/core')['useDevicesList'] 175 | const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia'] 176 | const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility'] 177 | const useDraggable: typeof import('@vueuse/core')['useDraggable'] 178 | const useDropZone: typeof import('@vueuse/core')['useDropZone'] 179 | const useElementBounding: typeof import('@vueuse/core')['useElementBounding'] 180 | const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint'] 181 | const useElementHover: typeof import('@vueuse/core')['useElementHover'] 182 | const useElementSize: typeof import('@vueuse/core')['useElementSize'] 183 | const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility'] 184 | const useEventBus: typeof import('@vueuse/core')['useEventBus'] 185 | const useEventListener: typeof import('@vueuse/core')['useEventListener'] 186 | const useEventSource: typeof import('@vueuse/core')['useEventSource'] 187 | const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] 188 | const useFavicon: typeof import('@vueuse/core')['useFavicon'] 189 | const useFetch: typeof import('@vueuse/core')['useFetch'] 190 | const useFileDialog: typeof import('@vueuse/core')['useFileDialog'] 191 | const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] 192 | const useFocus: typeof import('@vueuse/core')['useFocus'] 193 | const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] 194 | const useFps: typeof import('@vueuse/core')['useFps'] 195 | const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] 196 | const useGamepad: typeof import('@vueuse/core')['useGamepad'] 197 | const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] 198 | const useIdle: typeof import('@vueuse/core')['useIdle'] 199 | const useImage: typeof import('@vueuse/core')['useImage'] 200 | const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] 201 | const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver'] 202 | const useInterval: typeof import('@vueuse/core')['useInterval'] 203 | const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] 204 | const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] 205 | const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] 206 | const useLink: typeof import('vue-router')['useLink'] 207 | const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] 208 | const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] 209 | const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] 210 | const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] 211 | const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] 212 | const useMemoize: typeof import('@vueuse/core')['useMemoize'] 213 | const useMemory: typeof import('@vueuse/core')['useMemory'] 214 | const useMounted: typeof import('@vueuse/core')['useMounted'] 215 | const useMouse: typeof import('@vueuse/core')['useMouse'] 216 | const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] 217 | const useMousePressed: typeof import('@vueuse/core')['useMousePressed'] 218 | const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver'] 219 | const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage'] 220 | const useNetwork: typeof import('@vueuse/core')['useNetwork'] 221 | const useNow: typeof import('@vueuse/core')['useNow'] 222 | const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl'] 223 | const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination'] 224 | const useOnline: typeof import('@vueuse/core')['useOnline'] 225 | const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] 226 | const useParallax: typeof import('@vueuse/core')['useParallax'] 227 | const useParentElement: typeof import('@vueuse/core')['useParentElement'] 228 | const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver'] 229 | const usePermission: typeof import('@vueuse/core')['usePermission'] 230 | const usePointer: typeof import('@vueuse/core')['usePointer'] 231 | const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] 232 | const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] 233 | const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] 234 | const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] 235 | const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] 236 | const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] 237 | const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] 238 | const usePrevious: typeof import('@vueuse/core')['usePrevious'] 239 | const useRafFn: typeof import('@vueuse/core')['useRafFn'] 240 | const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] 241 | const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] 242 | const useRoute: typeof import('vue-router')['useRoute'] 243 | const useRouter: typeof import('vue-router')['useRouter'] 244 | const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] 245 | const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] 246 | const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] 247 | const useScroll: typeof import('@vueuse/core')['useScroll'] 248 | const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] 249 | const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] 250 | const useShare: typeof import('@vueuse/core')['useShare'] 251 | const useSlots: typeof import('vue')['useSlots'] 252 | const useSorted: typeof import('@vueuse/core')['useSorted'] 253 | const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] 254 | const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] 255 | const useStepper: typeof import('@vueuse/core')['useStepper'] 256 | const useStorage: typeof import('@vueuse/core')['useStorage'] 257 | const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] 258 | const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] 259 | const useSupported: typeof import('@vueuse/core')['useSupported'] 260 | const useSwipe: typeof import('@vueuse/core')['useSwipe'] 261 | const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] 262 | const useTextDirection: typeof import('@vueuse/core')['useTextDirection'] 263 | const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] 264 | const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize'] 265 | const useThrottle: typeof import('@vueuse/core')['useThrottle'] 266 | const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn'] 267 | const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory'] 268 | const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] 269 | const useTimeout: typeof import('@vueuse/core')['useTimeout'] 270 | const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] 271 | const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] 272 | const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] 273 | const useTitle: typeof import('@vueuse/core')['useTitle'] 274 | const useToNumber: typeof import('@vueuse/core')['useToNumber'] 275 | const useToString: typeof import('@vueuse/core')['useToString'] 276 | const useToggle: typeof import('@vueuse/core')['useToggle'] 277 | const useTransition: typeof import('@vueuse/core')['useTransition'] 278 | const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] 279 | const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] 280 | const useVModel: typeof import('@vueuse/core')['useVModel'] 281 | const useVModels: typeof import('@vueuse/core')['useVModels'] 282 | const useVibrate: typeof import('@vueuse/core')['useVibrate'] 283 | const useVirtualList: typeof import('@vueuse/core')['useVirtualList'] 284 | const useWakeLock: typeof import('@vueuse/core')['useWakeLock'] 285 | const useWebNotification: typeof import('@vueuse/core')['useWebNotification'] 286 | const useWebSocket: typeof import('@vueuse/core')['useWebSocket'] 287 | const useWebWorker: typeof import('@vueuse/core')['useWebWorker'] 288 | const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn'] 289 | const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] 290 | const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] 291 | const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] 292 | const watch: typeof import('vue')['watch'] 293 | const watchArray: typeof import('@vueuse/core')['watchArray'] 294 | const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] 295 | const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] 296 | const watchDeep: typeof import('@vueuse/core')['watchDeep'] 297 | const watchEffect: typeof import('vue')['watchEffect'] 298 | const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] 299 | const watchImmediate: typeof import('@vueuse/core')['watchImmediate'] 300 | const watchOnce: typeof import('@vueuse/core')['watchOnce'] 301 | const watchPausable: typeof import('@vueuse/core')['watchPausable'] 302 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 303 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 304 | const watchThrottled: typeof import('@vueuse/core')['watchThrottled'] 305 | const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable'] 306 | const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] 307 | const whenever: typeof import('@vueuse/core')['whenever'] 308 | } 309 | // for type re-export 310 | declare global { 311 | // @ts-ignore 312 | export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' 313 | import('vue') 314 | } 315 | // for vue template auto import 316 | import { UnwrapRef } from 'vue' 317 | declare module 'vue' { 318 | interface ComponentCustomProperties { 319 | readonly $$: UnwrapRef 320 | readonly $: UnwrapRef 321 | readonly $computed: UnwrapRef 322 | readonly $customRef: UnwrapRef 323 | readonly $ref: UnwrapRef 324 | readonly $shallowRef: UnwrapRef 325 | readonly $toRef: UnwrapRef 326 | readonly EffectScope: UnwrapRef 327 | readonly asyncComputed: UnwrapRef 328 | readonly autoResetRef: UnwrapRef 329 | readonly computed: UnwrapRef 330 | readonly computedAsync: UnwrapRef 331 | readonly computedEager: UnwrapRef 332 | readonly computedInject: UnwrapRef 333 | readonly computedWithControl: UnwrapRef 334 | readonly controlledComputed: UnwrapRef 335 | readonly controlledRef: UnwrapRef 336 | readonly createApp: UnwrapRef 337 | readonly createEventHook: UnwrapRef 338 | readonly createGlobalState: UnwrapRef 339 | readonly createInjectionState: UnwrapRef 340 | readonly createReactiveFn: UnwrapRef 341 | readonly createReusableTemplate: UnwrapRef 342 | readonly createSharedComposable: UnwrapRef 343 | readonly createTemplatePromise: UnwrapRef 344 | readonly createUnrefFn: UnwrapRef 345 | readonly customRef: UnwrapRef 346 | readonly debouncedRef: UnwrapRef 347 | readonly debouncedWatch: UnwrapRef 348 | readonly defineAsyncComponent: UnwrapRef 349 | readonly defineComponent: UnwrapRef 350 | readonly detect: UnwrapRef 351 | readonly detectVideo: UnwrapRef 352 | readonly eagerComputed: UnwrapRef 353 | readonly effectScope: UnwrapRef 354 | readonly extendRef: UnwrapRef 355 | readonly getCurrentInstance: UnwrapRef 356 | readonly getCurrentScope: UnwrapRef 357 | readonly globalActiveKey: UnwrapRef 358 | readonly h: UnwrapRef 359 | readonly ignorableWatch: UnwrapRef 360 | readonly initNet: UnwrapRef 361 | readonly inject: UnwrapRef 362 | readonly injectLocal: UnwrapRef 363 | readonly inputShape: UnwrapRef 364 | readonly isDark: UnwrapRef 365 | readonly isDefined: UnwrapRef 366 | readonly isProxy: UnwrapRef 367 | readonly isReactive: UnwrapRef 368 | readonly isReadonly: UnwrapRef 369 | readonly isRef: UnwrapRef 370 | readonly loadTfModels: UnwrapRef 371 | readonly makeDestructurable: UnwrapRef 372 | readonly markRaw: UnwrapRef 373 | readonly model: UnwrapRef 374 | readonly modelName: UnwrapRef 375 | readonly nextTick: UnwrapRef 376 | readonly onActivated: UnwrapRef 377 | readonly onBeforeMount: UnwrapRef 378 | readonly onBeforeRouteLeave: UnwrapRef 379 | readonly onBeforeRouteUpdate: UnwrapRef 380 | readonly onBeforeUnmount: UnwrapRef 381 | readonly onBeforeUpdate: UnwrapRef 382 | readonly onClickOutside: UnwrapRef 383 | readonly onDeactivated: UnwrapRef 384 | readonly onErrorCaptured: UnwrapRef 385 | readonly onKeyStroke: UnwrapRef 386 | readonly onLongPress: UnwrapRef 387 | readonly onMounted: UnwrapRef 388 | readonly onRenderTracked: UnwrapRef 389 | readonly onRenderTriggered: UnwrapRef 390 | readonly onScopeDispose: UnwrapRef 391 | readonly onServerPrefetch: UnwrapRef 392 | readonly onStartTyping: UnwrapRef 393 | readonly onUnmounted: UnwrapRef 394 | readonly onUpdated: UnwrapRef 395 | readonly pausableWatch: UnwrapRef 396 | readonly provide: UnwrapRef 397 | readonly provideLocal: UnwrapRef 398 | readonly reactify: UnwrapRef 399 | readonly reactifyObject: UnwrapRef 400 | readonly reactive: UnwrapRef 401 | readonly reactiveComputed: UnwrapRef 402 | readonly reactiveOmit: UnwrapRef 403 | readonly reactivePick: UnwrapRef 404 | readonly readonly: UnwrapRef 405 | readonly ref: UnwrapRef 406 | readonly refAutoReset: UnwrapRef 407 | readonly refDebounced: UnwrapRef 408 | readonly refDefault: UnwrapRef 409 | readonly refThrottled: UnwrapRef 410 | readonly refWithControl: UnwrapRef 411 | readonly resolveComponent: UnwrapRef 412 | readonly resolveRef: UnwrapRef 413 | readonly resolveUnref: UnwrapRef 414 | readonly shallowReactive: UnwrapRef 415 | readonly shallowReadonly: UnwrapRef 416 | readonly shallowRef: UnwrapRef 417 | readonly syncRef: UnwrapRef 418 | readonly syncRefs: UnwrapRef 419 | readonly templateRef: UnwrapRef 420 | readonly tf: UnwrapRef 421 | readonly tfjsLoading: UnwrapRef 422 | readonly tfjsProgress: UnwrapRef 423 | readonly tfjsSpinTip: UnwrapRef 424 | readonly throttledRef: UnwrapRef 425 | readonly throttledWatch: UnwrapRef 426 | readonly toRaw: UnwrapRef 427 | readonly toReactive: UnwrapRef 428 | readonly toRef: UnwrapRef 429 | readonly toRefs: UnwrapRef 430 | readonly toValue: UnwrapRef 431 | readonly toggleDark: UnwrapRef 432 | readonly triggerRef: UnwrapRef 433 | readonly tryOnBeforeMount: UnwrapRef 434 | readonly tryOnBeforeUnmount: UnwrapRef 435 | readonly tryOnMounted: UnwrapRef 436 | readonly tryOnScopeDispose: UnwrapRef 437 | readonly tryOnUnmounted: UnwrapRef 438 | readonly unDetectVideo: UnwrapRef 439 | readonly unref: UnwrapRef 440 | readonly unrefElement: UnwrapRef 441 | readonly until: UnwrapRef 442 | readonly useActiveElement: UnwrapRef 443 | readonly useAnimate: UnwrapRef 444 | readonly useArrayDifference: UnwrapRef 445 | readonly useArrayEvery: UnwrapRef 446 | readonly useArrayFilter: UnwrapRef 447 | readonly useArrayFind: UnwrapRef 448 | readonly useArrayFindIndex: UnwrapRef 449 | readonly useArrayFindLast: UnwrapRef 450 | readonly useArrayIncludes: UnwrapRef 451 | readonly useArrayJoin: UnwrapRef 452 | readonly useArrayMap: UnwrapRef 453 | readonly useArrayReduce: UnwrapRef 454 | readonly useArraySome: UnwrapRef 455 | readonly useArrayUnique: UnwrapRef 456 | readonly useAsyncQueue: UnwrapRef 457 | readonly useAsyncState: UnwrapRef 458 | readonly useAttrs: UnwrapRef 459 | readonly useBase64: UnwrapRef 460 | readonly useBattery: UnwrapRef 461 | readonly useBluetooth: UnwrapRef 462 | readonly useBreakpoints: UnwrapRef 463 | readonly useBroadcastChannel: UnwrapRef 464 | readonly useBrowserLocation: UnwrapRef 465 | readonly useCached: UnwrapRef 466 | readonly useClipboard: UnwrapRef 467 | readonly useClipboardItems: UnwrapRef 468 | readonly useCloned: UnwrapRef 469 | readonly useColorMode: UnwrapRef 470 | readonly useConfirmDialog: UnwrapRef 471 | readonly useCounter: UnwrapRef 472 | readonly useCssModule: UnwrapRef 473 | readonly useCssVar: UnwrapRef 474 | readonly useCssVars: UnwrapRef 475 | readonly useCurrentElement: UnwrapRef 476 | readonly useCycleList: UnwrapRef 477 | readonly useDark: UnwrapRef 478 | readonly useDateFormat: UnwrapRef 479 | readonly useDebounce: UnwrapRef 480 | readonly useDebounceFn: UnwrapRef 481 | readonly useDebouncedRefHistory: UnwrapRef 482 | readonly useDeviceMotion: UnwrapRef 483 | readonly useDeviceOrientation: UnwrapRef 484 | readonly useDevicePixelRatio: UnwrapRef 485 | readonly useDevicesList: UnwrapRef 486 | readonly useDisplayMedia: UnwrapRef 487 | readonly useDocumentVisibility: UnwrapRef 488 | readonly useDraggable: UnwrapRef 489 | readonly useDropZone: UnwrapRef 490 | readonly useElementBounding: UnwrapRef 491 | readonly useElementByPoint: UnwrapRef 492 | readonly useElementHover: UnwrapRef 493 | readonly useElementSize: UnwrapRef 494 | readonly useElementVisibility: UnwrapRef 495 | readonly useEventBus: UnwrapRef 496 | readonly useEventListener: UnwrapRef 497 | readonly useEventSource: UnwrapRef 498 | readonly useEyeDropper: UnwrapRef 499 | readonly useFavicon: UnwrapRef 500 | readonly useFetch: UnwrapRef 501 | readonly useFileDialog: UnwrapRef 502 | readonly useFileSystemAccess: UnwrapRef 503 | readonly useFocus: UnwrapRef 504 | readonly useFocusWithin: UnwrapRef 505 | readonly useFps: UnwrapRef 506 | readonly useFullscreen: UnwrapRef 507 | readonly useGamepad: UnwrapRef 508 | readonly useGeolocation: UnwrapRef 509 | readonly useIdle: UnwrapRef 510 | readonly useImage: UnwrapRef 511 | readonly useInfiniteScroll: UnwrapRef 512 | readonly useIntersectionObserver: UnwrapRef 513 | readonly useInterval: UnwrapRef 514 | readonly useIntervalFn: UnwrapRef 515 | readonly useKeyModifier: UnwrapRef 516 | readonly useLastChanged: UnwrapRef 517 | readonly useLink: UnwrapRef 518 | readonly useLocalStorage: UnwrapRef 519 | readonly useMagicKeys: UnwrapRef 520 | readonly useManualRefHistory: UnwrapRef 521 | readonly useMediaControls: UnwrapRef 522 | readonly useMediaQuery: UnwrapRef 523 | readonly useMemoize: UnwrapRef 524 | readonly useMemory: UnwrapRef 525 | readonly useMounted: UnwrapRef 526 | readonly useMouse: UnwrapRef 527 | readonly useMouseInElement: UnwrapRef 528 | readonly useMousePressed: UnwrapRef 529 | readonly useMutationObserver: UnwrapRef 530 | readonly useNavigatorLanguage: UnwrapRef 531 | readonly useNetwork: UnwrapRef 532 | readonly useNow: UnwrapRef 533 | readonly useObjectUrl: UnwrapRef 534 | readonly useOffsetPagination: UnwrapRef 535 | readonly useOnline: UnwrapRef 536 | readonly usePageLeave: UnwrapRef 537 | readonly useParallax: UnwrapRef 538 | readonly useParentElement: UnwrapRef 539 | readonly usePerformanceObserver: UnwrapRef 540 | readonly usePermission: UnwrapRef 541 | readonly usePointer: UnwrapRef 542 | readonly usePointerLock: UnwrapRef 543 | readonly usePointerSwipe: UnwrapRef 544 | readonly usePreferredColorScheme: UnwrapRef 545 | readonly usePreferredContrast: UnwrapRef 546 | readonly usePreferredDark: UnwrapRef 547 | readonly usePreferredLanguages: UnwrapRef 548 | readonly usePreferredReducedMotion: UnwrapRef 549 | readonly usePrevious: UnwrapRef 550 | readonly useRafFn: UnwrapRef 551 | readonly useRefHistory: UnwrapRef 552 | readonly useResizeObserver: UnwrapRef 553 | readonly useRoute: UnwrapRef 554 | readonly useRouter: UnwrapRef 555 | readonly useScreenOrientation: UnwrapRef 556 | readonly useScreenSafeArea: UnwrapRef 557 | readonly useScriptTag: UnwrapRef 558 | readonly useScroll: UnwrapRef 559 | readonly useScrollLock: UnwrapRef 560 | readonly useSessionStorage: UnwrapRef 561 | readonly useShare: UnwrapRef 562 | readonly useSlots: UnwrapRef 563 | readonly useSorted: UnwrapRef 564 | readonly useSpeechRecognition: UnwrapRef 565 | readonly useSpeechSynthesis: UnwrapRef 566 | readonly useStepper: UnwrapRef 567 | readonly useStorage: UnwrapRef 568 | readonly useStorageAsync: UnwrapRef 569 | readonly useStyleTag: UnwrapRef 570 | readonly useSupported: UnwrapRef