feat(desktop): macOS vibrancy for the left sidebar with a toggle
Add native macOS vibrancy behind the left sidebar (the only translucent surface; header/chat/right sidebar stay opaque), plus a setting to turn it off. - Window created with vibrancy applied after first show (avoids the cold-launch no-composite quirk); minimize/restore suppress the frost during the genie animation. Renderer frosts the sidebar via --sidebar-vibrancy-overlay once data-oc-vibrancy[-ready] are set; project-actions pill matches when open. - data-oc-vibrancy-ready defaults are set in cssGenerator (DOM guaranteed), not the preload (document-start race left the sidebar un-frosted on launch). - prefers-reduced-transparency falls back to solid surfaces. - Appearance settings (macOS desktop only): a checkbox to enable/disable vibrancy, persisted to settings.json and applied via a Save & restart button (vibrancy is a window-creation option, so it needs a relaunch).
This commit is contained in:
@@ -17,6 +17,10 @@ const clientToken = readArgValue('--openchamber-client-token');
|
||||
const homeDirectory = readArgValue('--openchamber-home');
|
||||
const macosMajorRaw = readArgValue('--openchamber-macos-major');
|
||||
const macosMajor = Number.parseInt(macosMajorRaw, 10);
|
||||
const macVibrancySupported = process.platform === 'darwin';
|
||||
// Effective state for this window (main process resolves the saved preference
|
||||
// and passes it in). Defaults on when supported unless explicitly '0'.
|
||||
const hasMacVibrancy = macVibrancySupported && readArgValue('--openchamber-mac-vibrancy') !== '0';
|
||||
|
||||
// Preload re-executes on every cross-origin navigation (we run with
|
||||
// sandbox:false, per-document). Two separate concerns to balance:
|
||||
@@ -72,6 +76,8 @@ if (Number.isFinite(macosMajor) && macosMajor > 0) {
|
||||
|
||||
contextBridge.exposeInMainWorld('__OPENCHAMBER_ELECTRON__', {
|
||||
runtime: 'electron',
|
||||
macVibrancy: hasMacVibrancy,
|
||||
macVibrancySupported,
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('__OPENCHAMBER_PLATFORM__', process.platform);
|
||||
@@ -120,6 +126,18 @@ const dispatchNativeEvent = (event, detail) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Toggles the frost on/off in response to the main process around the
|
||||
// minimize/restore cycle. The default ("ready") state is set reliably in the
|
||||
// renderer (cssGenerator) — not here — because this preload runs at
|
||||
// document-start when documentElement may not exist yet.
|
||||
const setVibrancyReady = (ready) => {
|
||||
if (!hasMacVibrancy) return;
|
||||
try {
|
||||
document.documentElement.toggleAttribute('data-oc-vibrancy-ready', ready === true);
|
||||
} catch {
|
||||
}
|
||||
};
|
||||
|
||||
// Main-process events are read-only notifications (update progress,
|
||||
// window focus, etc.) — safe to deliver to any page rendered in this
|
||||
// webContents. The events themselves don't grant capability.
|
||||
@@ -133,6 +151,10 @@ ipcRenderer.on('openchamber:emit', (_evt, payload) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event === 'openchamber:vibrancy-ready') {
|
||||
setVibrancyReady(payload.detail?.ready === true);
|
||||
}
|
||||
|
||||
dispatchNativeEvent(event, payload.detail);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user