| | |
| | | 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' |
| | | |
| | |
| | | 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[]>([]) |
| | |
| | | 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() { |
| | |
| | | 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 {} |
| | |
| | | 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 {} |
| | | } |
| | | |
| | |
| | | <!-- 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 --> |
| | |
| | | {#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> |
| | | |
| | |
| | | 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); |