From 8f11df460a247dfcaafd84ce3046605724d11998 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Mon, 18 May 2026 17:20:11 +0800
Subject: [PATCH] feat: add hotkey input mode setting
---
VoiceSnapGo/frontend/src/components/settings/GeneralPage.svelte | 74 ++++++++++++++++++++++++++++++++++++-
1 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/VoiceSnapGo/frontend/src/components/settings/GeneralPage.svelte b/VoiceSnapGo/frontend/src/components/settings/GeneralPage.svelte
index 091a855..d54ca49 100755
--- a/VoiceSnapGo/frontend/src/components/settings/GeneralPage.svelte
+++ b/VoiceSnapGo/frontend/src/components/settings/GeneralPage.svelte
@@ -2,7 +2,7 @@
import { Call } from '@wailsio/runtime'
import ToggleSwitch from '../shared/ToggleSwitch.svelte'
import { t } from '../../lib/i18n'
- import { autoHide, soundFeedback, copyToClipboard, startAtLogin, hotkeyVK } from '../../lib/stores/config'
+ import { autoHide, soundFeedback, copyToClipboard, startAtLogin, hotkeyVK, hotkeyMode } from '../../lib/stores/config'
import { deviceName, engineStatus, engineHardwareInfo } from '../../lib/stores/app'
import { hotkeyName } from '../../lib/stores/indicator'
@@ -21,6 +21,7 @@
let status = $state('loading')
let hwInfo = $state('')
let currentKeyName = $state('Ctrl')
+ let hotkeyModeVal: 'tap' | 'hold' = $state('hold')
let isRecording = $state(false)
let hintText = $state('')
let devices = $state<InputDevice[]>([])
@@ -34,6 +35,7 @@
const unsub6 = hotkeyName.subscribe(k => { currentKeyName = k })
const unsub7 = soundFeedback.subscribe(v => { soundFeedbackVal = v })
const unsub8 = copyToClipboard.subscribe(v => { copyToClipboardVal = v })
+ const unsub9 = hotkeyMode.subscribe(v => { hotkeyModeVal = v })
// Load actual device name from backend on mount
async function loadDeviceName() {
@@ -69,6 +71,12 @@
copyToClipboardVal = copy
} catch {}
try {
+ const mode: any = await Call.ByName('voicesnap/services.ConfigService.GetHotkeyMode')
+ const normalized = mode === 'tap' ? 'tap' : 'hold'
+ hotkeyMode.set(normalized)
+ hotkeyModeVal = normalized
+ } catch {}
+ try {
const hideDock: any = await Call.ByName('voicesnap/services.ConfigService.GetHideDockIcon')
hideDockIconVal = !!hideDock
} catch {}
@@ -94,6 +102,14 @@
copyToClipboard.set(checked)
try {
await Call.ByName('voicesnap/services.ConfigService.SetCopyToClipboard', checked)
+ } catch {}
+ }
+
+ async function onHotkeyModeChange(mode: 'tap' | 'hold') {
+ hotkeyMode.set(mode)
+ hotkeyModeVal = mode
+ try {
+ await Call.ByName('voicesnap/services.ConfigService.SetHotkeyMode', mode)
} catch {}
}
@@ -212,7 +228,7 @@
<!-- Header -->
<div class="header">
<h1 class="tagline">{t('home.tagline')}</h1>
- <p class="app-desc">{t('home.modeHint')}</p>
+ <p class="app-desc">{hotkeyModeVal === 'tap' ? t('home.modeHintTap') : t('home.modeHintHold')}</p>
</div>
<!-- Hotkey -->
@@ -232,6 +248,31 @@
{#if hintText}
<p class="hint" class:recording={isRecording}>{hintText}</p>
{/if}
+
+ <div class="divider"></div>
+
+ <div class="setting-row">
+ <div class="setting-info">
+ <span class="setting-label">{t('settings.hotkeyMode')}</span>
+ <span class="setting-desc">
+ {hotkeyModeVal === 'tap' ? t('settings.hotkeyModeTapDesc') : t('settings.hotkeyModeHoldDesc')}
+ </span>
+ </div>
+ <div class="segmented" role="group" aria-label={t('settings.hotkeyMode')}>
+ <button
+ class:active={hotkeyModeVal === 'tap'}
+ onclick={() => onHotkeyModeChange('tap')}
+ >
+ {t('settings.hotkeyModeTap')}
+ </button>
+ <button
+ class:active={hotkeyModeVal === 'hold'}
+ onclick={() => onHotkeyModeChange('hold')}
+ >
+ {t('settings.hotkeyModeHold')}
+ </button>
+ </div>
+ </div>
</div>
@@ -476,6 +517,35 @@
color: var(--color-tertiary-label);
}
+ .segmented {
+ display: inline-grid;
+ grid-template-columns: 1fr 1fr;
+ min-width: 148px;
+ padding: 2px;
+ border-radius: var(--radius-sm);
+ background: var(--color-bg-secondary);
+ flex-shrink: 0;
+ }
+
+ .segmented button {
+ min-width: 66px;
+ height: 30px;
+ padding: 0 12px;
+ border: none;
+ border-radius: calc(var(--radius-sm) - 2px);
+ background: transparent;
+ color: var(--color-secondary-label);
+ font-size: var(--font-size-sm);
+ font-weight: 500;
+ cursor: pointer;
+ transition: background var(--transition-fast), color var(--transition-fast);
+ }
+
+ .segmented button.active {
+ background: var(--color-blue);
+ color: white;
+ }
+
.divider {
height: 1px;
background: var(--color-separator);
--
Gitblit v1.9.3