mirror of
https://github.com/uthvah/vaultguard.git
synced 2026-07-22 06:44:51 +00:00
Custom navbar (#8)
* Enhance description in manifest.json Updated the description to include AES-GCM encryption. * Custom navbar - background colour matching - custom window action buttons - obsidian frame detection (disabled on native)
This commit is contained in:
parent
d221521d8e
commit
9ca99c8478
2 changed files with 155 additions and 7 deletions
98
index.js
98
index.js
|
|
@ -779,6 +779,93 @@ class VaultGuardPlugin extends obsidian.Plugin {
|
|||
await this.app.vault.modify(file, decoded);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// WINDOW FRAME DETECTION & CONTROLS
|
||||
// ========================================================================
|
||||
|
||||
getFrameStyle() {
|
||||
// Detected from body classes set by Obsidian at startup — authoritative,
|
||||
// unlike app.vault.config which does not expose this in the plugin API.
|
||||
// is-frameless + is-hidden-frameless → 'hidden' (default)
|
||||
// is-frameless only → 'obsidian' (Obsidian custom bar)
|
||||
// neither → 'native' (OS titlebar)
|
||||
const body = document.body;
|
||||
if (!body.classList.contains('is-frameless')) return 'native';
|
||||
if (body.classList.contains('is-hidden-frameless')) return 'hidden';
|
||||
return 'obsidian';
|
||||
}
|
||||
|
||||
_platform() {
|
||||
try { return process.platform; } catch { return 'unknown'; }
|
||||
}
|
||||
|
||||
// Show a themed navbar for hidden and obsidian styles — native lets the OS handle it.
|
||||
get needsNavbar() {
|
||||
return this.getFrameStyle() !== 'native';
|
||||
}
|
||||
|
||||
// On non-macOS with hidden or obsidian frame, our navbar covers Electron's own
|
||||
// window controls (web content) — render replacements.
|
||||
// macOS: traffic lights are native OS overlays above all web content, always visible.
|
||||
get needsWindowControls() {
|
||||
return this.needsNavbar && this._platform() !== 'darwin';
|
||||
}
|
||||
|
||||
createWindowControls() {
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'vg-window-controls';
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
cls: 'vg-wc-min',
|
||||
label: 'Minimize',
|
||||
svg: '<svg viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"><line x1="1" y1="5" x2="9" y2="5"/></svg>',
|
||||
action: 'minimize',
|
||||
},
|
||||
{
|
||||
cls: 'vg-wc-max',
|
||||
label: 'Maximize',
|
||||
svg: '<svg viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"><rect x="1" y="1" width="8" height="8"/></svg>',
|
||||
action: 'maximize',
|
||||
},
|
||||
{
|
||||
cls: 'vg-wc-close',
|
||||
label: 'Close',
|
||||
svg: '<svg viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"><line x1="1" y1="1" x2="9" y2="9"/><line x1="9" y1="1" x2="1" y2="9"/></svg>',
|
||||
action: 'close',
|
||||
},
|
||||
];
|
||||
|
||||
for (const { cls, label, svg, action } of buttons) {
|
||||
const btn = bar.createEl('button', {
|
||||
cls: `vg-wc-btn ${cls}`,
|
||||
attr: { 'aria-label': label },
|
||||
});
|
||||
btn.innerHTML = svg;
|
||||
btn.addEventListener('click', () => this._winAction(action));
|
||||
}
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
||||
_winAction(action) {
|
||||
// Path 1: Electron remote module (Obsidian ≤ 0.x / some 1.x builds)
|
||||
try {
|
||||
const electron = window.require('electron');
|
||||
const win = electron.remote?.getCurrentWindow?.();
|
||||
if (win) {
|
||||
if (action === 'minimize') { win.minimize(); return; }
|
||||
if (action === 'maximize') { win.isMaximized() ? win.unmaximize() : win.maximize(); return; }
|
||||
if (action === 'close') { win.close(); return; }
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// Path 2: Obsidian command fallback (close only)
|
||||
if (action === 'close') {
|
||||
try { this.app.commands.executeCommandById('app:quit'); } catch {}
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// LOCK SCREEN UI
|
||||
// ========================================================================
|
||||
|
|
@ -801,7 +888,10 @@ class VaultGuardPlugin extends obsidian.Plugin {
|
|||
}
|
||||
|
||||
_buildLockScreenUI(el) {
|
||||
el.createDiv({ cls: 'vaultguard-titlebar' });
|
||||
if (this.needsNavbar) {
|
||||
const navbar = el.createDiv({ cls: 'vaultguard-navbar' });
|
||||
if (this.needsWindowControls) navbar.appendChild(this.createWindowControls());
|
||||
}
|
||||
const mainContentEl = el.createDiv({ cls: 'vaultguard-main-content' });
|
||||
mainContentEl.appendChild(this.createBackgroundElement());
|
||||
mainContentEl.appendChild(this.createOverlayElement());
|
||||
|
|
@ -822,8 +912,10 @@ class VaultGuardPlugin extends obsidian.Plugin {
|
|||
}
|
||||
|
||||
_buildTamperedUI(el) {
|
||||
// Titlebar so the window remains draggable on desktop
|
||||
el.createDiv({ cls: 'vaultguard-titlebar' });
|
||||
if (this.needsNavbar) {
|
||||
const navbar = el.createDiv({ cls: 'vaultguard-navbar' });
|
||||
if (this.needsWindowControls) navbar.appendChild(this.createWindowControls());
|
||||
}
|
||||
|
||||
const screen = el.createDiv({ cls: 'vaultguard-tamper-screen' });
|
||||
const panel = screen.createDiv({ cls: 'vaultguard-tamper-panel' });
|
||||
|
|
|
|||
64
styles.css
64
styles.css
|
|
@ -42,16 +42,72 @@
|
|||
}
|
||||
|
||||
/* ============================================================================
|
||||
TITLEBAR (for window dragging)
|
||||
NAVBAR — shown for hidden and obsidian frame styles.
|
||||
Matches Obsidian's titlebar background so it blends seamlessly.
|
||||
native frame: omitted entirely (OS chrome handles it).
|
||||
============================================================================ */
|
||||
|
||||
.vaultguard-titlebar {
|
||||
.vaultguard-navbar {
|
||||
flex-shrink: 0;
|
||||
height: 30px;
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
-webkit-app-region: drag;
|
||||
background: transparent;
|
||||
background-color: var(--titlebar-background, var(--background-secondary));
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Match Obsidian's own focused-window titlebar colour */
|
||||
body.is-focused .vaultguard-navbar {
|
||||
background-color: var(--titlebar-background-focused, var(--background-secondary-alt));
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
WINDOW CONTROLS — rendered inside .vaultguard-navbar on Windows/Linux
|
||||
where Electron's own controls are web content covered by the lockscreen.
|
||||
macOS: traffic lights are native OS overlays — always visible, never needed here.
|
||||
============================================================================ */
|
||||
|
||||
.vg-window-controls {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.vg-wc-btn {
|
||||
width: 46px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--titlebar-text-color, rgba(255, 255, 255, 0.55));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.1s ease, color 0.1s ease;
|
||||
padding: 0;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.vg-wc-btn svg {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.vg-wc-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: var(--titlebar-text-color, white);
|
||||
}
|
||||
|
||||
.vg-wc-close:hover {
|
||||
background: #c42b1c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
|
|
|
|||
Loading…
Reference in a new issue