diff --git a/.changeset/all-poets-write.md b/.changeset/all-poets-write.md new file mode 100644 index 0000000..1a7d713 --- /dev/null +++ b/.changeset/all-poets-write.md @@ -0,0 +1,6 @@ +--- +'@tanstack/vue-hotkeys': minor +'@tanstack/vue-hotkeys-devtools': minor +--- + +- Initial Vue adapter release diff --git a/examples/vue/useHeldKeys/eslint.config.js b/examples/vue/useHeldKeys/eslint.config.js new file mode 100644 index 0000000..92d9bee --- /dev/null +++ b/examples/vue/useHeldKeys/eslint.config.js @@ -0,0 +1,13 @@ +// @ts-check + +import rootConfig from '../../../eslint.config.js' + +/** @type {import('eslint').Linter.Config[]} */ +const config = [ + ...rootConfig, + { + files: ['**/*.{ts,tsx,vue}'], + }, +] + +export default config diff --git a/examples/vue/useHeldKeys/index.html b/examples/vue/useHeldKeys/index.html new file mode 100644 index 0000000..dfe5163 --- /dev/null +++ b/examples/vue/useHeldKeys/index.html @@ -0,0 +1,12 @@ + + + + + + useHeldKeys - TanStack Hotkeys Vue Example + + +
+ + + diff --git a/examples/vue/useHeldKeys/package.json b/examples/vue/useHeldKeys/package.json new file mode 100644 index 0000000..25ae292 --- /dev/null +++ b/examples/vue/useHeldKeys/package.json @@ -0,0 +1,19 @@ +{ + "name": "@tanstack/hotkeys-example-vue-use-held-keys", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port=3076", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/vue-hotkeys": "^0.3.0", + "vue": "^3.5.14" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "typescript": "5.9.3", + "vite": "^7.3.1" + } +} diff --git a/examples/vue/useHeldKeys/src/App.vue b/examples/vue/useHeldKeys/src/App.vue new file mode 100644 index 0000000..7632285 --- /dev/null +++ b/examples/vue/useHeldKeys/src/App.vue @@ -0,0 +1,39 @@ + + + diff --git a/examples/vue/useHeldKeys/src/index.ts b/examples/vue/useHeldKeys/src/index.ts new file mode 100644 index 0000000..01433bc --- /dev/null +++ b/examples/vue/useHeldKeys/src/index.ts @@ -0,0 +1,4 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/examples/vue/useHeldKeys/tsconfig.json b/examples/vue/useHeldKeys/tsconfig.json new file mode 100644 index 0000000..b1d7261 --- /dev/null +++ b/examples/vue/useHeldKeys/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "jsx": "preserve" + }, + "include": ["src"], + "exclude": ["eslint.config.js"] +} diff --git a/examples/vue/useHeldKeys/vite.config.ts b/examples/vue/useHeldKeys/vite.config.ts new file mode 100644 index 0000000..c40aa3c --- /dev/null +++ b/examples/vue/useHeldKeys/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], +}) diff --git a/examples/vue/useHotkey/eslint.config.js b/examples/vue/useHotkey/eslint.config.js new file mode 100644 index 0000000..92d9bee --- /dev/null +++ b/examples/vue/useHotkey/eslint.config.js @@ -0,0 +1,13 @@ +// @ts-check + +import rootConfig from '../../../eslint.config.js' + +/** @type {import('eslint').Linter.Config[]} */ +const config = [ + ...rootConfig, + { + files: ['**/*.{ts,tsx,vue}'], + }, +] + +export default config diff --git a/examples/vue/useHotkey/index.html b/examples/vue/useHotkey/index.html new file mode 100644 index 0000000..84e0a78 --- /dev/null +++ b/examples/vue/useHotkey/index.html @@ -0,0 +1,14 @@ + + + + + + + useHotkey - TanStack Hotkeys Vue Example + + + +
+ + + diff --git a/examples/vue/useHotkey/package.json b/examples/vue/useHotkey/package.json new file mode 100644 index 0000000..58d1f27 --- /dev/null +++ b/examples/vue/useHotkey/package.json @@ -0,0 +1,23 @@ +{ + "name": "@tanstack/hotkeys-example-vue-use-hotkey", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port=3075", + "build": "vite build", + "preview": "vite preview", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "test:types": "tsc" + }, + "dependencies": { + "@tanstack/vue-hotkeys": "^0.3.0", + "vue": "^3.5.14" + }, + "devDependencies": { + "@tanstack/vue-hotkeys-devtools": "^0.3.0", + "@vitejs/plugin-vue": "^5.2.3", + "typescript": "5.9.3", + "vite": "^7.3.1" + } +} diff --git a/examples/vue/useHotkey/src/App.vue b/examples/vue/useHotkey/src/App.vue new file mode 100644 index 0000000..07d15ca --- /dev/null +++ b/examples/vue/useHotkey/src/App.vue @@ -0,0 +1,212 @@ + + + diff --git a/examples/vue/useHotkey/src/index.css b/examples/vue/useHotkey/src/index.css new file mode 100644 index 0000000..bd5b809 --- /dev/null +++ b/examples/vue/useHotkey/src/index.css @@ -0,0 +1,203 @@ +* { + box-sizing: border-box; +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background: #0f1419; + color: #e0e0e0; +} + +.app { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +header { + text-align: center; + margin-bottom: 3rem; +} + +h1 { + font-size: 3rem; + margin: 0 0 1rem; + background: linear-gradient(135deg, #00d4aa 0%, #00a3ff 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +header p { + font-size: 1.2rem; + opacity: 0.7; +} + +.demo-section { + background: #1a1f2e; + border-radius: 8px; + padding: 2rem; + margin-bottom: 2rem; +} + +.demo-section h2 { + margin-top: 0; + color: #00d4aa; +} + +.hotkey-display { + font-size: 2rem; + text-align: center; + padding: 2rem; + background: #0f1419; + border-radius: 8px; + font-family: monospace; +} + +.hotkey-list { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.hotkey-item { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem; + background: #0f1419; + border-radius: 8px; +} + +.hotkey-item kbd { + min-width: 100px; +} + +.hotkey-item button { + margin-left: auto; +} + +kbd { + background: #2a3f5f; + border: 1px solid #3a5f8f; + border-radius: 4px; + padding: 0.25rem 0.5rem; + font-family: monospace; + font-size: 0.9em; +} + +button { + background: #00a3ff; + color: white; + border: none; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; + font-size: 1rem; +} + +button:hover { + background: #0088dd; +} + +.tabs { + display: flex; + gap: 0.5rem; + margin-bottom: 1rem; +} + +.tabs button { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.tabs button.active { + background: #00d4aa; +} + +.tab-content { + padding: 2rem; + background: #0f1419; + border-radius: 8px; + min-height: 100px; +} + +.scoped-demo { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-bottom: 2rem; +} + +.scoped-box { + padding: 1.5rem; + background: #0f1419; + border-radius: 8px; + border: 2px solid transparent; +} + +.scoped-box:focus { + outline: none; + border-color: #00d4aa; +} + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.8); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal { + background: #1a1f2e; + padding: 2rem; + border-radius: 8px; + border: 2px solid #00d4aa; + min-width: 300px; +} + +.modal:focus { + outline: none; +} + +.editor-demo { + margin-top: 2rem; +} + +.code-editor { + width: 100%; + min-height: 200px; + background: #0f1419; + color: #e0e0e0; + border: 2px solid transparent; + border-radius: 8px; + padding: 1rem; + font-family: monospace; + font-size: 1rem; + resize: vertical; +} + +.code-editor:focus { + outline: none; + border-color: #00d4aa; +} + +.editor-stats { + margin-top: 0.5rem; + opacity: 0.7; + font-size: 0.9rem; +} diff --git a/examples/vue/useHotkey/src/index.ts b/examples/vue/useHotkey/src/index.ts new file mode 100644 index 0000000..50a4dab --- /dev/null +++ b/examples/vue/useHotkey/src/index.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import App from './App.vue' +import './index.css' + +createApp(App).mount('#app') diff --git a/examples/vue/useHotkey/tsconfig.json b/examples/vue/useHotkey/tsconfig.json new file mode 100644 index 0000000..b1d7261 --- /dev/null +++ b/examples/vue/useHotkey/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "jsx": "preserve" + }, + "include": ["src"], + "exclude": ["eslint.config.js"] +} diff --git a/examples/vue/useHotkey/vite.config.ts b/examples/vue/useHotkey/vite.config.ts new file mode 100644 index 0000000..c40aa3c --- /dev/null +++ b/examples/vue/useHotkey/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], +}) diff --git a/examples/vue/useHotkeyRecorder/eslint.config.js b/examples/vue/useHotkeyRecorder/eslint.config.js new file mode 100644 index 0000000..92d9bee --- /dev/null +++ b/examples/vue/useHotkeyRecorder/eslint.config.js @@ -0,0 +1,13 @@ +// @ts-check + +import rootConfig from '../../../eslint.config.js' + +/** @type {import('eslint').Linter.Config[]} */ +const config = [ + ...rootConfig, + { + files: ['**/*.{ts,tsx,vue}'], + }, +] + +export default config diff --git a/examples/vue/useHotkeyRecorder/index.html b/examples/vue/useHotkeyRecorder/index.html new file mode 100644 index 0000000..a5bc9bf --- /dev/null +++ b/examples/vue/useHotkeyRecorder/index.html @@ -0,0 +1,12 @@ + + + + + + useHotkeyRecorder - TanStack Hotkeys Vue Example + + +
+ + + diff --git a/examples/vue/useHotkeyRecorder/package.json b/examples/vue/useHotkeyRecorder/package.json new file mode 100644 index 0000000..459f07b --- /dev/null +++ b/examples/vue/useHotkeyRecorder/package.json @@ -0,0 +1,19 @@ +{ + "name": "@tanstack/hotkeys-example-vue-use-hotkey-recorder", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port=3079", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/vue-hotkeys": "^0.3.0", + "vue": "^3.5.14" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "typescript": "5.9.3", + "vite": "^7.3.1" + } +} diff --git a/examples/vue/useHotkeyRecorder/src/App.vue b/examples/vue/useHotkeyRecorder/src/App.vue new file mode 100644 index 0000000..36ef9c0 --- /dev/null +++ b/examples/vue/useHotkeyRecorder/src/App.vue @@ -0,0 +1,109 @@ + + + diff --git a/examples/vue/useHotkeyRecorder/src/index.ts b/examples/vue/useHotkeyRecorder/src/index.ts new file mode 100644 index 0000000..01433bc --- /dev/null +++ b/examples/vue/useHotkeyRecorder/src/index.ts @@ -0,0 +1,4 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/examples/vue/useHotkeyRecorder/tsconfig.json b/examples/vue/useHotkeyRecorder/tsconfig.json new file mode 100644 index 0000000..b1d7261 --- /dev/null +++ b/examples/vue/useHotkeyRecorder/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "jsx": "preserve" + }, + "include": ["src"], + "exclude": ["eslint.config.js"] +} diff --git a/examples/vue/useHotkeyRecorder/vite.config.ts b/examples/vue/useHotkeyRecorder/vite.config.ts new file mode 100644 index 0000000..c40aa3c --- /dev/null +++ b/examples/vue/useHotkeyRecorder/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], +}) diff --git a/examples/vue/useHotkeySequence/eslint.config.js b/examples/vue/useHotkeySequence/eslint.config.js new file mode 100644 index 0000000..92d9bee --- /dev/null +++ b/examples/vue/useHotkeySequence/eslint.config.js @@ -0,0 +1,13 @@ +// @ts-check + +import rootConfig from '../../../eslint.config.js' + +/** @type {import('eslint').Linter.Config[]} */ +const config = [ + ...rootConfig, + { + files: ['**/*.{ts,tsx,vue}'], + }, +] + +export default config diff --git a/examples/vue/useHotkeySequence/index.html b/examples/vue/useHotkeySequence/index.html new file mode 100644 index 0000000..18a2236 --- /dev/null +++ b/examples/vue/useHotkeySequence/index.html @@ -0,0 +1,12 @@ + + + + + + useHotkeySequence - TanStack Hotkeys Vue Example + + +
+ + + diff --git a/examples/vue/useHotkeySequence/package.json b/examples/vue/useHotkeySequence/package.json new file mode 100644 index 0000000..73f273f --- /dev/null +++ b/examples/vue/useHotkeySequence/package.json @@ -0,0 +1,19 @@ +{ + "name": "@tanstack/hotkeys-example-vue-use-hotkey-sequence", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port=3078", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/vue-hotkeys": "^0.3.0", + "vue": "^3.5.14" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "typescript": "5.9.3", + "vite": "^7.3.1" + } +} diff --git a/examples/vue/useHotkeySequence/src/App.vue b/examples/vue/useHotkeySequence/src/App.vue new file mode 100644 index 0000000..0734a14 --- /dev/null +++ b/examples/vue/useHotkeySequence/src/App.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/examples/vue/useHotkeySequence/src/index.ts b/examples/vue/useHotkeySequence/src/index.ts new file mode 100644 index 0000000..01433bc --- /dev/null +++ b/examples/vue/useHotkeySequence/src/index.ts @@ -0,0 +1,4 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/examples/vue/useHotkeySequence/tsconfig.json b/examples/vue/useHotkeySequence/tsconfig.json new file mode 100644 index 0000000..b1d7261 --- /dev/null +++ b/examples/vue/useHotkeySequence/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "jsx": "preserve" + }, + "include": ["src"], + "exclude": ["eslint.config.js"] +} diff --git a/examples/vue/useHotkeySequence/vite.config.ts b/examples/vue/useHotkeySequence/vite.config.ts new file mode 100644 index 0000000..c40aa3c --- /dev/null +++ b/examples/vue/useHotkeySequence/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], +}) diff --git a/examples/vue/useKeyhold/eslint.config.js b/examples/vue/useKeyhold/eslint.config.js new file mode 100644 index 0000000..92d9bee --- /dev/null +++ b/examples/vue/useKeyhold/eslint.config.js @@ -0,0 +1,13 @@ +// @ts-check + +import rootConfig from '../../../eslint.config.js' + +/** @type {import('eslint').Linter.Config[]} */ +const config = [ + ...rootConfig, + { + files: ['**/*.{ts,tsx,vue}'], + }, +] + +export default config diff --git a/examples/vue/useKeyhold/index.html b/examples/vue/useKeyhold/index.html new file mode 100644 index 0000000..c99cbf2 --- /dev/null +++ b/examples/vue/useKeyhold/index.html @@ -0,0 +1,12 @@ + + + + + + useKeyHold - TanStack Hotkeys Vue Example + + +
+ + + diff --git a/examples/vue/useKeyhold/package.json b/examples/vue/useKeyhold/package.json new file mode 100644 index 0000000..12aae13 --- /dev/null +++ b/examples/vue/useKeyhold/package.json @@ -0,0 +1,19 @@ +{ + "name": "@tanstack/hotkeys-example-vue-use-keyhold", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port=3077", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/vue-hotkeys": "^0.3.0", + "vue": "^3.5.14" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "typescript": "5.9.3", + "vite": "^7.3.1" + } +} diff --git a/examples/vue/useKeyhold/src/App.vue b/examples/vue/useKeyhold/src/App.vue new file mode 100644 index 0000000..8fba920 --- /dev/null +++ b/examples/vue/useKeyhold/src/App.vue @@ -0,0 +1,99 @@ + + + diff --git a/examples/vue/useKeyhold/src/index.ts b/examples/vue/useKeyhold/src/index.ts new file mode 100644 index 0000000..01433bc --- /dev/null +++ b/examples/vue/useKeyhold/src/index.ts @@ -0,0 +1,4 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/examples/vue/useKeyhold/tsconfig.json b/examples/vue/useKeyhold/tsconfig.json new file mode 100644 index 0000000..b1d7261 --- /dev/null +++ b/examples/vue/useKeyhold/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "jsx": "preserve" + }, + "include": ["src"], + "exclude": ["eslint.config.js"] +} diff --git a/examples/vue/useKeyhold/vite.config.ts b/examples/vue/useKeyhold/vite.config.ts new file mode 100644 index 0000000..c40aa3c --- /dev/null +++ b/examples/vue/useKeyhold/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], +}) diff --git a/packages/react-hotkeys/src/HotkeysProvider.tsx b/packages/react-hotkeys/src/HotkeysProvider.tsx index ee1bcb1..8e1a493 100644 --- a/packages/react-hotkeys/src/HotkeysProvider.tsx +++ b/packages/react-hotkeys/src/HotkeysProvider.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useContext, useMemo } from 'react' +import React, { createContext, use, useMemo } from 'react' import type { ReactNode } from 'react' import type { HotkeyRecorderOptions } from '@tanstack/hotkeys' import type { UseHotkeyOptions } from './useHotkey' @@ -35,17 +35,17 @@ export function HotkeysProvider({ ) return ( - + {children} - + ) } export function useHotkeysContext() { - return useContext(HotkeysContext) + return use(HotkeysContext) } export function useDefaultHotkeysOptions() { - const context = useContext(HotkeysContext) + const context = use(HotkeysContext) return context?.defaultOptions ?? {} } diff --git a/packages/vue-hotkeys-devtools/CHANGELOG.md b/packages/vue-hotkeys-devtools/CHANGELOG.md new file mode 100644 index 0000000..3e65f08 --- /dev/null +++ b/packages/vue-hotkeys-devtools/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tanstack/vue-hotkeys-devtools + +## 0.3.0 + +### Minor Changes + +- Initial Vue devtools release diff --git a/packages/vue-hotkeys-devtools/README.md b/packages/vue-hotkeys-devtools/README.md new file mode 100644 index 0000000..adfb0cd --- /dev/null +++ b/packages/vue-hotkeys-devtools/README.md @@ -0,0 +1,32 @@ +# TanStack Hotkeys Devtools for Vue + +Developer tools for TanStack Hotkeys Vue adapter. + +## Installation + +```bash +pnpm add @tanstack/vue-hotkeys-devtools +# or +npm install @tanstack/vue-hotkeys-devtools +# or +yarn add @tanstack/vue-hotkeys-devtools +``` + +## Usage + +```vue + + + +``` + +## License + +MIT © [Tanner Linsley](https://github.com/tannerlinsley) diff --git a/packages/vue-hotkeys-devtools/eslint.config.js b/packages/vue-hotkeys-devtools/eslint.config.js new file mode 100644 index 0000000..a3afead --- /dev/null +++ b/packages/vue-hotkeys-devtools/eslint.config.js @@ -0,0 +1,13 @@ +// @ts-check + +import rootConfig from '../../eslint.config.js' + +/** @type {import('eslint').Linter.Config[]} */ +const config = [ + ...rootConfig, + { + files: ['**/*.{ts,tsx,vue}'], + }, +] + +export default config diff --git a/packages/vue-hotkeys-devtools/package.json b/packages/vue-hotkeys-devtools/package.json new file mode 100644 index 0000000..9149efe --- /dev/null +++ b/packages/vue-hotkeys-devtools/package.json @@ -0,0 +1,66 @@ +{ + "name": "@tanstack/vue-hotkeys-devtools", + "version": "0.3.0", + "description": "Vue devtools for TanStack Hotkeys", + "author": "Tanner Linsley", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/TanStack/hotkeys.git", + "directory": "packages/vue-hotkeys-devtools" + }, + "homepage": "https://tanstack.com/hotkeys", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "vue", + "vuejs", + "tanstack", + "keys", + "devtools", + "hotkeys", + "keyboard" + ], + "scripts": { + "clean": "premove ./build ./dist", + "lint": "eslint ./src", + "lint:fix": "eslint ./src --fix", + "test:eslint": "eslint ./src", + "test:lib": "vitest --passWithNoTests", + "test:lib:dev": "pnpm test:lib --watch", + "test:types": "tsc", + "build": "tsdown" + }, + "type": "module", + "types": "./dist/index.d.cts", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "engines": { + "node": ">=18" + }, + "files": [ + "dist/", + "src" + ], + "peerDependencies": { + "vue": ">=3.0.0" + }, + "dependencies": { + "@tanstack/devtools-utils": "^0.3.0", + "@tanstack/hotkeys-devtools": "workspace:*" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "vue": "^3.5.14" + }, + "main": "./dist/index.cjs", + "module": "./dist/index.js" +} diff --git a/packages/vue-hotkeys-devtools/src/VueHotkeysDevtools.tsx b/packages/vue-hotkeys-devtools/src/VueHotkeysDevtools.tsx new file mode 100644 index 0000000..c4ba8b3 --- /dev/null +++ b/packages/vue-hotkeys-devtools/src/VueHotkeysDevtools.tsx @@ -0,0 +1,16 @@ +import { createVuePanel } from '@tanstack/devtools-utils/vue' +import { HotkeysDevtoolsCore } from '@tanstack/hotkeys-devtools' +import type { DevtoolsPanelProps } from '@tanstack/devtools-utils/vue' + +export interface HotkeysDevtoolsVueInit extends DevtoolsPanelProps {} + +type DevtoolsPanelConstructor = new (props: DevtoolsPanelProps) => { + mount: (el: HTMLElement, theme?: 'dark' | 'light' | 'system') => void + unmount: () => void +} + +const [HotkeysDevtoolsPanel, HotkeysDevtoolsPanelNoOp] = createVuePanel( + HotkeysDevtoolsCore as unknown as DevtoolsPanelConstructor, +) + +export { HotkeysDevtoolsPanel, HotkeysDevtoolsPanelNoOp } diff --git a/packages/vue-hotkeys-devtools/src/index.ts b/packages/vue-hotkeys-devtools/src/index.ts new file mode 100644 index 0000000..336d2da --- /dev/null +++ b/packages/vue-hotkeys-devtools/src/index.ts @@ -0,0 +1,14 @@ +import * as Devtools from './VueHotkeysDevtools' +import * as plugin from './plugin' + +export const HotkeysDevtoolsPanel = + process.env.NODE_ENV !== 'development' + ? Devtools.HotkeysDevtoolsPanelNoOp + : Devtools.HotkeysDevtoolsPanel + +export const hotkeysDevtoolsPlugin = + process.env.NODE_ENV !== 'development' + ? plugin.hotkeysDevtoolsNoOpPlugin + : plugin.hotkeysDevtoolsPlugin + +export type { HotkeysDevtoolsVueInit } from './VueHotkeysDevtools' diff --git a/packages/vue-hotkeys-devtools/src/plugin.ts b/packages/vue-hotkeys-devtools/src/plugin.ts new file mode 100644 index 0000000..e868f75 --- /dev/null +++ b/packages/vue-hotkeys-devtools/src/plugin.ts @@ -0,0 +1,9 @@ +import { createVuePlugin } from '@tanstack/devtools-utils/vue' +import { HotkeysDevtoolsPanel } from './VueHotkeysDevtools' + +const [hotkeysDevtoolsPlugin, hotkeysDevtoolsNoOpPlugin] = createVuePlugin({ + name: 'TanStack Hotkeys', + Component: HotkeysDevtoolsPanel, +}) + +export { hotkeysDevtoolsPlugin, hotkeysDevtoolsNoOpPlugin } diff --git a/packages/vue-hotkeys-devtools/src/production.ts b/packages/vue-hotkeys-devtools/src/production.ts new file mode 100644 index 0000000..a89c5e2 --- /dev/null +++ b/packages/vue-hotkeys-devtools/src/production.ts @@ -0,0 +1,5 @@ +export { HotkeysDevtoolsPanel } from './VueHotkeysDevtools' + +export type { HotkeysDevtoolsVueInit } from './VueHotkeysDevtools' + +export { hotkeysDevtoolsPlugin } from './plugin' diff --git a/packages/vue-hotkeys-devtools/src/vue.d.ts b/packages/vue-hotkeys-devtools/src/vue.d.ts new file mode 100644 index 0000000..b07a059 --- /dev/null +++ b/packages/vue-hotkeys-devtools/src/vue.d.ts @@ -0,0 +1,6 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/packages/vue-hotkeys-devtools/tsconfig.json b/packages/vue-hotkeys-devtools/tsconfig.json new file mode 100644 index 0000000..e716923 --- /dev/null +++ b/packages/vue-hotkeys-devtools/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "jsx": "preserve" + }, + "include": ["src", "vitest.config.ts"], + "exclude": ["eslint.config.js"] +} diff --git a/packages/vue-hotkeys-devtools/tsdown.config.ts b/packages/vue-hotkeys-devtools/tsdown.config.ts new file mode 100644 index 0000000..71071cb --- /dev/null +++ b/packages/vue-hotkeys-devtools/tsdown.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['./src/index.ts'], + format: ['esm', 'cjs'], + unbundle: true, + dts: true, + sourcemap: true, + clean: true, + minify: false, + fixedExtension: false, + exports: true, + publint: { + strict: true, + }, +}) diff --git a/packages/vue-hotkeys-devtools/vitest.config.ts b/packages/vue-hotkeys-devtools/vitest.config.ts new file mode 100644 index 0000000..cc39b3c --- /dev/null +++ b/packages/vue-hotkeys-devtools/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + test: { + environment: 'jsdom', + globals: true, + }, +}) diff --git a/packages/vue-hotkeys/README.md b/packages/vue-hotkeys/README.md new file mode 100644 index 0000000..c7d8e70 --- /dev/null +++ b/packages/vue-hotkeys/README.md @@ -0,0 +1,145 @@ +
+ TanStack Hotkeys +
+ +
+ +
+ + + + + + + + + +
+ +
+ + semantic-release + + + Release + + + Follow @TanStack + +
+ +
+ +### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/) + +
+ +# TanStack Hotkeys for Vue + +> [!NOTE] +> TanStack Hotkeys is pre-alpha (prototyping phase). We are actively developing the library and are open to feedback and contributions. + +Type-safe keyboard shortcuts for Vue. Template-string bindings, parsed objects, a cross-platform `Mod` key, a singleton Hotkey Manager, and utilities for cheatsheet UIs—built to stay SSR-friendly. + +- Type-safe bindings — template strings (`Mod+Shift+S`, `Escape`) or parsed objects for full control +- Flexible options — `keydown`/`keyup`, `preventDefault`, `stopPropagation`, conditional enabled, `requireReset` +- Cross-platform Mod — maps to Cmd on macOS and Ctrl on Windows/Linux +- Batteries included — validation + matching, sequences (Vim-style), key-state tracking, recorder UI helpers, Vue composables, and devtools (in progress) + +### Read the docs → + +## Installation + +```bash +pnpm add @tanstack/vue-hotkeys +# or +npm install @tanstack/vue-hotkeys +# or +yarn add @tanstack/vue-hotkeys +``` + +## Quick Start + +```vue + + + +``` + +## Features + +### Composables + +- `useHotkey` — Register a keyboard shortcut +- `useHotkeySequence` — Register multi-key sequences (Vim-style like 'g g') +- `useHeldKeys` — Track currently pressed keys +- `useKeyHold` — Check if a specific key is held +- `useHotkeyRecorder` — Record hotkeys from user input +- `HotkeysProvider` — Provide default options to all hotkeys + +### Advanced Usage + +```vue + + + +``` + +## License + +MIT © [Tanner Linsley](https://github.com/tannerlinsley) diff --git a/packages/vue-hotkeys/eslint.config.js b/packages/vue-hotkeys/eslint.config.js new file mode 100644 index 0000000..a3afead --- /dev/null +++ b/packages/vue-hotkeys/eslint.config.js @@ -0,0 +1,13 @@ +// @ts-check + +import rootConfig from '../../eslint.config.js' + +/** @type {import('eslint').Linter.Config[]} */ +const config = [ + ...rootConfig, + { + files: ['**/*.{ts,tsx,vue}'], + }, +] + +export default config diff --git a/packages/vue-hotkeys/package.json b/packages/vue-hotkeys/package.json new file mode 100644 index 0000000..a8d66d7 --- /dev/null +++ b/packages/vue-hotkeys/package.json @@ -0,0 +1,66 @@ +{ + "name": "@tanstack/vue-hotkeys", + "version": "0.3.0", + "description": "Vue adapter for TanStack Hotkeys", + "author": "Tanner Linsley", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/TanStack/hotkeys.git", + "directory": "packages/vue-hotkeys" + }, + "homepage": "https://tanstack.com/hotkeys", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "vue", + "vuejs", + "tanstack", + "hotkeys", + "keyboard", + "shortcuts" + ], + "scripts": { + "clean": "premove ./build ./dist", + "lint": "eslint ./src", + "lint:fix": "eslint ./src --fix", + "test:eslint": "eslint ./src", + "test:lib": "vitest --passWithNoTests", + "test:lib:dev": "pnpm test:lib --watch", + "test:types": "tsc", + "build": "tsdown" + }, + "type": "module", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.cts", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "engines": { + "node": ">=18" + }, + "files": [ + "dist", + "src" + ], + "dependencies": { + "@tanstack/hotkeys": "workspace:*", + "@tanstack/vue-store": "^0.9.1" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "@vue/test-utils": "^2.4.6", + "vue": "^3.5.14" + }, + "peerDependencies": { + "vue": ">=3.0.0" + } +} diff --git a/packages/vue-hotkeys/src/HotkeysProvider.tsx b/packages/vue-hotkeys/src/HotkeysProvider.tsx new file mode 100644 index 0000000..7e37c70 --- /dev/null +++ b/packages/vue-hotkeys/src/HotkeysProvider.tsx @@ -0,0 +1,29 @@ +import { defineComponent } from 'vue' +import { provideHotkeysContext } from './HotkeysProviderContext' +import type { HotkeysProviderOptions } from './HotkeysProviderContext' + +/** + * Vue component that provides default options for hotkeys context. + * + * @example + * ```vue + * + * ``` + */ +export const HotkeysProvider = defineComponent({ + name: 'HotkeysProvider', + props: { + defaultOptions: { + type: Object as () => HotkeysProviderOptions, + default: undefined, + }, + }, + setup(props, { slots }) { + provideHotkeysContext(props.defaultOptions) + return () => slots.default?.() + }, +}) diff --git a/packages/vue-hotkeys/src/HotkeysProviderContext.ts b/packages/vue-hotkeys/src/HotkeysProviderContext.ts new file mode 100644 index 0000000..10f1aff --- /dev/null +++ b/packages/vue-hotkeys/src/HotkeysProviderContext.ts @@ -0,0 +1,37 @@ +import { inject, provide } from 'vue' +import type { InjectionKey } from 'vue' +import type { HotkeyRecorderOptions } from '@tanstack/hotkeys' +import type { UseHotkeyOptions } from './useHotkey' +import type { UseHotkeySequenceOptions } from './useHotkeySequence' + +export interface HotkeysProviderOptions { + hotkey?: Partial + hotkeyRecorder?: Partial + hotkeySequence?: Partial +} + +interface HotkeysContextValue { + defaultOptions: HotkeysProviderOptions +} + +const HotkeysContext: InjectionKey = + Symbol('HotkeysContext') + +const DEFAULT_OPTIONS: HotkeysProviderOptions = {} + +export function provideHotkeysContext(defaultOptions?: HotkeysProviderOptions) { + const contextValue: HotkeysContextValue = { + defaultOptions: defaultOptions ?? DEFAULT_OPTIONS, + } + + provide(HotkeysContext, contextValue) +} + +export function useHotkeysContext() { + return inject(HotkeysContext, null) +} + +export function useDefaultHotkeysOptions() { + const context = inject(HotkeysContext, null) + return context?.defaultOptions ?? {} +} diff --git a/packages/vue-hotkeys/src/index.ts b/packages/vue-hotkeys/src/index.ts new file mode 100644 index 0000000..78cd36d --- /dev/null +++ b/packages/vue-hotkeys/src/index.ts @@ -0,0 +1,14 @@ +// Re-export everything from the core package +export * from '@tanstack/hotkeys' + +// Provider +export * from './HotkeysProvider' +export * from './HotkeysProviderContext' + +// Vue-specific composables +export * from './useHotkey' +export * from './useHeldKeys' +export * from './useHeldKeyCodes' +export * from './useKeyHold' +export * from './useHotkeySequence' +export * from './useHotkeyRecorder' diff --git a/packages/vue-hotkeys/src/useHeldKeyCodes.ts b/packages/vue-hotkeys/src/useHeldKeyCodes.ts new file mode 100644 index 0000000..b182ec0 --- /dev/null +++ b/packages/vue-hotkeys/src/useHeldKeyCodes.ts @@ -0,0 +1,34 @@ +import { useStore } from '@tanstack/vue-store' +import { getKeyStateTracker } from '@tanstack/hotkeys' +import type { Ref } from 'vue' + +/** + * Vue composable that returns a reactive ref mapping currently held key names to their physical `event.code` values. + * + * This is useful for debugging which physical key was pressed (e.g. distinguishing + * left vs right Shift via "ShiftLeft" / "ShiftRight"). + * + * @returns Reactive ref containing record mapping normalized key names to their `event.code` values + * + * @example + * ```vue + * + * + * + * ``` + */ +export function useHeldKeyCodes(): Ref> { + const tracker = getKeyStateTracker() + return useStore(tracker.store, (state) => state.heldCodes) +} diff --git a/packages/vue-hotkeys/src/useHeldKeys.ts b/packages/vue-hotkeys/src/useHeldKeys.ts new file mode 100644 index 0000000..c93ae30 --- /dev/null +++ b/packages/vue-hotkeys/src/useHeldKeys.ts @@ -0,0 +1,32 @@ +import { useStore } from '@tanstack/vue-store' +import { getKeyStateTracker } from '@tanstack/hotkeys' +import type { Ref } from 'vue' + +/** + * Vue composable that returns a reactive ref of currently held keyboard keys. + * + * This composable uses `useStore` from `@tanstack/vue-store` to subscribe + * to the global KeyStateTracker and updates whenever keys are pressed + * or released. + * + * @returns Reactive ref containing array of currently held key names + * + * @example + * ```vue + * + * + * + * ``` + */ +export function useHeldKeys(): Ref> { + const tracker = getKeyStateTracker() + return useStore(tracker.store, (state) => state.heldKeys) +} diff --git a/packages/vue-hotkeys/src/useHotkey.ts b/packages/vue-hotkeys/src/useHotkey.ts new file mode 100644 index 0000000..a34db47 --- /dev/null +++ b/packages/vue-hotkeys/src/useHotkey.ts @@ -0,0 +1,173 @@ +import { onUnmounted, unref, watch } from 'vue' +import { + detectPlatform, + formatHotkey, + getHotkeyManager, + rawHotkeyToParsedHotkey, +} from '@tanstack/hotkeys' +import { useDefaultHotkeysOptions } from './HotkeysProviderContext' +import type { + Hotkey, + HotkeyCallback, + HotkeyOptions, + HotkeyRegistrationHandle, + RegisterableHotkey, +} from '@tanstack/hotkeys' +import type { MaybeRefOrGetter } from 'vue' + +export interface UseHotkeyOptions extends Omit { + /** + * The DOM element to attach the event listener to. + * Can be a Ref, a getter function, direct DOM element, or null. + * Defaults to document. + */ + target?: + | MaybeRefOrGetter + | HTMLElement + | Document + | Window + | null +} + +/** + * Vue composable for registering a keyboard hotkey. + * + * Uses the singleton HotkeyManager for efficient event handling. + * The callback receives both the keyboard event and a context object + * containing the hotkey string and parsed hotkey. + * + * This composable automatically tracks reactive dependencies and updates + * the registration when options or the callback change. + * + * @param hotkey - The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform) + * @param callback - The function to call when the hotkey is pressed + * @param options - Options for the hotkey behavior + * + * @example + * ```vue + * + * ``` + * + * @example + * ```vue + * + * ``` + * + * @example + * ```vue + * + * + * + * ``` + */ +export function useHotkey( + hotkey: MaybeRefOrGetter, + callback: HotkeyCallback, + options: MaybeRefOrGetter = {}, +): void { + const defaultOptions = useDefaultHotkeysOptions() + const manager = getHotkeyManager() + + let registration: HotkeyRegistrationHandle | null = null + + // Watch for changes to reactive dependencies + const stopWatcher = watch( + () => { + const resolvedHotkey = unref(hotkey) + const resolvedOptions = unref(options) + const mergedOptions = { + ...defaultOptions.hotkey, + ...resolvedOptions, + } as UseHotkeyOptions + + return { resolvedHotkey, mergedOptions } + }, + ({ resolvedHotkey, mergedOptions }) => { + // Normalize to hotkey string + const platform = mergedOptions.platform ?? detectPlatform() + const hotkeyString: Hotkey = + typeof resolvedHotkey === 'string' + ? resolvedHotkey + : (formatHotkey( + rawHotkeyToParsedHotkey(resolvedHotkey as any, platform), + ) as Hotkey) + + // Resolve target + let targetValue = 'target' in mergedOptions ? mergedOptions.target : null + if (typeof targetValue === 'function') { + targetValue = targetValue() + } else { + targetValue = unref(targetValue) + } + const resolvedTarget = targetValue ?? (typeof document !== 'undefined' ? document : null) + + if (!resolvedTarget) { + return + } + + // Unregister previous registration if it exists + if (registration?.isActive) { + registration.unregister() + registration = null + } + + // Extract options without target (target is handled separately) + const { target: _target, ...optionsWithoutTarget } = mergedOptions + + // Register the hotkey + registration = manager.register(hotkeyString, callback, { + ...optionsWithoutTarget, + target: resolvedTarget, + }) + + // Update callback and options + if (registration.isActive) { + registration.callback = callback + registration.setOptions(optionsWithoutTarget) + } + }, + { immediate: true }, + ) + + // Cleanup on unmount + onUnmounted(() => { + stopWatcher() + if (registration?.isActive) { + registration.unregister() + registration = null + } + }) +} diff --git a/packages/vue-hotkeys/src/useHotkeyRecorder.ts b/packages/vue-hotkeys/src/useHotkeyRecorder.ts new file mode 100644 index 0000000..b23f49d --- /dev/null +++ b/packages/vue-hotkeys/src/useHotkeyRecorder.ts @@ -0,0 +1,96 @@ +import { onUnmounted, unref } from 'vue' +import { useStore } from '@tanstack/vue-store' +import { HotkeyRecorder } from '@tanstack/hotkeys' +import { useDefaultHotkeysOptions } from './HotkeysProviderContext' +import type {MaybeRefOrGetter, Ref} from 'vue'; +import type { Hotkey, HotkeyRecorderOptions } from '@tanstack/hotkeys' + +export interface VueHotkeyRecorder { + /** Whether recording is currently active */ + isRecording: Ref + /** The currently recorded hotkey (for live preview) */ + recordedHotkey: Ref + /** Start recording a new hotkey */ + startRecording: () => void + /** Stop recording (same as cancel) */ + stopRecording: () => void + /** Cancel recording without saving */ + cancelRecording: () => void +} + +/** + * Vue composable for recording keyboard shortcuts. + * + * This composable provides a thin wrapper around the framework-agnostic `HotkeyRecorder` + * class, managing all the complexity of capturing keyboard events, converting them + * to hotkey strings, and handling edge cases like Escape to cancel or Backspace/Delete + * to clear. + * + * @param options - Configuration options for the recorder + * @returns An object with recording state and control functions + * + * @example + * ```vue + * + * + * + * ``` + */ +export function useHotkeyRecorder( + options: MaybeRefOrGetter, +): VueHotkeyRecorder { + const defaultOptions = useDefaultHotkeysOptions() + + // Merge default options with provided options + const resolvedOptions = unref(options) + const mergedOptions = { + ...defaultOptions.hotkeyRecorder, + ...resolvedOptions, + } as HotkeyRecorderOptions + + // Create recorder instance + const recorder = new HotkeyRecorder(mergedOptions) + + // Subscribe to recorder state using useStore + const isRecording = useStore(recorder.store, (state) => state.isRecording) + const recordedHotkey = useStore( + recorder.store, + (state) => state.recordedHotkey, + ) + + // Cleanup on unmount + onUnmounted(() => { + recorder.destroy() + }) + + return { + isRecording: isRecording as Ref, + recordedHotkey: recordedHotkey as Ref, + startRecording: () => recorder.start(), + stopRecording: () => recorder.stop(), + cancelRecording: () => recorder.cancel(), + } +} diff --git a/packages/vue-hotkeys/src/useHotkeySequence.ts b/packages/vue-hotkeys/src/useHotkeySequence.ts new file mode 100644 index 0000000..8ec68aa --- /dev/null +++ b/packages/vue-hotkeys/src/useHotkeySequence.ts @@ -0,0 +1,136 @@ +import { onUnmounted, unref, watch } from 'vue' +import { getSequenceManager } from '@tanstack/hotkeys' +import { useDefaultHotkeysOptions } from './HotkeysProviderContext' +import type {MaybeRefOrGetter} from 'vue'; +import type { + HotkeyCallback, + HotkeySequence, + SequenceOptions, +} from '@tanstack/hotkeys' + +export interface UseHotkeySequenceOptions extends Omit< + SequenceOptions, + 'target' +> { + /** + * The DOM element to attach the event listener to. + * Can be a Ref, a getter function, direct DOM element, or null. + * Defaults to document. + */ + target?: + | MaybeRefOrGetter + | HTMLElement + | Document + | Window + | null +} + +/** + * Vue composable for registering a keyboard shortcut sequence (Vim-style). + * + * This composable allows you to register multi-key sequences like 'g g' or 'd d' + * that trigger when the full sequence is pressed within a timeout. + * + * @param sequence - Array of hotkey strings that form the sequence + * @param callback - Function to call when the sequence is completed + * @param options - Options for the sequence behavior + * + * @example + * ```vue + * + * + * + * ``` + */ +export function useHotkeySequence( + sequence: MaybeRefOrGetter, + callback: HotkeyCallback, + options: MaybeRefOrGetter = {}, +): void { + const defaultOptions = useDefaultHotkeysOptions() + const manager = getSequenceManager() + + let registration: any = null + + // Watch for changes to reactive dependencies + const stopWatcher = watch( + () => { + const resolvedSequence = unref(sequence) + const resolvedOptions = unref(options) + const mergedOptions = { + ...defaultOptions.hotkeySequence, + ...resolvedOptions, + } as UseHotkeySequenceOptions + + return { resolvedSequence, mergedOptions } + }, + ({ resolvedSequence, mergedOptions }) => { + if (resolvedSequence.length === 0) { + return + } + + // Resolve target + let targetValue = 'target' in mergedOptions ? mergedOptions.target : null + if (typeof targetValue === 'function') { + targetValue = targetValue() + } else { + targetValue = unref(targetValue) + } + const resolvedTarget = targetValue ?? (typeof document !== 'undefined' ? document : null) + + if (!resolvedTarget) { + return + } + + // Unregister previous registration if it exists + if (registration?.isActive) { + registration.unregister() + registration = null + } + + // Extract options without target (target is handled separately) + const { target: _target, ...optionsWithoutTarget } = mergedOptions + + // Register the sequence + registration = manager.register(resolvedSequence as any, callback, { + ...optionsWithoutTarget, + target: resolvedTarget, + }) + + // Update callback and options + if (registration.isActive) { + registration.callback = callback + registration.setOptions(optionsWithoutTarget) + } + }, + { immediate: true }, + ) + + // Cleanup on unmount + onUnmounted(() => { + stopWatcher() + if (registration?.isActive) { + registration.unregister() + registration = null + } + }) +} diff --git a/packages/vue-hotkeys/src/useKeyHold.ts b/packages/vue-hotkeys/src/useKeyHold.ts new file mode 100644 index 0000000..46897db --- /dev/null +++ b/packages/vue-hotkeys/src/useKeyHold.ts @@ -0,0 +1,61 @@ +import { useStore } from '@tanstack/vue-store' +import { getKeyStateTracker } from '@tanstack/hotkeys' +import { unref } from 'vue' +import type {MaybeRefOrGetter, Ref} from 'vue'; +import type { HeldKey } from '@tanstack/hotkeys' + +/** + * Vue composable that returns a reactive ref indicating whether a specific key is currently being held. + * + * This composable uses `useStore` from `@tanstack/vue-store` to subscribe + * to the global KeyStateTracker and uses a selector to determine if + * the specified key is held. + * + * @param key - The key to check (e.g., 'Shift', 'Control', 'A') + * @returns Reactive ref that is true if the key is currently held down + * + * @example + * ```vue + * + * + * + * ``` + * + * @example + * ```vue + * + * + * + * ``` + */ +export function useKeyHold(key: MaybeRefOrGetter): Ref { + const tracker = getKeyStateTracker() + + const isHeld = useStore(tracker.store, (state) => { + const keyValue = unref(key) + const normalizedKey = (typeof keyValue === 'string' ? keyValue : String(keyValue)).toLowerCase() + return state.heldKeys.some((heldKey) => heldKey.toLowerCase() === normalizedKey) + }) + + return isHeld as Ref +} diff --git a/packages/vue-hotkeys/src/vue.d.ts b/packages/vue-hotkeys/src/vue.d.ts new file mode 100644 index 0000000..b07a059 --- /dev/null +++ b/packages/vue-hotkeys/src/vue.d.ts @@ -0,0 +1,6 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/packages/vue-hotkeys/tsconfig.docs.json b/packages/vue-hotkeys/tsconfig.docs.json new file mode 100644 index 0000000..214e95c --- /dev/null +++ b/packages/vue-hotkeys/tsconfig.docs.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src"], + "exclude": ["tests", "vitest.config.ts"] +} diff --git a/packages/vue-hotkeys/tsconfig.json b/packages/vue-hotkeys/tsconfig.json new file mode 100644 index 0000000..d24b3d3 --- /dev/null +++ b/packages/vue-hotkeys/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "jsx": "preserve" + }, + "include": ["src", "src/**/*.vue", "vitest.config.ts", "tests"], + "exclude": ["eslint.config.js"] +} diff --git a/packages/vue-hotkeys/tsdown.config.ts b/packages/vue-hotkeys/tsdown.config.ts new file mode 100644 index 0000000..71071cb --- /dev/null +++ b/packages/vue-hotkeys/tsdown.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: ['./src/index.ts'], + format: ['esm', 'cjs'], + unbundle: true, + dts: true, + sourcemap: true, + clean: true, + minify: false, + fixedExtension: false, + exports: true, + publint: { + strict: true, + }, +}) diff --git a/packages/vue-hotkeys/vitest.config.ts b/packages/vue-hotkeys/vitest.config.ts new file mode 100644 index 0000000..cc39b3c --- /dev/null +++ b/packages/vue-hotkeys/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + test: { + environment: 'jsdom', + globals: true, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a663c03..0cbc556 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -380,7 +380,7 @@ importers: dependencies: '@tanstack/devtools-utils': specifier: ^0.3.0 - version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11) + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) '@tanstack/solid-devtools': specifier: 0.7.26 version: 0.7.26(csstype@3.2.3)(solid-js@1.9.11) @@ -405,7 +405,7 @@ importers: dependencies: '@tanstack/devtools-utils': specifier: ^0.3.0 - version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11) + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) '@tanstack/solid-devtools': specifier: 0.7.26 version: 0.7.26(csstype@3.2.3)(solid-js@1.9.11) @@ -436,7 +436,7 @@ importers: dependencies: '@tanstack/devtools-utils': specifier: ^0.3.0 - version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11) + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) '@tanstack/solid-devtools': specifier: 0.7.26 version: 0.7.26(csstype@3.2.3)(solid-js@1.9.11) @@ -461,7 +461,7 @@ importers: dependencies: '@tanstack/devtools-utils': specifier: ^0.3.0 - version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11) + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) '@tanstack/solid-devtools': specifier: 0.7.26 version: 0.7.26(csstype@3.2.3)(solid-js@1.9.11) @@ -486,7 +486,7 @@ importers: dependencies: '@tanstack/devtools-utils': specifier: ^0.3.0 - version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11) + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) '@tanstack/solid-devtools': specifier: 0.7.26 version: 0.7.26(csstype@3.2.3)(solid-js@1.9.11) @@ -507,6 +507,104 @@ importers: specifier: ^2.11.10 version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.11)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2)) + examples/vue/useHeldKeys: + dependencies: + '@tanstack/vue-hotkeys': + specifier: ^0.3.0 + version: link:../../../packages/vue-hotkeys + vue: + specifier: ^3.5.14 + version: 3.5.28(typescript@5.9.3) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.2.3 + version: 5.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) + typescript: + specifier: 5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2) + + examples/vue/useHotkey: + dependencies: + '@tanstack/vue-hotkeys': + specifier: ^0.3.0 + version: link:../../../packages/vue-hotkeys + vue: + specifier: ^3.5.14 + version: 3.5.28(typescript@5.9.3) + devDependencies: + '@tanstack/vue-hotkeys-devtools': + specifier: ^0.3.0 + version: link:../../../packages/vue-hotkeys-devtools + '@vitejs/plugin-vue': + specifier: ^5.2.3 + version: 5.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) + typescript: + specifier: 5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2) + + examples/vue/useHotkeyRecorder: + dependencies: + '@tanstack/vue-hotkeys': + specifier: ^0.3.0 + version: link:../../../packages/vue-hotkeys + vue: + specifier: ^3.5.14 + version: 3.5.28(typescript@5.9.3) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.2.3 + version: 5.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) + typescript: + specifier: 5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2) + + examples/vue/useHotkeySequence: + dependencies: + '@tanstack/vue-hotkeys': + specifier: ^0.3.0 + version: link:../../../packages/vue-hotkeys + vue: + specifier: ^3.5.14 + version: 3.5.28(typescript@5.9.3) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.2.3 + version: 5.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) + typescript: + specifier: 5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2) + + examples/vue/useKeyhold: + dependencies: + '@tanstack/vue-hotkeys': + specifier: ^0.3.0 + version: link:../../../packages/vue-hotkeys + vue: + specifier: ^3.5.14 + version: 3.5.28(typescript@5.9.3) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.2.3 + version: 5.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) + typescript: + specifier: 5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2) + packages/hotkeys: dependencies: '@tanstack/store': @@ -520,7 +618,7 @@ importers: version: 0.4.4(csstype@3.2.3)(solid-js@1.9.11) '@tanstack/devtools-utils': specifier: ^0.3.0 - version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11) + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) '@tanstack/hotkeys': specifier: workspace:* version: link:../hotkeys @@ -561,7 +659,7 @@ importers: dependencies: '@tanstack/devtools-utils': specifier: ^0.3.0 - version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11) + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) '@tanstack/hotkeys-devtools': specifier: workspace:* version: link:../hotkeys-devtools @@ -611,7 +709,7 @@ importers: dependencies: '@tanstack/devtools-utils': specifier: ^0.3.0 - version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11) + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) '@tanstack/hotkeys-devtools': specifier: workspace:* version: link:../hotkeys-devtools @@ -667,7 +765,7 @@ importers: dependencies: '@tanstack/devtools-utils': specifier: ^0.3.0 - version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11) + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) '@tanstack/hotkeys-devtools': specifier: workspace:* version: link:../hotkeys-devtools @@ -679,6 +777,41 @@ importers: specifier: ^2.11.10 version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.11)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2)) + packages/vue-hotkeys: + dependencies: + '@tanstack/hotkeys': + specifier: workspace:* + version: link:../hotkeys + '@tanstack/vue-store': + specifier: ^0.9.1 + version: 0.9.1(vue@3.5.28(typescript@5.9.3)) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.2.3 + version: 5.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.4.6 + vue: + specifier: ^3.5.14 + version: 3.5.28(typescript@5.9.3) + + packages/vue-hotkeys-devtools: + dependencies: + '@tanstack/devtools-utils': + specifier: ^0.3.0 + version: 0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3)) + '@tanstack/hotkeys-devtools': + specifier: workspace:* + version: link:../hotkeys-devtools + devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.2.3 + version: 5.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) + vue: + specifier: ^3.5.14 + version: 3.5.28(typescript@5.9.3) + packages: '@adobe/css-tools@4.4.4': @@ -1198,6 +1331,10 @@ packages: resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==} engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@jest/diff-sequences@30.0.1': resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1307,6 +1444,9 @@ packages: cpu: [x64] os: [win32] + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@oxc-project/types@0.112.0': resolution: {integrity: sha512-m6RebKHIRsax2iCwVpYW2ErQwa4ywHJrE4sCK3/8JK8ZZAWOKXaRJFl/uP51gaVyyXlaS4+chU1nSCdzYf6QqQ==} @@ -1418,6 +1558,10 @@ packages: cpu: [x64] os: [win32] + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@preact/preset-vite@2.10.3': resolution: {integrity: sha512-1SiS+vFItpkNdBs7q585PSAIln0wBeBdcpJYbzPs1qipsb/FssnkUioNXuRsb8ZnU8YEQHr+3v8+/mzWSnTQmg==} peerDependencies: @@ -1881,6 +2025,15 @@ packages: resolution: {integrity: sha512-wVT2YfKDSpd+4f7fk6UaPIP3a2J7LSovlyVuFF1PH2yQb7gjqehod5zdFiwFyEXgvI9XGuFvvs1OehkKNYcr6A==} engines: {node: '>=18'} + '@tanstack/vue-store@0.9.1': + resolution: {integrity: sha512-mXXZzPWom656MExX2gG1fqopJhToDbqGEl98WtJ5/hyouQHtQXiAgtsPNLzUcVcwU9okM/OCWv7QAgXf6C5ziQ==} + peerDependencies: + '@vue/composition-api': ^1.2.1 + vue: ^2.5.0 || ^3.0.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} @@ -2150,6 +2303,13 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + '@vitest/expect@4.0.18': resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} @@ -2179,6 +2339,38 @@ packages: '@vitest/utils@4.0.18': resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@vue/compiler-core@3.5.28': + resolution: {integrity: sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ==} + + '@vue/compiler-dom@3.5.28': + resolution: {integrity: sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA==} + + '@vue/compiler-sfc@3.5.28': + resolution: {integrity: sha512-6TnKMiNkd6u6VeVDhZn/07KhEZuBSn43Wd2No5zaP5s3xm8IqFTHBj84HJah4UepSUJTro5SoqqlOY22FKY96g==} + + '@vue/compiler-ssr@3.5.28': + resolution: {integrity: sha512-JCq//9w1qmC6UGLWJX7RXzrGpKkroubey/ZFqTpvEIDJEKGgntuDMqkuWiZvzTzTA5h2qZvFBFHY7fAAa9475g==} + + '@vue/reactivity@3.5.28': + resolution: {integrity: sha512-gr5hEsxvn+RNyu9/9o1WtdYdwDjg5FgjUSBEkZWqgTKlo/fvwZ2+8W6AfKsc9YN2k/+iHYdS9vZYAhpi10kNaw==} + + '@vue/runtime-core@3.5.28': + resolution: {integrity: sha512-POVHTdbgnrBBIpnbYU4y7pOMNlPn2QVxVzkvEA2pEgvzbelQq4ZOUxbp2oiyo+BOtiYlm8Q44wShHJoBvDPAjQ==} + + '@vue/runtime-dom@3.5.28': + resolution: {integrity: sha512-4SXxSF8SXYMuhAIkT+eBRqOkWEfPu6nhccrzrkioA6l0boiq7sp18HCOov9qWJA5HML61kW8p/cB4MmBiG9dSA==} + + '@vue/server-renderer@3.5.28': + resolution: {integrity: sha512-pf+5ECKGj8fX95bNincbzJ6yp6nyzuLDhYZCeFxUNp8EBrQpPpQaLX3nNCp49+UbgbPun3CeVE+5CXVV1Xydfg==} + peerDependencies: + vue: 3.5.28 + + '@vue/shared@3.5.28': + resolution: {integrity: sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ==} + + '@vue/test-utils@2.4.6': + resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} + '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} @@ -2190,6 +2382,10 @@ packages: resolution: {integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==} hasBin: true + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2211,6 +2407,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -2219,6 +2419,10 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + ansis@4.2.0: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} @@ -2424,6 +2628,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + comment-parser@1.4.5: resolution: {integrity: sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==} engines: {node: '>= 12.0.0'} @@ -2434,6 +2642,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -2554,6 +2765,14 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} @@ -2565,6 +2784,9 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.0: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} @@ -2894,6 +3116,10 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + form-data@4.0.5: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} @@ -2955,6 +3181,11 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -3077,6 +3308,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -3201,6 +3435,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jake@10.9.4: resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} engines: {node: '>=10'} @@ -3214,6 +3451,15 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3300,6 +3546,9 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3375,6 +3624,10 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} + minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -3382,6 +3635,10 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -3428,6 +3685,11 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -3519,6 +3781,9 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} @@ -3546,6 +3811,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -3612,6 +3881,9 @@ packages: resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -3921,6 +4193,10 @@ packages: 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'} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -3928,6 +4204,10 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -4218,12 +4498,34 @@ packages: jsdom: optional: true + vue-component-type-helpers@2.2.12: + resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + vue-eslint-parser@10.4.0: resolution: {integrity: sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + vue@3.5.28: + resolution: {integrity: sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} @@ -4280,6 +4582,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -4959,6 +5265,15 @@ snapshots: dependencies: '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jest/diff-sequences@30.0.1': {} '@jest/get-type@30.1.0': {} @@ -5064,6 +5379,8 @@ snapshots: '@nx/nx-win32-x64-msvc@22.5.2': optional: true + '@one-ini/wasm@0.1.1': {} + '@oxc-project/types@0.112.0': {} '@oxc-resolver/binding-android-arm-eabi@11.18.0': @@ -5128,6 +5445,9 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.18.0': optional: true + '@pkgjs/parseargs@0.11.0': + optional: true + '@preact/preset-vite@2.10.3(@babel/core@7.29.0)(preact@10.28.4)(rollup@4.58.0)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2))': dependencies: '@babel/core': 7.29.0 @@ -5430,7 +5750,7 @@ snapshots: transitivePeerDependencies: - csstype - '@tanstack/devtools-utils@0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)': + '@tanstack/devtools-utils@0.3.0(@types/react@19.2.14)(csstype@3.2.3)(preact@10.28.4)(react@19.2.4)(solid-js@1.9.11)(vue@3.5.28(typescript@5.9.3))': dependencies: '@tanstack/devtools-ui': 0.4.4(csstype@3.2.3)(solid-js@1.9.11) optionalDependencies: @@ -5438,6 +5758,7 @@ snapshots: preact: 10.28.4 react: 19.2.4 solid-js: 1.9.11 + vue: 3.5.28(typescript@5.9.3) transitivePeerDependencies: - csstype @@ -5548,6 +5869,12 @@ snapshots: transitivePeerDependencies: - typescript + '@tanstack/vue-store@0.9.1(vue@3.5.28(typescript@5.9.3))': + dependencies: + '@tanstack/store': 0.9.1 + vue: 3.5.28(typescript@5.9.3) + vue-demi: 0.14.10(vue@3.5.28(typescript@5.9.3)) + '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.29.0 @@ -5831,6 +6158,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitejs/plugin-vue@5.2.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))': + dependencies: + vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(yaml@2.8.2) + vue: 3.5.28(typescript@5.9.3) + '@vitest/expect@4.0.18': dependencies: '@standard-schema/spec': 1.1.0 @@ -5870,6 +6202,65 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 + '@vue/compiler-core@3.5.28': + dependencies: + '@babel/parser': 7.29.0 + '@vue/shared': 3.5.28 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.28': + dependencies: + '@vue/compiler-core': 3.5.28 + '@vue/shared': 3.5.28 + + '@vue/compiler-sfc@3.5.28': + dependencies: + '@babel/parser': 7.29.0 + '@vue/compiler-core': 3.5.28 + '@vue/compiler-dom': 3.5.28 + '@vue/compiler-ssr': 3.5.28 + '@vue/shared': 3.5.28 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.28': + dependencies: + '@vue/compiler-dom': 3.5.28 + '@vue/shared': 3.5.28 + + '@vue/reactivity@3.5.28': + dependencies: + '@vue/shared': 3.5.28 + + '@vue/runtime-core@3.5.28': + dependencies: + '@vue/reactivity': 3.5.28 + '@vue/shared': 3.5.28 + + '@vue/runtime-dom@3.5.28': + dependencies: + '@vue/reactivity': 3.5.28 + '@vue/runtime-core': 3.5.28 + '@vue/shared': 3.5.28 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.28(vue@3.5.28(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.5.28 + '@vue/shared': 3.5.28 + vue: 3.5.28(typescript@5.9.3) + + '@vue/shared@3.5.28': {} + + '@vue/test-utils@2.4.6': + dependencies: + js-beautify: 1.15.4 + vue-component-type-helpers: 2.2.12 + '@yarnpkg/lockfile@1.1.0': {} '@yarnpkg/parsers@3.0.2': @@ -5881,6 +6272,8 @@ snapshots: dependencies: argparse: 2.0.1 + abbrev@2.0.0: {} + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -5898,12 +6291,16 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} + ansis@4.2.0: {} argparse@1.0.10: @@ -6114,12 +6511,19 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@10.0.1: {} + comment-parser@1.4.5: {} compare-versions@6.1.1: {} concat-map@0.0.1: {} + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + convert-source-map@2.0.0: {} cross-spawn@7.0.6: @@ -6243,6 +6647,15 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + + editorconfig@1.0.4: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.7.4 + ejs@3.1.10: dependencies: jake: 10.9.4 @@ -6251,6 +6664,8 @@ snapshots: emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} + empathic@2.0.0: {} encoding-sniffer@0.2.1: @@ -6697,6 +7112,11 @@ snapshots: dependencies: is-callable: 1.2.7 + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + form-data@4.0.5: dependencies: asynckit: 0.4.0 @@ -6768,6 +7188,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + globals@14.0.0: {} globals@15.15.0: {} @@ -6875,6 +7304,8 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -6992,6 +7423,12 @@ snapshots: isexe@2.0.0: {} + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jake@10.9.4: dependencies: async: 3.2.6 @@ -7007,6 +7444,16 @@ snapshots: jiti@2.6.1: {} + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.5.0 + js-cookie: 3.0.5 + nopt: 7.2.1 + + js-cookie@3.0.5: {} + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -7089,6 +7536,8 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 + lru-cache@10.4.3: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -7158,12 +7607,18 @@ snapshots: dependencies: brace-expansion: 2.0.2 + minimatch@9.0.1: + dependencies: + brace-expansion: 2.0.2 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 minimist@1.2.8: {} + minipass@7.1.3: {} + mri@1.2.0: {} ms@2.1.3: {} @@ -7193,6 +7648,10 @@ snapshots: node-releases@2.0.27: {} + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -7356,6 +7815,8 @@ snapshots: p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} + package-manager-detector@0.2.11: dependencies: quansync: 0.2.11 @@ -7383,6 +7844,11 @@ snapshots: path-key@3.1.1: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + path-type@4.0.0: {} pathe@2.0.3: {} @@ -7430,6 +7896,8 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + proto-list@1.2.4: {} + proxy-from-env@1.1.0: {} publint@0.3.17: @@ -7759,6 +8227,12 @@ snapshots: 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.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -7767,6 +8241,10 @@ snapshots: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + strip-bom@3.0.0: {} strip-indent@3.0.0: @@ -8053,6 +8531,12 @@ snapshots: - tsx - yaml + vue-component-type-helpers@2.2.12: {} + + vue-demi@0.14.10(vue@3.5.28(typescript@5.9.3)): + dependencies: + vue: 3.5.28(typescript@5.9.3) + vue-eslint-parser@10.4.0(eslint@9.39.3(jiti@2.6.1)): dependencies: debug: 4.4.3 @@ -8065,6 +8549,16 @@ snapshots: transitivePeerDependencies: - supports-color + vue@3.5.28(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.28 + '@vue/compiler-sfc': 3.5.28 + '@vue/runtime-dom': 3.5.28 + '@vue/server-renderer': 3.5.28(vue@3.5.28(typescript@5.9.3)) + '@vue/shared': 3.5.28 + optionalDependencies: + typescript: 5.9.3 + walk-up-path@4.0.0: {} wcwidth@1.0.1: @@ -8128,6 +8622,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + wrappy@1.0.2: {} ws@8.19.0: {}