Download apps/shared/src/translucency.ts from SaylorTwift/hermes-agent: direct link, hf CLI and curl.
- Browser
- Download file 19.8 kB
-
https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/apps/shared/src/translucency.ts
- Command line
-
hf download hf://SaylorTwift/hermes-agent/apps/shared/src/translucency.ts
-
curl -L -o translucency.ts https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/apps/shared/src/translucency.ts
19.8 kB
| /** | |
| * Window translucency β the one place the main process and the renderer agree | |
| * on what the setting means. | |
| * | |
| * One lever, 0β100 (0 = off, the default). Two modes decide HOW the desktop | |
| * shows through: | |
| * | |
| * - 'clear' β the main process maps the lever to native window opacity | |
| * (`setOpacity`), so the whole window fades, text included. macOS + Windows; | |
| * `setOpacity` is a no-op on Linux. | |
| * - 'glass' β the window stays fully opaque at the native level and the | |
| * renderer thins its page surfaces instead, letting a platform material | |
| * read as a matte blur while text keeps full contrast. macOS rides | |
| * `setVibrancy`; Windows 11 (22H2+) rides `setBackgroundMaterial`. Linux | |
| * has no first-party desktop material, so glass is not offered there. | |
| * | |
| * The renderer owns the value and mirrors it to main over IPC; main persists it | |
| * so a cold launch can apply it at window creation, before the renderer reports | |
| * anything. | |
| */ | |
| export type TranslucencyMode = 'clear' | 'glass' | |
| /** | |
| * macOS vibrancy materials offered as glass "frost" levels, ordered sheer β | |
| * heavy. macOS exposes no blur-radius knob (VibrancyOptions is only an | |
| * animation duration), so the material IS the frost control: each maps to a | |
| * different NSVisualEffectView material with its own luminance lift. | |
| * | |
| * Curated by pixel census on macOS 26 (one window, visualEffectState pinned | |
| * to 'active', cycling all 14 materials over the same wallpaper): the 14 | |
| * collapse to 9 distinct looks (sidebarβ‘hud, windowβ‘fullscreen-ui, | |
| * tooltipβ‘contentβ‘under-windowβ‘under-page). These four are the ladder with | |
| * the widest separations that stay distinct in BOTH appearances β dark lum | |
| * 26/63/84/127, light lum 217/233/254/242. sidebar/hud sit 9 lum from | |
| * under-window when focused and collapse INTO it when unfocused, which | |
| * shipped as two indistinguishable picker options once β don't re-add them. | |
| */ | |
| export const GLASS_MATERIALS = ['under-window', 'popover', 'titlebar', 'header'] as const | |
| export type GlassMaterial = (typeof GLASS_MATERIALS)[number] | |
| export const DEFAULT_GLASS_MATERIAL: GlassMaterial = 'under-window' | |
| /** | |
| * Where the glass field lives. 'window' thins every field surface; 'sidebar' | |
| * is the Finder shape β glass rail, opaque content column. The scope is a | |
| * renderer concern (which surfaces thin); the main process only persists and | |
| * echoes it. | |
| */ | |
| export const GLASS_SCOPES = ['window', 'sidebar'] as const | |
| export type GlassScope = (typeof GLASS_SCOPES)[number] | |
| export const DEFAULT_GLASS_SCOPE: GlassScope = 'sidebar' | |
| /** | |
| * Electron `setBackgroundMaterial` values. `'auto'` is deliberately absent β | |
| * it lets DWM pick, which would silently erase the frost choice. | |
| */ | |
| export const WINDOWS_BACKGROUND_MATERIALS = ['acrylic', 'tabbed', 'mica', 'none'] as const | |
| export type WindowsBackgroundMaterial = (typeof WINDOWS_BACKGROUND_MATERIALS)[number] | |
| /** | |
| * Frost (sheer β heavy) β Windows 11 system backdrop. Acrylic is the live-blur | |
| * transient material, closest to macOS under-window vibrancy; tabbed and mica | |
| * sample the wallpaper and read more opaque. | |
| * | |
| * Three backdrops for four rungs, so the two heaviest both land on mica. The | |
| * mapping stays total β a frost saved on a Mac still resolves β and the PICKER | |
| * drops the duplicate instead (see `glassMaterialsFor`). | |
| */ | |
| const WINDOWS_MATERIAL_BY_FROST: Record<GlassMaterial, Exclude<WindowsBackgroundMaterial, 'none'>> = { | |
| 'under-window': 'acrylic', | |
| popover: 'tabbed', | |
| titlebar: 'mica', | |
| header: 'mica' | |
| } | |
| /** | |
| * The frost rungs Windows can render as DISTINCT looks: the first rung for each | |
| * backdrop. Shipping two options that composite identically is the mistake the | |
| * macOS census already corrected once (sidebar/hud); deriving the list from the | |
| * mapping means a change there can never reintroduce a duplicate. | |
| */ | |
| const WINDOWS_GLASS_MATERIALS: readonly GlassMaterial[] = GLASS_MATERIALS.filter( | |
| (material, index) => | |
| GLASS_MATERIALS.findIndex(rung => WINDOWS_MATERIAL_BY_FROST[rung] === WINDOWS_MATERIAL_BY_FROST[material]) === index | |
| ) | |
| /** | |
| * Windows 11 22H2 (build 22621) is the floor Electron documents for | |
| * `setBackgroundMaterial`. Windows 11 still reports kernel 10.0; the build | |
| * number is the discriminator. Fail closed on a missing/unparseable release. | |
| * | |
| * @see https://www.electronjs.org/docs/latest/api/browser-window#winsetbackgroundmaterialmaterial-windows | |
| */ | |
| export const WINDOWS_GLASS_MIN_BUILD = 22621 | |
| export interface TranslucencyState { | |
| intensity: number | |
| /** | |
| * Glass only: native window opacity, on the same ramp Clear's lever uses. | |
| * Defaults to 0 (no fade) because fading a glass window fades its text too β | |
| * the very thing Glass exists to avoid. It is offered as a deliberate second | |
| * lever, never as part of the tint. | |
| */ | |
| fade: number | |
| mode: TranslucencyMode | |
| material: GlassMaterial | |
| scope: GlassScope | |
| } | |
| /** | |
| * The half of the state that is scoped to the light/dark appearance. | |
| * | |
| * A tint that reads as a whisper over a dark palette is a milky sheet over a | |
| * light one, so one shared number cannot serve both β the same setting has to | |
| * mean a different amount in each appearance. `mode` stays global: clear vs | |
| * glass is a choice about the window, not about the palette. | |
| */ | |
| export type TranslucencyValues = Omit<TranslucencyState, 'mode'> | |
| export type Appearance = 'light' | 'dark' | |
| /** | |
| * Glass starts at 29% tint, confined to the sidebar in both appearances. | |
| * Fade stays off so the content column and text remain fully opaque. | |
| * Frost keeps its platform/appearance tuning: macOS uses header/titlebar, | |
| * while Windows uses under-window, the live acrylic backdrop. | |
| */ | |
| const DEFAULT_VALUES: Record<'mac' | 'windows', Record<Appearance, TranslucencyValues>> = { | |
| mac: { | |
| light: { intensity: 29, fade: 0, material: 'header', scope: DEFAULT_GLASS_SCOPE }, | |
| dark: { intensity: 29, fade: 0, material: 'titlebar', scope: DEFAULT_GLASS_SCOPE } | |
| }, | |
| windows: { | |
| light: { intensity: 29, fade: 0, material: 'under-window', scope: DEFAULT_GLASS_SCOPE }, | |
| dark: { intensity: 29, fade: 0, material: 'under-window', scope: DEFAULT_GLASS_SCOPE } | |
| } | |
| } | |
| /** | |
| * The untouched values for an appearance on this platform. Linux never reaches | |
| * here β translucency is unsupported there, so nothing resolves. | |
| */ | |
| export function defaultTranslucencyValues(appearance: Appearance, isWindows: boolean): TranslucencyValues { | |
| return DEFAULT_VALUES[isWindows ? 'windows' : 'mac'][appearance] | |
| } | |
| /** | |
| * The renderer's book of translucency settings. | |
| * | |
| * `base` is the shared rung: a value the user set before appearances were | |
| * split (a migrated v1 state), or one they have never touched. An appearance | |
| * slot only carries the keys edited WHILE that appearance was painted, so | |
| * changing the tint in light mode leaves dark's alone and an untouched dark | |
| * still inherits whatever base says. That is the ladder β appearance over base | |
| * over default, per key, so \"unset\" keeps carrying over. | |
| * | |
| * The book is renderer-owned. The main process is handed the RESOLVED state | |
| * (see `resolveTranslucency`) because a window's backing, vibrancy and opacity | |
| * only ever concern the appearance actually on screen. | |
| */ | |
| export interface TranslucencyBook { | |
| mode: TranslucencyMode | |
| base: Partial<TranslucencyValues> | |
| light: Partial<TranslucencyValues> | |
| dark: Partial<TranslucencyValues> | |
| } | |
| export const TRANSLUCENCY_MIN = 0 | |
| export const TRANSLUCENCY_MAX = 100 | |
| /** Renderer slider granularity. Main accepts any integer in range. */ | |
| export const TRANSLUCENCY_STEP = 1 | |
| /** Most see-through clear setting β floored so it stays usable, not invisible. */ | |
| export const TRANSLUCENCY_OPACITY_FLOOR = 0.3 | |
| /** | |
| * Exponent for the clear intensity β opacity ramp. 1 is a linear ramp, which | |
| * spends the whole readable band (opacity β³ 0.95) in the first few percent of | |
| * the lever. 2 holds that band across roughly the first third while leaving | |
| * both endpoints bit-identical to the linear ramp. | |
| */ | |
| export const TRANSLUCENCY_CURVE = 2 | |
| export function clampIntensity(value: unknown): number { | |
| const n = Math.round(Number(value)) | |
| return Number.isFinite(n) ? Math.min(TRANSLUCENCY_MAX, Math.max(TRANSLUCENCY_MIN, n)) : TRANSLUCENCY_MIN | |
| } | |
| /** | |
| * Whether this OS can do ANY translucency. Clear rides `setOpacity`, which | |
| * Electron documents as doing nothing on Linux, and glass needs a native | |
| * material Linux does not have β so the whole setting is dead there and the | |
| * row should not be shown at all. | |
| */ | |
| export function translucencySupportedOn(platform: string): boolean { | |
| return platform === 'darwin' || platform === 'win32' | |
| } | |
| /** | |
| * Whether this OS can back glass with a first-party Electron material. | |
| * macOS: `setVibrancy`. Windows 11 22H2+: `setBackgroundMaterial`. Linux and | |
| * older Windows: no. | |
| */ | |
| export function glassSupportedOn(platform: string, release = ''): boolean { | |
| if (platform === 'darwin') { | |
| return true | |
| } | |
| if (platform !== 'win32') { | |
| return false | |
| } | |
| const build = Number.parseInt(release.split('.')[2] ?? '', 10) | |
| return Number.isFinite(build) && build >= WINDOWS_GLASS_MIN_BUILD | |
| } | |
| /** | |
| * Glass needs a native window material, so unsupported platforms stay on | |
| * 'clear'. | |
| * | |
| * With no mode recorded, a glass-capable OS gets glass β it is the | |
| * better-looking half of the feature and the one worth finding, and | |
| * pre-selecting it costs a fresh profile nothing because the intensity still | |
| * starts at 0 (the whole feature is off until the user raises the lever). | |
| * `legacyIntensity` is the escape hatch: a profile that already carries a | |
| * NON-ZERO intensity but no mode predates this setting and has been rendering | |
| * as clear all along, so it keeps rendering as clear. Flipping a window | |
| * someone already tuned is the one thing a default must not do. | |
| */ | |
| export function normalizeMode(value: unknown, glassSupported: boolean, legacyIntensity = 0): TranslucencyMode { | |
| if (!glassSupported) { | |
| return 'clear' | |
| } | |
| if (value === 'glass' || value === 'clear') { | |
| return value | |
| } | |
| return legacyIntensity > 0 ? 'clear' : 'glass' | |
| } | |
| /** Unknown or unsupported values fall back to the default material. */ | |
| export function normalizeMaterial(value: unknown): GlassMaterial { | |
| return GLASS_MATERIALS.includes(value as GlassMaterial) ? (value as GlassMaterial) : DEFAULT_GLASS_MATERIAL | |
| } | |
| /** Unknown or unsupported values fall back to sidebar glass. */ | |
| export function normalizeScope(value: unknown): GlassScope { | |
| return GLASS_SCOPES.includes(value as GlassScope) ? (value as GlassScope) : DEFAULT_GLASS_SCOPE | |
| } | |
| /** Parse a persisted translucency.json / IPC payload into a safe state. */ | |
| export function normalizeState(payload: unknown, glassSupported: boolean): TranslucencyState { | |
| const record = payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : {} | |
| const intensity = clampIntensity(record.intensity) | |
| return { | |
| intensity, | |
| fade: clampIntensity(record.fade), | |
| mode: normalizeMode(record.mode, glassSupported, intensity), | |
| material: normalizeMaterial(record.material), | |
| scope: normalizeScope(record.scope) | |
| } | |
| } | |
| /** | |
| * The resolved state a surface should assume before anyone has said otherwise. | |
| * | |
| * Main needs this at window CREATION on a first launch: the renderer has not | |
| * reported yet, and a window created with the opaque backing cannot reliably | |
| * be swapped to glass moments later (see `windowBackingOptions`). Guessing the | |
| * appearance from `nativeTheme` is close enough β the renderer's first resolved | |
| * send corrects any mismatch while the window is still young, and every launch | |
| * after the first reads the persisted state instead. | |
| */ | |
| export function defaultTranslucencyState( | |
| appearance: Appearance, | |
| glassSupported: boolean, | |
| isWindows: boolean | |
| ): TranslucencyState { | |
| return { | |
| ...defaultTranslucencyValues(appearance, isWindows), | |
| mode: normalizeMode(undefined, glassSupported) | |
| } | |
| } | |
| /** Keep only the value keys actually present, each normalized. Unknown keys drop. */ | |
| function normalizeValues(payload: unknown): Partial<TranslucencyValues> { | |
| const record = payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : {} | |
| const out: Partial<TranslucencyValues> = {} | |
| if (record.intensity !== undefined) { | |
| out.intensity = clampIntensity(record.intensity) | |
| } | |
| if (record.fade !== undefined) { | |
| out.fade = clampIntensity(record.fade) | |
| } | |
| if (record.material !== undefined) { | |
| out.material = normalizeMaterial(record.material) | |
| } | |
| if (record.scope !== undefined) { | |
| out.scope = normalizeScope(record.scope) | |
| } | |
| return out | |
| } | |
| /** | |
| * Parse a persisted book, or migrate a flat v1 state into one. | |
| * | |
| * A v1 payload is a window someone already tuned, so its values land in `base` | |
| * β every appearance inherits exactly what was on screen before the upgrade, | |
| * and the new per-appearance defaults apply only where nothing was ever set. | |
| * The legacy clear rule rides along: a non-zero v1 intensity with no mode was | |
| * rendering as clear and keeps doing so. | |
| */ | |
| export function normalizeBook(payload: unknown, glassSupported: boolean): TranslucencyBook { | |
| const record = payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : {} | |
| const migrating = record.base === undefined && record.light === undefined && record.dark === undefined | |
| const base = normalizeValues(migrating ? record : record.base) | |
| const legacyIntensity = migrating ? clampIntensity(record.intensity) : 0 | |
| return { | |
| mode: normalizeMode(record.mode, glassSupported, legacyIntensity), | |
| base, | |
| light: normalizeValues(migrating ? null : record.light), | |
| dark: normalizeValues(migrating ? null : record.dark) | |
| } | |
| } | |
| /** | |
| * Flatten the book for one appearance: appearance slot β base β default. | |
| * | |
| * This is the only thing outside the renderer's settings surface that should | |
| * ever be handed around β main, the CSS field surfaces, and every consumer of | |
| * `$translucency` all want the resolved answer for what is painted right now. | |
| */ | |
| export function resolveTranslucency( | |
| book: TranslucencyBook, | |
| appearance: Appearance, | |
| isWindows: boolean | |
| ): TranslucencyState { | |
| const fallback = defaultTranslucencyValues(appearance, isWindows) | |
| const slot = book[appearance] | |
| return { | |
| mode: book.mode, | |
| intensity: slot.intensity ?? book.base.intensity ?? fallback.intensity, | |
| fade: slot.fade ?? book.base.fade ?? fallback.fade, | |
| material: slot.material ?? book.base.material ?? fallback.material, | |
| scope: slot.scope ?? book.base.scope ?? fallback.scope | |
| } | |
| } | |
| /** | |
| * Record an edit against the appearance being painted. | |
| * | |
| * The edit is written to the appearance slot rather than to base, so tuning | |
| * light mode is scoped to light mode. Base is left intact as the inheritance | |
| * rung for whichever appearance has not been touched. | |
| */ | |
| export function setTranslucencyValues( | |
| book: TranslucencyBook, | |
| appearance: Appearance, | |
| patch: Partial<TranslucencyValues> | |
| ): TranslucencyBook { | |
| return { ...book, [appearance]: { ...book[appearance], ...normalizeValues(patch) } } | |
| } | |
| /** Lever percent β native window opacity, floored so it stays usable. */ | |
| function opacityRamp(lever: number): number { | |
| const ratio = clampIntensity(lever) / TRANSLUCENCY_MAX | |
| return 1 - (1 - TRANSLUCENCY_OPACITY_FLOOR) * Math.pow(ratio, TRANSLUCENCY_CURVE) | |
| } | |
| /** | |
| * Native window opacity for a state. | |
| * | |
| * Under Clear the lever IS the opacity. Under Glass the lever paints the tint | |
| * and only the separate `fade` reaches the window, so a glass window stays at | |
| * 1 until the user opts into fading it β which is what keeps a tint drag from | |
| * touching anything native. | |
| * | |
| * Fade is gated on glass being ACTIVE, not merely selected. The light default | |
| * carries a single point of it so the window edge reads as glass rather than | |
| * as paint, and without this gate that point would follow someone who had | |
| * turned the tint to zero β leaving a window that asked to be opaque sitting | |
| * at 0.9999. Off has to mean off. | |
| */ | |
| export function windowOpacityFor(state: TranslucencyState): number { | |
| if (state.mode !== 'glass') { | |
| return opacityRamp(state.intensity) | |
| } | |
| return opacityRamp(glassActive(state) ? state.fade : 0) | |
| } | |
| /** | |
| * Whether glass is visually active. Both processes branch on this: main to | |
| * decide a window's backing, the renderer to decide whether to thin surfaces. | |
| */ | |
| export function glassActive({ intensity, mode }: TranslucencyState): boolean { | |
| return mode === 'glass' && intensity > 0 | |
| } | |
| /** | |
| * Percent of the surface tint the renderer KEEPS at a given intensity. Linear | |
| * to zero: at 100 the tint is fully gone β bare platform glass β so the slider | |
| * spans the whole range from opaque theme to untinted blur. Text and cards | |
| * keep their own opaque tokens for contrast; only the field surfaces thin. | |
| */ | |
| export function glassSurfaceKeep(intensity: number): number { | |
| return TRANSLUCENCY_MAX - clampIntensity(intensity) | |
| } | |
| /** | |
| * The vibrancy material a chat window should carry. 'sidebar' is the | |
| * long-standing default the titlebar band was designed against; glass mode | |
| * swaps the whole window onto the user's chosen material (setVibrancy is | |
| * cheap and animatable at runtime, unlike the backing). | |
| */ | |
| export function vibrancyFor(state: TranslucencyState): GlassMaterial | 'sidebar' { | |
| return glassActive(state) ? state.material : 'sidebar' | |
| } | |
| /** | |
| * The Windows 11 system backdrop a chat window should carry. 'none' while | |
| * glass is off so DWM does not keep drawing mica/acrylic under the opaque | |
| * themed backing. | |
| */ | |
| export function backgroundMaterialFor(state: TranslucencyState): WindowsBackgroundMaterial { | |
| return glassActive(state) ? WINDOWS_MATERIAL_BY_FROST[state.material] : 'none' | |
| } | |
| /** The frost rungs to offer on this platform. */ | |
| export function glassMaterialsFor(isWindows: boolean): readonly GlassMaterial[] { | |
| return isWindows ? WINDOWS_GLASS_MATERIALS : GLASS_MATERIALS | |
| } | |
| /** | |
| * The native frost a HUD-style transparent window should carry. | |
| * | |
| * Two gates, because the HUD's frost answers to more than the setting. The | |
| * material is the WINDOW's β nothing on the page can clip it β so it is only | |
| * ever right while the band actually covers the window below the bar; | |
| * `showing` is the renderer's answer to that (see `useHudGlass`). The setting | |
| * is the other half: Glass off, or the tint at zero, means no frost at all. | |
| * | |
| * The off answer is `null` rather than a resting material, which is the one | |
| * way this differs from `vibrancyFor`. A chat window is opaque and keeps | |
| * 'sidebar' under its titlebar band whatever the setting says; a transparent | |
| * window has no opaque page to hide an unwanted material behind, so off has | |
| * to mean off or the frost is a grey slab hanging over someone else's app. | |
| */ | |
| export function hudFrostFor( | |
| state: TranslucencyState, | |
| showing: boolean | |
| ): { backgroundMaterial: WindowsBackgroundMaterial; vibrancy: GlassMaterial | null } { | |
| const active = showing && glassActive(state) | |
| return { | |
| vibrancy: active ? state.material : null, | |
| backgroundMaterial: active ? backgroundMaterialFor(state) : 'none' | |
| } | |
| } | |
| /** | |
| * The rung the picker highlights. A frost with no rung of its own here β a | |
| * Mac's 'header' read on Windows β folds onto the rung that renders the same | |
| * backdrop, so the picker shows a truthful selection without rewriting the | |
| * value the user saved on their other machine. | |
| */ | |
| export function glassMaterialForPicker(material: GlassMaterial, isWindows: boolean): GlassMaterial { | |
| if (!isWindows) { | |
| return material | |
| } | |
| return ( | |
| WINDOWS_GLASS_MATERIALS.find(rung => WINDOWS_MATERIAL_BY_FROST[rung] === WINDOWS_MATERIAL_BY_FROST[material]) ?? | |
| DEFAULT_GLASS_MATERIAL | |
| ) | |
| } | |