mirror of
https://github.com/jahruz67/wisp-open.git
synced 2026-08-08 18:14:08 +00:00
feat: implement enhanced Linux hotkey handling and add command copying functionality
This commit is contained in:
@@ -632,25 +632,36 @@ func (a *App) startupHeadless() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Hotkey Listener
|
// Initialize Hotkey Listener
|
||||||
a.hotkeyListener = hotkey.NewListener(a.config.Shortcut, a.StartRecording, a.StopRecording)
|
if shouldStartBuiltInHotkeyListener() {
|
||||||
if runtime.GOOS != "windows" {
|
a.hotkeyListener = hotkey.NewListener(a.config.Shortcut, a.StartRecording, a.StopRecording)
|
||||||
a.hotkeyListener.SetRegistrationErrorCallback(func(err error) {
|
if runtime.GOOS != "windows" {
|
||||||
logger.Error("Linux hotkey registration failed: %v", err)
|
a.hotkeyListener.SetRegistrationErrorCallback(func(err error) {
|
||||||
go func() {
|
logger.Error("Linux hotkey registration failed: %v", err)
|
||||||
time.Sleep(2 * time.Second)
|
go func() {
|
||||||
if a.overlay != nil {
|
time.Sleep(2 * time.Second)
|
||||||
a.overlay.Show("Shortcut registration failed. Please add a custom system shortcut calling 'wis-free-v3 --action=toggle' as a fallback.")
|
if a.overlay != nil {
|
||||||
}
|
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")
|
||||||
}
|
}
|
||||||
a.hotkeyListener.Start()
|
|
||||||
|
|
||||||
logger.Info("Components initialized successfully!")
|
logger.Info("Components initialized successfully!")
|
||||||
|
|
||||||
logger.Info("Basic app components loaded, continuing startup...")
|
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
|
// Shutdown cleans up resources
|
||||||
func (a *App) Shutdown(ctx context.Context) {
|
func (a *App) Shutdown(ctx context.Context) {
|
||||||
if a.hotkeyListener != nil {
|
if a.hotkeyListener != nil {
|
||||||
|
|||||||
+3
File diff suppressed because one or more lines are too long
-3
File diff suppressed because one or more lines are too long
Vendored
+4
-4
@@ -6,7 +6,7 @@
|
|||||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||||
<title>Wisp Settings</title>
|
<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">
|
<link rel="stylesheet" href="/assets/index.6e77aa4d.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -54,8 +54,8 @@
|
|||||||
<button onclick="copyLinuxPressCommand()">Copy</button>
|
<button onclick="copyLinuxPressCommand()">Copy</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint">GNOME: Settings -> Keyboard -> Custom Shortcuts -> 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 using the copied command.</p>
|
<p class="hint">KDE: System Settings -> Shortcuts -> Command/URL -> Add a shortcut with the copied command.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Microphone -->
|
<!-- Microphone -->
|
||||||
@@ -189,4 +189,4 @@
|
|||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+30
-2
@@ -333,7 +333,16 @@
|
|||||||
const settings = await window.go.main.App.GetSettings();
|
const settings = await window.go.main.App.GetSettings();
|
||||||
|
|
||||||
document.getElementById('apiKey').value = settings.api_key || '';
|
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('whisperModel').value = settings.whisper_model || 'whisper-large-v3-turbo';
|
||||||
document.getElementById('aiModel').value = settings.ai_model || 'llama-3.3-70b-versatile';
|
document.getElementById('aiModel').value = settings.ai_model || 'llama-3.3-70b-versatile';
|
||||||
document.getElementById('aiPrompt').value = settings.ai_prompt || '';
|
document.getElementById('aiPrompt').value = settings.ai_prompt || '';
|
||||||
@@ -407,6 +416,25 @@
|
|||||||
showSaveStatus('Prompt saved');
|
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 () {
|
window.toggleStartup = async function () {
|
||||||
await window.go.main.App.ToggleStartup(document.getElementById('startupToggle').checked);
|
await window.go.main.App.ToggleStartup(document.getElementById('startupToggle').checked);
|
||||||
};
|
};
|
||||||
@@ -560,4 +588,4 @@
|
|||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+72
-15
@@ -13,9 +13,21 @@ import (
|
|||||||
const linuxPressAddr = "127.0.0.1:9876"
|
const linuxPressAddr = "127.0.0.1:9876"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
linuxPressMu sync.Mutex
|
linuxPressMu sync.Mutex
|
||||||
linuxPressTimer *time.Timer
|
linuxPressReleaseTimer *time.Timer
|
||||||
linuxPressRecording bool
|
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() {
|
func init() {
|
||||||
@@ -58,22 +70,36 @@ func (a *App) startLinuxPressDaemon() {
|
|||||||
|
|
||||||
if !linuxPressRecording {
|
if !linuxPressRecording {
|
||||||
linuxPressRecording = true
|
linuxPressRecording = true
|
||||||
|
linuxPressHoldMode = false
|
||||||
|
linuxPressDetectingHold = true
|
||||||
go a.StartRecording()
|
go a.StartRecording()
|
||||||
}
|
|
||||||
|
|
||||||
if linuxPressTimer != nil {
|
if linuxPressDetectTimer != nil {
|
||||||
linuxPressTimer.Stop()
|
linuxPressDetectTimer.Stop()
|
||||||
}
|
|
||||||
|
|
||||||
linuxPressTimer = time.AfterFunc(300*time.Millisecond, func() {
|
|
||||||
linuxPressMu.Lock()
|
|
||||||
defer linuxPressMu.Unlock()
|
|
||||||
if linuxPressRecording {
|
|
||||||
linuxPressRecording = false
|
|
||||||
go a.StopRecording()
|
|
||||||
}
|
}
|
||||||
})
|
linuxPressDetectTimer = time.AfterFunc(linuxPressHoldDetectWindow, func() {
|
||||||
|
linuxPressMu.Lock()
|
||||||
|
defer linuxPressMu.Unlock()
|
||||||
|
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)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -81,3 +107,34 @@ func (a *App) startLinuxPressDaemon() {
|
|||||||
_ = http.ListenAndServe(linuxPressAddr, mux)
|
_ = 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user