feat: implement enhanced Linux hotkey handling and add command copying functionality

This commit is contained in:
jahruz67
2026-05-20 15:13:52 -07:00
parent 181e663de4
commit 910877d35e
6 changed files with 132 additions and 36 deletions
+12 -1
View File
@@ -632,6 +632,7 @@ func (a *App) startupHeadless() {
}
// Initialize Hotkey Listener
if shouldStartBuiltInHotkeyListener() {
a.hotkeyListener = hotkey.NewListener(a.config.Shortcut, a.StartRecording, a.StopRecording)
if runtime.GOOS != "windows" {
a.hotkeyListener.SetRegistrationErrorCallback(func(err error) {
@@ -639,18 +640,28 @@ func (a *App) startupHeadless() {
go func() {
time.Sleep(2 * time.Second)
if a.overlay != nil {
a.overlay.Show("Shortcut registration failed. Please add a custom system shortcut calling 'wis-free-v3 --action=toggle' as a fallback.")
a.overlay.Show("Shortcut registration failed. Add a custom system shortcut with the command shown in Settings.")
}
}()
})
}
a.hotkeyListener.Start()
} else {
logger.Info("Linux portal hotkey disabled; use the --press command from Settings for GNOME shortcuts")
}
logger.Info("Components initialized successfully!")
logger.Info("Basic app components loaded, continuing startup...")
}
func shouldStartBuiltInHotkeyListener() bool {
if runtime.GOOS != "linux" {
return true
}
return os.Getenv("WISFREE_USE_PORTAL_HOTKEY") == "1"
}
// Shutdown cleans up resources
func (a *App) Shutdown(ctx context.Context) {
if a.hotkeyListener != nil {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -6,7 +6,7 @@
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Wisp Settings</title>
<script type="module" crossorigin src="/assets/index.fbf781c1.js"></script>
<script type="module" crossorigin src="/assets/index.3a064eef.js"></script>
<link rel="stylesheet" href="/assets/index.6e77aa4d.css">
</head>
@@ -54,8 +54,8 @@
<button onclick="copyLinuxPressCommand()">Copy</button>
</div>
</div>
<p class="hint">GNOME: Settings -> Keyboard -> Custom Shortcuts -> Add a shortcut using the copied command.</p>
<p class="hint">KDE: System Settings -> Shortcuts -> Command/URL -> Add a shortcut using the copied command.</p>
<p class="hint">GNOME: Settings -> Keyboard -> Custom Shortcuts -> Add a shortcut with the copied command.</p>
<p class="hint">KDE: System Settings -> Shortcuts -> Command/URL -> Add a shortcut with the copied command.</p>
</div>
<!-- Microphone -->
+29 -1
View File
@@ -333,7 +333,16 @@
const settings = await window.go.main.App.GetSettings();
document.getElementById('apiKey').value = settings.api_key || '';
document.getElementById('shortcutInput').value = settings.shortcut || 'alt+z';
const shortcutInput = document.getElementById('shortcutInput');
if (shortcutInput) {
shortcutInput.value = settings.shortcut || 'alt+z';
}
if (settings.linux_press_mode) {
const section = document.getElementById('linuxPressSection');
const cmdInput = document.getElementById('linuxPressCommand');
if (section) section.style.display = 'block';
if (cmdInput) cmdInput.value = settings.linux_press_command || '';
}
document.getElementById('whisperModel').value = settings.whisper_model || 'whisper-large-v3-turbo';
document.getElementById('aiModel').value = settings.ai_model || 'llama-3.3-70b-versatile';
document.getElementById('aiPrompt').value = settings.ai_prompt || '';
@@ -407,6 +416,25 @@
showSaveStatus('Prompt saved');
};
window.copyLinuxPressCommand = async function () {
const cmdInput = document.getElementById('linuxPressCommand');
if (!cmdInput) return;
const command = cmdInput.value || '';
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(command);
} else {
cmdInput.focus();
cmdInput.select();
document.execCommand('copy');
}
showSaveStatus('Command copied');
} catch (err) {
console.error('Failed to copy Linux command:', err);
}
};
window.toggleStartup = async function () {
await window.go.main.App.ToggleStartup(document.getElementById('startupToggle').checked);
};
+67 -10
View File
@@ -14,8 +14,20 @@ const linuxPressAddr = "127.0.0.1:9876"
var (
linuxPressMu sync.Mutex
linuxPressTimer *time.Timer
linuxPressReleaseTimer *time.Timer
linuxPressDetectTimer *time.Timer
linuxPressRecording bool
linuxPressHoldMode bool
linuxPressDetectingHold bool
)
const (
// GNOME custom shortcuts commonly auto-repeat while the key is held.
// If we see a second ping quickly, treat the shortcut as push-to-talk.
linuxPressHoldDetectWindow = 1200 * time.Millisecond
// Once hold mode is confirmed, lack of fresh pings means the key was released.
linuxPressReleaseGrace = 450 * time.Millisecond
)
func init() {
@@ -58,26 +70,71 @@ func (a *App) startLinuxPressDaemon() {
if !linuxPressRecording {
linuxPressRecording = true
linuxPressHoldMode = false
linuxPressDetectingHold = true
go a.StartRecording()
}
if linuxPressTimer != nil {
linuxPressTimer.Stop()
if linuxPressDetectTimer != nil {
linuxPressDetectTimer.Stop()
}
linuxPressTimer = time.AfterFunc(300*time.Millisecond, func() {
linuxPressDetectTimer = time.AfterFunc(linuxPressHoldDetectWindow, func() {
linuxPressMu.Lock()
defer linuxPressMu.Unlock()
if linuxPressRecording {
linuxPressRecording = false
go a.StopRecording()
}
linuxPressDetectingHold = false
})
w.WriteHeader(http.StatusNoContent)
return
}
if linuxPressDetectingHold || linuxPressHoldMode {
linuxPressHoldMode = true
linuxPressDetectingHold = false
if linuxPressDetectTimer != nil {
linuxPressDetectTimer.Stop()
linuxPressDetectTimer = nil
}
resetLinuxPressReleaseTimer(a)
w.WriteHeader(http.StatusNoContent)
return
}
stopLinuxPressRecording(a)
w.WriteHeader(http.StatusNoContent)
})
go func() {
_ = http.ListenAndServe(linuxPressAddr, mux)
}()
}
func resetLinuxPressReleaseTimer(a *App) {
if linuxPressReleaseTimer != nil {
linuxPressReleaseTimer.Stop()
}
linuxPressReleaseTimer = time.AfterFunc(linuxPressReleaseGrace, func() {
linuxPressMu.Lock()
defer linuxPressMu.Unlock()
if linuxPressRecording && linuxPressHoldMode {
stopLinuxPressRecording(a)
}
})
}
func stopLinuxPressRecording(a *App) {
if linuxPressReleaseTimer != nil {
linuxPressReleaseTimer.Stop()
linuxPressReleaseTimer = nil
}
if linuxPressDetectTimer != nil {
linuxPressDetectTimer.Stop()
linuxPressDetectTimer = nil
}
if linuxPressRecording {
linuxPressRecording = false
linuxPressHoldMode = false
linuxPressDetectingHold = false
go a.StopRecording()
}
}