diff --git a/app.go b/app.go
index 98ae68c..e33cf9f 100644
--- a/app.go
+++ b/app.go
@@ -5,10 +5,10 @@ import (
"fmt"
"os"
"path/filepath"
+ "runtime"
"strings"
"time"
-
"wis-free-v3/internal/audio/recorder"
"wis-free-v3/internal/config"
"wis-free-v3/internal/hotkey"
@@ -19,7 +19,7 @@ import (
"wis-free-v3/internal/ui/tray"
"github.com/go-vgo/robotgo"
- "github.com/wailsapp/wails/v2/pkg/runtime"
+ wailsruntime "github.com/wailsapp/wails/v2/pkg/runtime"
)
// App struct
@@ -46,10 +46,15 @@ func (a *App) GetConfig() *config.Config {
return a.config
}
+// Version returns the application version (see scripts/VERSION and build scripts).
+func (a *App) Version() string {
+ return AppVersion
+}
+
// ShowSettings shows the settings window
func (a *App) ShowSettings() {
if a.ctx != nil {
- runtime.WindowShow(a.ctx)
+ wailsruntime.WindowShow(a.ctx)
}
}
@@ -57,6 +62,15 @@ func (a *App) ShowSettings() {
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
+ title := appTitle
+ switch AppVersion {
+ case "", "dev":
+ title = appTitle + " (dev)"
+ default:
+ title = appTitle + " v" + AppVersion
+ }
+ wailsruntime.WindowSetTitle(ctx, title)
+
// Initialize components
a.startupHeadless()
@@ -69,7 +83,7 @@ func (a *App) beforeClose(ctx context.Context) (prevent bool) {
if a.isQuitting {
return false
}
- runtime.WindowHide(ctx)
+ wailsruntime.WindowHide(ctx)
return true
}
@@ -78,7 +92,7 @@ func (a *App) Quit() {
a.isQuitting = true
logger.Info("Application quitting...")
logger.Close()
- runtime.Quit(a.ctx)
+ wailsruntime.Quit(a.ctx)
}
// StartRecording starts the audio recording
@@ -235,10 +249,14 @@ func (a *App) processRecording() {
if len(a.config.History) > 50 {
a.config.History = a.config.History[:50]
}
- config.Save(a.config, "")
+ if err := config.Save(a.config, ""); err != nil {
+ logger.Error("Failed to save config after history update: %v", err)
+ } else if a.ctx != nil {
+ wailsruntime.EventsEmit(a.ctx, "history:updated")
+ }
// Copy to clipboard
- runtime.ClipboardSetText(a.ctx, refinedText)
+ wailsruntime.ClipboardSetText(a.ctx, refinedText)
// Paste
a.pasteText()
@@ -276,6 +294,7 @@ func (a *App) GetSettings() map[string]interface{} {
conf["microphone_device"] = a.config.MicrophoneDevice
conf["history"] = a.config.History
conf["startup"] = platform.IsInStartup()
+ conf["app_version"] = AppVersion
return conf
}
@@ -285,11 +304,13 @@ func (a *App) SaveSettings(settings map[string]interface{}) string {
a.config.APIKey = val
}
if val, ok := settings["shortcut"].(string); ok {
- // Validate shortcut before applying
- _, _, ok := hotkey.ParseShortcut(val)
+ _, _, modOnly, ok := hotkey.ParseShortcut(val)
if !ok {
logger.Error("Invalid shortcut: %s (rejected)", val)
- return "Invalid shortcut - must have at least one modifier and a regular key"
+ return "Invalid shortcut - use modifiers plus a key (e.g. ctrl+k), or modifier-only on Windows (e.g. ctrl+win)"
+ }
+ if modOnly && runtime.GOOS != "windows" {
+ return "Modifier-only shortcuts (like ctrl+win) are only supported on Windows"
}
a.config.Shortcut = val
diff --git a/build/bin/wis-free-v3 b/build/bin/wis-free-v3.exe
old mode 100755
new mode 100644
similarity index 59%
rename from build/bin/wis-free-v3
rename to build/bin/wis-free-v3.exe
index 67a4faa..167f6a8
Binary files a/build/bin/wis-free-v3 and b/build/bin/wis-free-v3.exe differ
diff --git a/frontend/dist/assets/index-BNpC-7X0.js b/frontend/dist/assets/index-BNpC-7X0.js
new file mode 100644
index 0000000..b5292de
--- /dev/null
+++ b/frontend/dist/assets/index-BNpC-7X0.js
@@ -0,0 +1,3 @@
+(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=!1;window.toggleApiKey=function(){let t=document.getElementById(`apiKey`),n=document.getElementById(`eyeBtn`);e=!e,t.type=e?`text`:`password`,n.textContent=e?`🙈`:`👁️`},window.recordShortcut=function(){let e=document.getElementById(`recordBtn`),t=document.getElementById(`shortcutInput`),n=e=>e.metaKey||e.key===`Meta`||e.key===`OS`||e.code===`MetaLeft`||e.code===`MetaRight`;e.textContent=`Recording...`,e.disabled=!0,t.value=``;let r=``,i=e=>{e.preventDefault(),e.stopPropagation();let i=[];e.ctrlKey&&i.push(`ctrl`),e.altKey&&i.push(`alt`),e.shiftKey&&i.push(`shift`),n(e)&&i.push(`win`);let a=e.key.toLowerCase();a===`control`&&(a=`ctrl`),a===`alt`&&(a=`alt`),a===`shift`&&(a=`shift`),(a===`meta`||a===`os`)&&(a=`win`),(e.code===`MetaLeft`||e.code===`MetaRight`)&&(a=`win`),a===` `&&(a=`space`);let s=new Set(i);s.add(a);let c=Array.from(s).filter(e=>e!==a);c.push(a),r=c.join(`+`),t.value=r,[`ctrl`,`alt`,`shift`,`win`].includes(a)||o()},a=e=>{e.preventDefault(),e.stopPropagation(),r&&(r.split(`+`).every(e=>[`ctrl`,`alt`,`shift`,`win`].includes(e))&&r.includes(`+`)?setTimeout(o,100):!e.ctrlKey&&!e.altKey&&!e.shiftKey&&!n(e)&&o())},o=()=>{if(!r){e.textContent=`Record`,e.disabled=!1,s();return}window.go.main.App.SaveSettings({shortcut:r}).then(t=>{t&&t.indexOf(`Invalid shortcut`)!==-1&&alert(t),e.textContent=`Record`,e.disabled=!1,s()})},s=()=>{window.removeEventListener(`keydown`,i,!0),window.removeEventListener(`keyup`,a,!0)};window.addEventListener(`keydown`,i,!0),window.addEventListener(`keyup`,a,!0)};function t(e){let t=document.getElementById(`historyList`);if(!e||e.length===0){t.innerHTML=`
No history yet.
`;return}t.innerHTML=``,[...e].reverse().forEach(e=>{let n=document.createElement(`div`);n.className=`history-item`;let r=document.createElement(`span`);r.className=`history-time`,r.textContent=new Date(e.timestamp).toLocaleString(void 0,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`});let i=document.createElement(`div`);i.textContent=e.text,n.appendChild(r),n.appendChild(i),t.appendChild(n)})}async function n(){try{t((await window.go.main.App.GetSettings()).history||[])}catch(e){console.error(`Failed to refresh history:`,e)}}async function r(){try{let e=await window.go.main.App.GetSettings();document.getElementById(`apiKey`).value=e.api_key||``,document.getElementById(`shortcutInput`).value=e.shortcut||`alt+z`,document.getElementById(`whisperModel`).value=e.whisper_model||`whisper-large-v3-turbo`,document.getElementById(`aiModel`).value=e.ai_model||`llama-3.3-70b-versatile`,document.getElementById(`aiPrompt`).value=e.ai_prompt||``,document.getElementById(`language`).value=e.language||`en`,document.getElementById(`startupToggle`).checked=e.startup||!1;let n=e.app_version&&String(e.app_version).trim()||`dev`;document.getElementById(`appVersion`).textContent=n===`dev`?`dev`:`v`+n,t(e.history||[])}catch(e){console.error(`Failed to load settings:`,e)}}async function i(){try{let e=await window.go.main.App.GetMicrophones(),t=document.getElementById(`micDevice`);e.forEach(e=>{if(e.index!==-1){let n=document.createElement(`option`);n.value=e.index,n.textContent=e.name,t.appendChild(n)}})}catch(e){console.error(`Failed to load mics:`,e)}}window.saveApiKey=async function(){let e=document.getElementById(`apiKey`).value.trim();await window.go.main.App.SaveSettings({api_key:e}),a()};function a(e=`Settings Saved!`){let t=document.getElementById(`saveStatus`);t.textContent=e,t.classList.add(`show`),setTimeout(()=>t.classList.remove(`show`),2e3)}window.saveWhisperModel=async function(){await window.go.main.App.SaveSettings({whisper_model:document.getElementById(`whisperModel`).value}),a()},window.saveAiModel=async function(){await window.go.main.App.SaveSettings({ai_model:document.getElementById(`aiModel`).value}),a()},window.saveLanguage=async function(){await window.go.main.App.SaveSettings({language:document.getElementById(`language`).value}),a(`Language Saved!`)},window.saveMicDevice=async function(){let e=document.getElementById(`micDevice`).value;await window.go.main.App.SaveSettings({microphone_device:e===`default`?null:parseInt(e)}),a()},window.savePrompt=async function(){await window.go.main.App.SaveSettings({ai_prompt:document.getElementById(`aiPrompt`).value.trim()}),a(`Prompt Saved!`)},window.toggleStartup=async function(){await window.go.main.App.ToggleStartup(document.getElementById(`startupToggle`).checked)},window.clearHistory=async function(){confirm(`Clear all history?`)&&(await window.go.main.App.ClearHistory(),t([]))};async function o(){try{let e=await window.go.main.App.CheckOnline(),t=document.getElementById(`connectionStatus`);if(e)t.innerHTML=`🟢 Online - Cloud transcription available`,t.style.background=`rgba(34, 197, 94, 0.1)`,t.style.color=`#22c55e`;else{t.innerHTML=`🔴 Offline - Install local Whisper for transcription`,t.style.background=`rgba(239, 68, 68, 0.1)`,t.style.color=`#ef4444`;let e=document.getElementById(`aiModel`);document.getElementById(`whisperModel`);for(let t of e.options)t.value!==`None`&&!t.value.startsWith(`local-`)&&(t.disabled=!0,t.text.includes(`(offline)`)||(t.text+=` (offline)`))}}catch(e){console.error(`Connection check failed:`,e)}}async function s(){try{let e=await window.go.main.App.GetWhisperInfo(),t=document.getElementById(`whisperModel`),n=t.querySelector(`option[value^="local-"]`);if(n&&n.remove(),e.installed){document.getElementById(`whisperNotInstalled`).style.display=`none`,document.getElementById(`whisperInstalled`).style.display=`block`,document.getElementById(`installedModel`).textContent=e.model+` model`;let n=document.createElement(`option`);n.value=`local-`+e.model,n.textContent=`🖥️ Local - `+e.model+` (offline)`,n.style.fontWeight=`bold`,t.insertBefore(n,t.firstChild)}else document.getElementById(`whisperNotInstalled`).style.display=`block`,document.getElementById(`whisperInstalled`).style.display=`none`}catch(e){console.error(`Whisper status check failed:`,e)}}window.installWhisper=async function(){let e=document.getElementById(`whisperModelSelect`).value,t=document.getElementById(`installBtn`);t.disabled=!0,t.textContent=`Starting installation...`;try{let t=await window.go.main.App.InstallWhisper(e);alert(t+`
+
+A terminal window will open showing the installation progress. This may take several minutes depending on your internet speed.`),setTimeout(s,5e3)}catch(e){alert(`Installation failed: `+e)}t.disabled=!1,t.textContent=`🚀 Install Offline Whisper`},window.uninstallWhisper=async function(){if(confirm(`Are you sure you want to uninstall offline Whisper? This will delete the downloaded model.`))try{let e=await window.go.main.App.UninstallWhisper();alert(e),s()}catch(e){alert(`Uninstall failed: `+e)}};async function c(){try{if(navigator.userAgent.toLowerCase().includes(`linux`)){let e=document.getElementById(`offlineWhisperSection`);e&&(e.style.display=`none`);let t=document.getElementById(`startupLabel`);t&&(t.innerText=`Run on Linux OS Startup`)}}catch(e){console.error(`Failed to get environment:`,e)}if(await r(),await i(),await o(),await s(),window.runtime&&typeof window.runtime.EventsOn==`function`&&window.runtime.EventsOn(`history:updated`,function(){n()}),navigator.userAgent.toLowerCase().includes(`linux`)){let e=document.getElementById(`whisperModel`);e&&Array.from(e.options).forEach(e=>{e.value.startsWith(`local-`)&&e.remove()})}}c();
\ No newline at end of file
diff --git a/frontend/dist/assets/index-BsntdVbh.css b/frontend/dist/assets/index-BsntdVbh.css
new file mode 100644
index 0000000..a74adec
--- /dev/null
+++ b/frontend/dist/assets/index-BsntdVbh.css
@@ -0,0 +1 @@
+:root{--bg-color:#0b0f1a;--card-bg:#1e293bb3;--input-bg:#33415580;--text-color:#f8fafc;--text-muted:#94a3b8;--primary-color:#3b82f6;--primary-hover:#2563eb;--accent-color:#6366f1;--border-color:#4b556366;--success-color:#10b981;--glass-border:#ffffff1a;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}body{color:var(--text-color);letter-spacing:-.01em;background:radial-gradient(circle at 100% 0,#1e293b,#0b0f1a);min-height:100vh;margin:0;padding:30px;font-family:Inter,system-ui,-apple-system,sans-serif}.container{max-width:720px;margin:0 auto}h1{background:linear-gradient(135deg,#60a5fa,#a78bfa);-webkit-text-fill-color:transparent;-webkit-background-clip:text;background-clip:text;margin-bottom:30px;font-size:28px;font-weight:700}.section{background:var(--card-bg);-webkit-backdrop-filter:blur(12px);border:1px solid var(--glass-border);border-radius:16px;margin-bottom:24px;padding:24px;transition:transform .2s;box-shadow:0 10px 15px -3px #0003}.section:hover{border-color:#fff3}label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px;font-size:13px;font-weight:600;display:block}input[type=checkbox]{accent-color:var(--primary-color);cursor:pointer;width:18px;height:18px}input[type=text],input[type=password],select,textarea{background:var(--input-bg);border:1px solid var(--border-color);color:#fff;box-sizing:border-box;border-radius:10px;width:100%;padding:12px 14px;font-size:14px;transition:all .2s}select{appearance:none;background-image:url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");background-position:right 14px center;background-repeat:no-repeat;background-size:16px;padding-right:40px}select option{background:var(--bg-color);color:#fff}textarea{resize:vertical;min-height:80px}input:focus,select:focus,textarea:focus{border-color:var(--primary-color);background:#334155cc;outline:none;box-shadow:0 0 0 3px #3b82f633}.form-control{width:100%;max-width:480px}.input-wrapper{flex:1;min-width:0;position:relative}.input-wrapper input{box-sizing:border-box;width:100%;padding-right:44px;display:block}.eye-btn{cursor:pointer;opacity:.5;justify-content:center;align-items:center;font-size:16px;line-height:1;transition:opacity .2s;display:flex;position:absolute;top:50%;right:8px;transform:translateY(-50%);box-shadow:none!important;background:0 0!important;border:none!important;padding:4px!important}.eye-btn:hover{opacity:1;transform:translateY(-50%)scale(1.1)}.save-status{background:var(--success-color);color:#fff;opacity:0;z-index:1000;border-radius:12px;padding:12px 24px;font-weight:600;transition:all .3s cubic-bezier(.4,0,.2,1);position:fixed;bottom:30px;right:30px;transform:translateY(100px);box-shadow:0 10px 15px -3px #0003}.save-status.show{opacity:1;transform:translateY(0)}button{background-color:var(--primary-color);color:#fff;cursor:pointer;white-space:nowrap;border:none;border-radius:10px;flex-shrink:0;padding:10px 20px;font-size:14px;font-weight:600;transition:all .2s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0003}button:hover{background-color:var(--primary-hover);transform:translateY(-1px);box-shadow:0 10px 15px -3px #0000004d}button:active{transform:translateY(0)}button[style*="background: transparent"]{opacity:.7;text-decoration:underline;box-shadow:none!important;background:0 0!important}button[style*="background: transparent"]:hover{opacity:1}.flex-row{flex-wrap:nowrap;align-items:center;gap:12px;display:flex}.flex-between{justify-content:space-between;align-items:center;display:flex}.history-list{max-height:250px;margin-top:10px;padding-right:5px;overflow-y:auto}.history-item{background:#ffffff08;border:1px solid #ffffff0d;border-radius:12px;margin-bottom:10px;padding:14px;transition:all .2s}.history-item:hover{background:#ffffff0f;transform:translate(2px)}.history-time{color:var(--primary-color);margin-bottom:6px;font-size:11px;font-weight:700;display:block}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:10px}::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}
diff --git a/frontend/dist/assets/index.c6252eaf.js b/frontend/dist/assets/index.c6252eaf.js
deleted file mode 100644
index 1892917..0000000
--- a/frontend/dist/assets/index.c6252eaf.js
+++ /dev/null
@@ -1,3 +0,0 @@
-(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))a(n);new MutationObserver(n=>{for(const i of n)if(i.type==="childList")for(const r of i.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&a(r)}).observe(document,{childList:!0,subtree:!0});function o(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerpolicy&&(i.referrerPolicy=n.referrerpolicy),n.crossorigin==="use-credentials"?i.credentials="include":n.crossorigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function a(n){if(n.ep)return;n.ep=!0;const i=o(n);fetch(n.href,i)}})();let u=!1;window.toggleApiKey=function(){const e=document.getElementById("apiKey"),t=document.getElementById("eyeBtn");u=!u,e.type=u?"text":"password",t.textContent=u?"\u{1F648}":"\u{1F441}\uFE0F"};window.recordShortcut=function(){const e=document.getElementById("recordBtn"),t=document.getElementById("shortcutInput");e.textContent="Recording...",e.disabled=!0,t.value="";let o="";const a=s=>{s.preventDefault(),s.stopPropagation();const c=[];s.ctrlKey&&c.push("ctrl"),s.altKey&&c.push("alt"),s.shiftKey&&c.push("shift"),(s.metaKey||s.key==="Meta"||s.key==="OS")&&c.push("win");let l=s.key.toLowerCase();l==="control"&&(l="ctrl"),l==="alt"&&(l="alt"),l==="shift"&&(l="shift"),(l==="meta"||l==="os")&&(l="win"),l===" "&&(l="space");const p=new Set(c);p.add(l);const y=Array.from(p).filter(f=>f!==l);y.push(l),o=y.join("+"),t.value=o,["ctrl","alt","shift","win"].includes(l)||i()},n=s=>{s.preventDefault(),s.stopPropagation(),o&&(o.split("+").every(l=>["ctrl","alt","shift","win"].includes(l))&&o.includes("+")?setTimeout(i,100):!s.ctrlKey&&!s.altKey&&!s.shiftKey&&!s.metaKey&&i())},i=()=>{if(!o){e.textContent="Record",e.disabled=!1,r();return}window.go.main.App.SaveSettings({shortcut:o}).then(()=>{e.textContent="Record",e.disabled=!1,r()})},r=()=>{window.removeEventListener("keydown",a,!0),window.removeEventListener("keyup",n,!0)};window.addEventListener("keydown",a,!0),window.addEventListener("keyup",n,!0)};async function w(){try{const e=await window.go.main.App.GetSettings();document.getElementById("apiKey").value=e.api_key||"",document.getElementById("shortcutInput").value=e.shortcut||"alt+z",document.getElementById("whisperModel").value=e.whisper_model||"whisper-large-v3-turbo",document.getElementById("aiModel").value=e.ai_model||"llama-3.3-70b-versatile",document.getElementById("aiPrompt").value=e.ai_prompt||"",document.getElementById("language").value=e.language||"en",document.getElementById("startupToggle").checked=e.startup||!1;const t=e.history||[],o=document.getElementById("historyList");t.length>0&&(o.innerHTML="",[...t].reverse().forEach(a=>{const n=document.createElement("div");n.className="history-item";const i=document.createElement("span");i.className="history-time",i.textContent=new Date(a.timestamp).toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"});const r=document.createElement("div");r.textContent=a.text,n.appendChild(i),n.appendChild(r),o.appendChild(n)}))}catch(e){console.error("Failed to load settings:",e)}}async function g(){try{const e=await window.go.main.App.GetMicrophones(),t=document.getElementById("micDevice");e.forEach(o=>{if(o.index!==-1){const a=document.createElement("option");a.value=o.index,a.textContent=o.name,t.appendChild(a)}})}catch(e){console.error("Failed to load mics:",e)}}window.saveApiKey=async function(){const e=document.getElementById("apiKey").value.trim();await window.go.main.App.SaveSettings({api_key:e}),d()};function d(e="Settings Saved!"){const t=document.getElementById("saveStatus");t.textContent=e,t.classList.add("show"),setTimeout(()=>t.classList.remove("show"),2e3)}window.saveWhisperModel=async function(){await window.go.main.App.SaveSettings({whisper_model:document.getElementById("whisperModel").value}),d()};window.saveAiModel=async function(){await window.go.main.App.SaveSettings({ai_model:document.getElementById("aiModel").value}),d()};window.saveLanguage=async function(){await window.go.main.App.SaveSettings({language:document.getElementById("language").value}),d("Language Saved!")};window.saveMicDevice=async function(){const e=document.getElementById("micDevice").value;await window.go.main.App.SaveSettings({microphone_device:e==="default"?null:parseInt(e)}),d()};window.savePrompt=async function(){await window.go.main.App.SaveSettings({ai_prompt:document.getElementById("aiPrompt").value.trim()}),d("Prompt Saved!")};window.toggleStartup=async function(){await window.go.main.App.ToggleStartup(document.getElementById("startupToggle").checked)};window.clearHistory=async function(){confirm("Clear all history?")&&(await window.go.main.App.ClearHistory(),document.getElementById("historyList").innerHTML='No history yet.
')};async function h(){try{const e=await window.go.main.App.CheckOnline(),t=document.getElementById("connectionStatus");if(e)t.innerHTML="\u{1F7E2} Online - Cloud transcription available",t.style.background="rgba(34, 197, 94, 0.1)",t.style.color="#22c55e";else{t.innerHTML="\u{1F534} Offline - Install local Whisper for transcription",t.style.background="rgba(239, 68, 68, 0.1)",t.style.color="#ef4444";const o=document.getElementById("aiModel"),a=document.getElementById("whisperModel");for(let n of o.options)n.value!=="None"&&!n.value.startsWith("local-")&&(n.disabled=!0,n.text.includes("(offline)")||(n.text+=" (offline)"))}}catch(e){console.error("Connection check failed:",e)}}async function m(){try{const e=await window.go.main.App.GetWhisperInfo(),t=document.getElementById("whisperModel"),o=t.querySelector('option[value^="local-"]');if(o&&o.remove(),e.installed){document.getElementById("whisperNotInstalled").style.display="none",document.getElementById("whisperInstalled").style.display="block",document.getElementById("installedModel").textContent=e.model+" model";const a=document.createElement("option");a.value="local-"+e.model,a.textContent="\u{1F5A5}\uFE0F Local - "+e.model+" (offline)",a.style.fontWeight="bold",t.insertBefore(a,t.firstChild)}else document.getElementById("whisperNotInstalled").style.display="block",document.getElementById("whisperInstalled").style.display="none"}catch(e){console.error("Whisper status check failed:",e)}}window.installWhisper=async function(){const e=document.getElementById("whisperModelSelect").value,t=document.getElementById("installBtn");t.disabled=!0,t.textContent="Starting installation...";try{const o=await window.go.main.App.InstallWhisper(e);alert(o+`
-
-A terminal window will open showing the installation progress. This may take several minutes depending on your internet speed.`),setTimeout(m,5e3)}catch(o){alert("Installation failed: "+o)}t.disabled=!1,t.textContent="\u{1F680} Install Offline Whisper"};window.uninstallWhisper=async function(){if(!!confirm("Are you sure you want to uninstall offline Whisper? This will delete the downloaded model."))try{const e=await window.go.main.App.UninstallWhisper();alert(e),m()}catch(e){alert("Uninstall failed: "+e)}};async function v(){try{if(navigator.userAgent.toLowerCase().includes("linux")){const t=document.getElementById("offlineWhisperSection");t&&(t.style.display="none");const o=document.getElementById("startupLabel");o&&(o.innerText="Run on Linux OS Startup")}}catch(e){console.error("Failed to get environment:",e)}if(await w(),await g(),await h(),await m(),navigator.userAgent.toLowerCase().includes("linux")){const e=document.getElementById("whisperModel");e&&Array.from(e.options).forEach(t=>{t.value.startsWith("local-")&&t.remove()})}}v();
diff --git a/frontend/dist/assets/index.d8635aeb.css b/frontend/dist/assets/index.d8635aeb.css
deleted file mode 100644
index 31c34a4..0000000
--- a/frontend/dist/assets/index.d8635aeb.css
+++ /dev/null
@@ -1 +0,0 @@
-:root{--bg-color: #0b0f1a;--card-bg: rgba(30, 41, 59, .7);--input-bg: rgba(51, 65, 85, .5);--text-color: #f8fafc;--text-muted: #94a3b8;--primary-color: #3b82f6;--primary-hover: #2563eb;--accent-color: #6366f1;--border-color: rgba(75, 85, 99, .4);--success-color: #10b981;--glass-border: rgba(255, 255, 255, .1);color-scheme:dark}body{background:radial-gradient(circle at top right,#1e293b,#0b0f1a);color:var(--text-color);font-family:Inter,system-ui,-apple-system,sans-serif;margin:0;padding:30px;min-height:100vh;letter-spacing:-.01em}.container{max-width:720px;margin:0 auto}h1{font-size:28px;font-weight:700;background:linear-gradient(135deg,#60a5fa,#a78bfa);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;margin-bottom:30px}.section{background:var(--card-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);padding:24px;border-radius:16px;margin-bottom:24px;border:1px solid var(--glass-border);box-shadow:0 10px 15px -3px #0003;transition:transform .2s ease}.section:hover{border-color:#fff3}label{display:block;color:var(--text-muted);font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px}input[type=checkbox]{accent-color:var(--primary-color);width:18px;height:18px;cursor:pointer}input[type=text],input[type=password],select,textarea{width:100%;background:var(--input-bg);border:1px solid var(--border-color);color:#fff;padding:12px 14px;border-radius:10px;font-size:14px;transition:all .2s ease;box-sizing:border-box}select{appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 14px center;background-size:16px;padding-right:40px}select option{background:var(--bg-color);color:#fff}textarea{resize:vertical;min-height:80px}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--primary-color);background:rgba(51,65,85,.8);box-shadow:0 0 0 3px #3b82f633}.form-control{max-width:480px;width:100%}.input-wrapper{position:relative;flex:1;min-width:0}.input-wrapper input{width:100%;padding-right:44px;box-sizing:border-box;display:block}.eye-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:transparent!important;border:none!important;box-shadow:none!important;cursor:pointer;padding:4px!important;font-size:16px;opacity:.5;transition:opacity .2s;line-height:1;display:flex;align-items:center;justify-content:center}.eye-btn:hover{opacity:1;transform:translateY(-50%) scale(1.1)}.save-status{position:fixed;bottom:30px;right:30px;background:var(--success-color);color:#fff;padding:12px 24px;border-radius:12px;font-weight:600;box-shadow:0 10px 15px -3px #0003;transform:translateY(100px);opacity:0;transition:all .3s cubic-bezier(.4,0,.2,1);z-index:1000}.save-status.show{transform:translateY(0);opacity:1}button{background-color:var(--primary-color);color:#fff;border:none;padding:10px 20px;border-radius:10px;cursor:pointer;font-weight:600;font-size:14px;transition:all .2s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0003;white-space:nowrap;flex-shrink:0}button:hover{transform:translateY(-1px);box-shadow:0 10px 15px -3px #0000004d;background-color:var(--primary-hover)}button:active{transform:translateY(0)}button[style*="background: transparent"]{background:transparent!important;box-shadow:none!important;text-decoration:underline;opacity:.7}button[style*="background: transparent"]:hover{opacity:1}.flex-row{display:flex;gap:12px;align-items:center;flex-wrap:nowrap}.flex-between{display:flex;justify-content:space-between;align-items:center}.history-list{max-height:250px;overflow-y:auto;margin-top:10px;padding-right:5px}.history-item{background:rgba(255,255,255,.03);padding:14px;border-radius:12px;margin-bottom:10px;border:1px solid rgba(255,255,255,.05);transition:all .2s ease}.history-item:hover{background:rgba(255,255,255,.06);transform:translate(2px)}.history-time{color:var(--primary-color);font-size:11px;font-weight:700;margin-bottom:6px;display:block}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:10px}::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}
diff --git a/frontend/dist/index.html b/frontend/dist/index.html
index da016d0..bbfc781 100644
--- a/frontend/dist/index.html
+++ b/frontend/dist/index.html
@@ -5,9 +5,8 @@
wis-free-v3 Settings
-
-
-
+
+
@@ -15,7 +14,7 @@
wis-free-v3 Settings
-
v1.0.0
+
—
Settings Saved!
@@ -183,7 +182,6 @@
-