feat: 新增「猫咪设置 > 模型设置 > 按键自动释放延迟」配置项 (#725)

This commit is contained in:
ayangweb
2025-11-24 21:11:42 +08:00
committed by GitHub
parent a75c2d6a74
commit 29623577ff
7 changed files with 59 additions and 14 deletions

View File

@@ -23,7 +23,10 @@ const hasDescription = computed(() => {
justify="space-between"
:vertical="vertical"
>
<Flex align="center">
<Flex
align="center"
class="flex-1"
>
<Flex vertical>
<div class="text-sm font-medium">
{{ title }}

View File

@@ -1,7 +1,6 @@
import type { CursorPoint } from '@/utils/monitor'
import { invoke } from '@tauri-apps/api/core'
import { useDebounceFn } from '@vueuse/core'
import { isEqual, mapValues } from 'es-toolkit'
import { ref } from 'vue'
@@ -10,7 +9,9 @@ import { INVOKE_KEY, LISTEN_KEY } from '../constants'
import { useModel } from './useModel'
import { useTauriListen } from './useTauriListen'
import { useCatStore } from '@/stores/cat'
import { useModelStore } from '@/stores/model'
import { isWindows } from '@/utils/platform'
interface MouseButtonEvent {
kind: 'MousePress' | 'MouseRelease'
@@ -32,14 +33,14 @@ type DeviceEvent = MouseButtonEvent | MouseMoveEvent | KeyboardEvent
export function useDevice() {
const modelStore = useModelStore()
const lastCursorPoint = ref<CursorPoint>({ x: 0, y: 0 })
const releaseTimers = new Map<string, NodeJS.Timeout>()
const catStore = useCatStore()
const { handlePress, handleRelease, handleMouseChange, handleMouseMove } = useModel()
const startListening = () => {
invoke(INVOKE_KEY.START_DEVICE_LISTENING)
}
const debouncedRelease = useDebounceFn(handleRelease, 100)
const getSupportedKey = (key: string) => {
let nextKey = key
@@ -69,6 +70,22 @@ export function useDevice() {
return handleMouseMove(point)
}
const handleAutoRelease = (key: string, delay = 100) => {
handlePress(key)
if (releaseTimers.has(key)) {
clearTimeout(releaseTimers.get(key))
}
const timer = setTimeout(() => {
handleRelease(key)
releaseTimers.delete(key)
}, delay)
releaseTimers.set(key, timer)
}
useTauriListen<DeviceEvent>(LISTEN_KEY.DEVICE_CHANGED, ({ payload }) => {
const { kind, value } = payload
@@ -78,12 +95,16 @@ export function useDevice() {
if (!nextValue) return
if (nextValue === 'CapsLock') {
handlePress(nextValue)
return debouncedRelease(nextValue)
return handleAutoRelease(nextValue)
}
if (kind === 'KeyboardPress') {
if (isWindows) {
const delay = catStore.model.autoReleaseDelay * 1000
return handleAutoRelease(nextValue, delay)
}
return handlePress(nextValue)
}

View File

@@ -19,7 +19,8 @@
"alwaysOnTop": "Always On Top",
"windowSize": "Window Size",
"windowRadius": "Window Radius",
"opacity": "Opacity"
"opacity": "Opacity",
"autoReleaseDelay": "Auto Release Delay"
},
"hints": {
"mirrorMode": "When enabled, the model will be horizontally flipped.",
@@ -27,7 +28,8 @@
"mouseMirror": "When enabled, the mouse will mirror follow the hand movement.",
"passThrough": "When enabled, the window will not affect operations on other applications.",
"alwaysOnTop": "When enabled, the window will always stay above other applications.",
"windowSize": "Move the mouse to the edge of the window or hold Shift and right-drag to resize."
"windowSize": "Move the mouse to the edge of the window or hold Shift and right-drag to resize.",
"autoReleaseDelay": "Due to Windows not capturing the release events of certain system-level keys, they will be automatically treated as released after a timeout."
}
},
"general": {

View File

@@ -19,7 +19,8 @@
"alwaysOnTop": "Luôn trên cùng",
"windowSize": "Kích thước",
"windowRadius": "Độ bo tròn cửa sổ",
"opacity": "Độ mờ"
"opacity": "Độ mờ",
"autoReleaseDelay": "Độ trễ tự động nhả phím"
},
"hints": {
"mirrorMode": "Bật để lật ngang mô hình.",
@@ -27,7 +28,8 @@
"mouseMirror": "Khi bật, chuột của mô hình sẽ phản chiếu theo chuyển động chuột thực tế.",
"passThrough": "Bật để cửa sổ không ảnh hưởng đến thao tác trên ứng dụng khác.",
"alwaysOnTop": "Bật để cửa sổ luôn nằm trên ứng dụng khác.",
"windowSize": "Di chuyển chuột đến mép cửa sổ hoặc giữ Shift và kéo chuột phải để thay đổi kích thước."
"windowSize": "Di chuyển chuột đến mép cửa sổ hoặc giữ Shift và kéo chuột phải để thay đổi kích thước.",
"autoReleaseDelay": "Do Windows không bắt được sự kiện nhả của một số phím hệ thống, các phím đó sẽ được tự động xem như đã nhả sau khi hết thời gian chờ."
}
},
"general": {

View File

@@ -19,7 +19,8 @@
"alwaysOnTop": "窗口置顶",
"windowSize": "窗口尺寸",
"windowRadius": "窗口圆角",
"opacity": "不透明度"
"opacity": "不透明度",
"autoReleaseDelay": "按键自动释放延迟"
},
"hints": {
"mirrorMode": "启用后,模型将水平镜像翻转。",
@@ -27,7 +28,8 @@
"mouseMirror": "启用后,鼠标将镜像跟随手部移动。",
"passThrough": "启用后,窗口不影响对其他应用程序的操作。",
"alwaysOnTop": "启用后,窗口始终显示在其他应用程序上方。",
"windowSize": "将鼠标移至窗口边缘,或按住 Shift 并右键拖动,也可以调整窗口大小。"
"windowSize": "将鼠标移至窗口边缘,或按住 Shift 并右键拖动,也可以调整窗口大小。",
"autoReleaseDelay": "由于 Windows 下部分系统级按键无法捕获释放事件,超时后将自动视为已释放。"
}
},
"general": {

View File

@@ -4,6 +4,7 @@ import { InputNumber, Slider, Switch } from 'ant-design-vue'
import ProList from '@/components/pro-list/index.vue'
import ProListItem from '@/components/pro-list-item/index.vue'
import { useCatStore } from '@/stores/cat'
import { isWindows } from '@/utils/platform'
const catStore = useCatStore()
</script>
@@ -30,6 +31,18 @@ const catStore = useCatStore()
>
<Switch v-model:checked="catStore.model.mouseMirror" />
</ProListItem>
<ProListItem
v-if="isWindows"
:description="$t('pages.preference.cat.hints.autoReleaseDelay')"
:title="$t('pages.preference.cat.labels.autoReleaseDelay')"
>
<InputNumber
v-model:value="catStore.model.autoReleaseDelay"
addon-after="s"
class="w-28"
/>
</ProListItem>
</ProList>
<ProList :title="$t('pages.preference.cat.labels.windowSettings')">
@@ -74,7 +87,7 @@ const catStore = useCatStore()
>
<Slider
v-model:value="catStore.window.opacity"
class="m-0!"
class="m-[0]!"
:max="100"
:min="10"
:tip-formatter="(value) => `${value}%`"

View File

@@ -6,6 +6,7 @@ export interface CatStore {
mirror: boolean
single: boolean
mouseMirror: boolean
autoReleaseDelay: number
}
window: {
visible: boolean
@@ -48,6 +49,7 @@ export const useCatStore = defineStore('cat', () => {
mirror: false,
single: false,
mouseMirror: false,
autoReleaseDelay: 3,
})
const window = reactive<CatStore['window']>({