diff --git a/main.go b/main.go index 5e53dcf..05b9560 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "os" "os/signal" "syscall" + "unsafe" "github.com/jchv/go-webview2" "github.com/mumur/driver-booster/internal/bridge" @@ -22,6 +23,51 @@ import ( //go:embed ui/* var uiFS embed.FS +var ( + user32 = syscall.NewLazyDLL("user32.dll") + procGetWindowLong = user32.NewProc("GetWindowLongW") + procSetWindowLong = user32.NewProc("SetWindowLongW") + procSetWindowPos = user32.NewProc("SetWindowPos") + procPostMessage = user32.NewProc("PostMessageW") + procShowWindow = user32.NewProc("ShowWindow") +) + +const ( + gwlStyle = -16 + wsCaption = 0x00C00000 + wsSysMenu = 0x00080000 + wsThickFrame = 0x00040000 + wsMinBox = 0x00020000 + wsMaxBox = 0x00010000 + wsVisible = 0x10000000 + wsPopup = 0x80000000 + + swpFrameChanged = 0x0020 + swpNoMove = 0x0002 + swpNoSize = 0x0001 + swpNoZOrder = 0x0004 + + wmSysCommand = 0x0112 + scMinimize = 0xF020 + scMaximize = 0xF030 + scRestore = 0xF120 + scClose = 0xF060 + + swMaximize = 3 + swRestore = 9 +) + +func removeWindowFrame(hwnd uintptr) { + gwlStyleUintptr := uintptr(0xFFFFFFF0) // GWL_STYLE = -16 as unsigned + style, _, _ := procGetWindowLong.Call(hwnd, gwlStyleUintptr) + // Remove caption (title bar) but keep thick frame (resizable) and min/max boxes + newStyle := (style &^ wsCaption) | wsThickFrame | wsMinBox | wsMaxBox + procSetWindowLong.Call(hwnd, gwlStyleUintptr, newStyle) + // Force redraw frame + procSetWindowPos.Call(hwnd, 0, 0, 0, 0, 0, + swpFrameChanged|swpNoMove|swpNoSize|swpNoZOrder) +} + func main() { // Start embedded HTTP server for UI assets uiContent, err := fs.Sub(uiFS, "ui") @@ -74,6 +120,24 @@ func main() { } defer w.Destroy() + // Remove native title bar to use our custom one + hwnd := uintptr(unsafe.Pointer(w.Window())) + removeWindowFrame(hwnd) + + // Bind window control functions for custom title bar buttons + w.Bind("windowMinimize", func() { + procPostMessage.Call(hwnd, wmSysCommand, scMinimize, 0) + }) + w.Bind("windowMaximize", func() { + procPostMessage.Call(hwnd, wmSysCommand, scMaximize, 0) + }) + w.Bind("windowRestore", func() { + procPostMessage.Call(hwnd, wmSysCommand, scRestore, 0) + }) + w.Bind("windowClose", func() { + procPostMessage.Call(hwnd, wmSysCommand, scClose, 0) + }) + // Bind Go functions to JS w.Bind("goGetSysInfo", func() string { info := b.SysInfo.Collect() diff --git a/ui/index.html b/ui/index.html index 15cb1db..d6deeed 100644 --- a/ui/index.html +++ b/ui/index.html @@ -8,8 +8,8 @@
- -
+ +
@@ -17,12 +17,23 @@ PRO
-
+
System Protected
+
+ + + +
diff --git a/ui/style.css b/ui/style.css index 7e7ac3d..1729551 100644 --- a/ui/style.css +++ b/ui/style.css @@ -67,31 +67,53 @@ body { /* ---- App Shell ---- */ .app { display:flex; flex-direction:column; height:100vh; background:var(--mica); } -/* ---- Title Bar (Win11 style) ---- */ +/* ---- Custom Title Bar (frameless window) ---- */ .titlebar { - display:flex; justify-content:space-between; align-items:center; - padding:10px 16px; + display:flex; align-items:center; + padding:0 0 0 12px; + height:38px; background:var(--bg); border-bottom:1px solid var(--border); flex-shrink:0; -webkit-app-region:drag; + user-select:none; } -.titlebar-left { display:flex; align-items:center; gap:10px; -webkit-app-region:no-drag; } -.app-title { display:flex; align-items:center; gap:8px; } -.app-name { font-size:13px; font-weight:600; color:var(--text); letter-spacing:.3px; } +.titlebar-left { display:flex; align-items:center; gap:8px; -webkit-app-region:no-drag; flex-shrink:0; } +.titlebar-center { flex:1; display:flex; align-items:center; justify-content:center; } +.app-title { display:flex; align-items:center; gap:6px; } +.app-name { font-size:12px; font-weight:600; color:var(--text); letter-spacing:.3px; } .app-edition { - font-size:10px; font-weight:600; color:var(--accent); - background:var(--accent-subtle); padding:1px 6px; + font-size:9px; font-weight:600; color:var(--accent); + background:var(--accent-subtle); padding:1px 5px; border-radius:var(--radius-sm); letter-spacing:.5px; } -.sys-status { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--text-sec); } +.sys-status { display:flex; align-items:center; gap:6px; font-size:11px; color:var(--text-dim); } .status-led { - width:8px; height:8px; border-radius:50%; + width:6px; height:6px; border-radius:50%; background:var(--green); box-shadow:0 0 6px var(--green); animation:pulse 2.5s ease-in-out infinite; } @keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}} +/* Window control buttons (Win11 style) */ +.titlebar-controls { + display:flex; align-items:stretch; + height:38px; flex-shrink:0; + -webkit-app-region:no-drag; +} +.win-btn { + width:46px; height:38px; + display:flex; align-items:center; justify-content:center; + background:transparent; border:none; + color:var(--text-sec); cursor:pointer; + transition:background .1s ease; + font-family:inherit; +} +.win-btn:hover { background:rgba(255,255,255,0.06); color:var(--text); } +.win-btn:active { background:rgba(255,255,255,0.04); } +.win-close:hover { background:#c42b1c; color:white; } +.win-close:active { background:#b52a1c; } + /* ---- Toolbar (Win11 Tabs / NavigationView) ---- */ .toolbar { display:flex; gap:2px; padding:6px 12px;