Add full disk access checks and settings

Add background themes and custom images

Add shell command logging toggle

Add UTF-8 locale guarantee for PTY

Add Claude hook settings injection

Add hotkey system for GUI

Add glass panel styling

Add search disabled state for agent panels

Add zoom toggle command

Add device report filtering

Add entitlements for notarization

Update version to 0.1.27
This commit is contained in:
2026-06-15 22:26:06 +07:00
parent 2ee2aaaffb
commit ee845e15b3
30 changed files with 859 additions and 123 deletions
+95 -7
View File
@@ -7,6 +7,11 @@ export const COLORS = {
bgElevated: "var(--c-bg-elevated)",
bgHover: "var(--c-bg-hover)",
bgPanel: "var(--c-bg-panel)",
panelGlass: "var(--c-panel-glass, var(--c-bg-panel))",
panelBlur: "var(--c-panel-blur, none)",
appBg: "var(--app-bg, var(--c-bg-app))",
elevatedGlass: "var(--c-elevated-glass, var(--c-bg-elevated))",
sidebarGlass: "var(--c-sidebar-glass, var(--c-bg-sidebar))",
bgSidebar: "var(--c-bg-sidebar)",
borderStrong: "var(--c-border-strong)",
borderSubtle: "var(--c-border-subtle)",
@@ -92,15 +97,98 @@ export const ACCENTS: Record<string, string> = {
export type ThemeName = "dark" | "light";
/** Real color values for consumers that can't use var() (xterm). Keys are the kebab tokens plus "accent". */
export function resolvePalette(theme: ThemeName, accent: string): Record<string, string> {
const base = theme === "light" ? LIGHT : DARK;
return { ...base, accent: ACCENTS[accent] ?? ACCENTS.blue };
// ---------------------------------------------------------------------------
// Background themes (Warp-style full-window fills)
// ---------------------------------------------------------------------------
/**
* A background theme paints the WHOLE window behind every panel, instead of each
* terminal owning an opaque tile. Panels become semi-transparent glass (rgba over
* a backdrop blur) so the shared fill shows through uniformly across the grid.
*
* `css` is the CSS `background` value for the app root: a gradient, or `""` for
* the classic solid look ("none"), or the sentinel "custom" handled at apply time
* via a user-supplied image. `panelAlpha`/`blur` tune the glass over the fill.
*/
export interface BackgroundTheme {
label: string;
css: string; // app-root `background` value ("" = solid bg-app)
swatch: string; // gallery preview (gradient/color)
panelAlpha: number; // 0..1 — panel glass opacity over the fill
blur: number; // px — panel backdrop blur
}
/** Write the active palette to :root as --c-* custom properties. */
export function applyTheme(theme: ThemeName, accent: string): void {
const p = resolvePalette(theme, accent);
export const CUSTOM_BACKGROUND = "custom";
export const BACKGROUNDS: Record<string, BackgroundTheme> = {
none: { label: "None", css: "", swatch: DARK["bg-panel"], panelAlpha: 1, blur: 0 },
cyberwave: { label: "Cyber Wave", css: "linear-gradient(135deg,#06121f 0%,#0a2a3f 45%,#10183a 100%)", swatch: "linear-gradient(135deg,#06121f,#0a2a3f,#10183a)", panelAlpha: 0.46, blur: 9 },
phenomenon: { label: "Phenomenon", css: "radial-gradient(120% 120% at 80% 0%,#241a2e 0%,#15121d 45%,#0a0910 100%)", swatch: "radial-gradient(120% 120% at 80% 0%,#241a2e,#15121d,#0a0910)", panelAlpha: 0.5, blur: 8 },
dracula: { label: "Dracula", css: "linear-gradient(160deg,#282a36 0%,#21222c 60%,#1a1b23 100%)", swatch: "linear-gradient(160deg,#282a36,#21222c,#1a1b23)", panelAlpha: 0.58, blur: 6 },
aurora: { label: "Aurora", css: "linear-gradient(135deg,#0b3d2e 0%,#0a2c3a 40%,#241147 100%)", swatch: "linear-gradient(135deg,#0b3d2e,#0a2c3a,#241147)", panelAlpha: 0.44, blur: 10 },
ember: { label: "Ember", css: "linear-gradient(135deg,#2a0f12 0%,#3a1410 45%,#160a14 100%)", swatch: "linear-gradient(135deg,#2a0f12,#3a1410,#160a14)", panelAlpha: 0.5, blur: 8 },
referred: { label: "Referred", css: "linear-gradient(120deg,#b9c6ff 0%,#cdb6ff 40%,#ffd6e7 100%)", swatch: "linear-gradient(120deg,#b9c6ff,#cdb6ff,#ffd6e7)", panelAlpha: 0.34, blur: 12 },
};
/** Resolve a background name to its theme, falling back to "none". */
export function backgroundFor(name: string): BackgroundTheme {
return BACKGROUNDS[name] ?? BACKGROUNDS.none;
}
/** Hex (#rrggbb) → rgba() string at the given alpha. */
function hexToRgba(hex: string, alpha: number): string {
const h = hex.replace("#", "");
const r = parseInt(h.slice(0, 2), 16);
const g = parseInt(h.slice(2, 4), 16);
const b = parseInt(h.slice(4, 6), 16);
return `rgba(${r},${g},${b},${alpha})`;
}
/**
* Real color values for consumers that can't use var() (xterm). Keys are the
* kebab tokens plus "accent" and "term-bg". When a background theme is active the
* terminal renders on transparent glass, so "term-bg" is fully transparent and
* the panel container supplies the visible tint.
*/
export function resolvePalette(theme: ThemeName, accent: string, background: string = "none"): Record<string, string> {
const base = theme === "light" ? LIGHT : DARK;
const active = background !== "none";
return {
...base,
accent: ACCENTS[accent] ?? ACCENTS.blue,
"term-bg": active ? "rgba(0,0,0,0)" : base["bg-panel"],
};
}
/**
* Write the active palette + background fill to :root as --c-* custom properties.
* `imageDataUrl` is only consulted when `background === "custom"`.
*/
export function applyTheme(theme: ThemeName, accent: string, background: string = "none", imageDataUrl: string | null = null): void {
const p = resolvePalette(theme, accent, background);
const root = document.documentElement.style;
for (const [k, v] of Object.entries(p)) root.setProperty(`--c-${k}`, v);
const bg = backgroundFor(background);
const base = theme === "light" ? LIGHT : DARK;
const active = background !== "none";
// App-root fill: custom image (cover) > gradient > solid bg-app.
const appBg = background === CUSTOM_BACKGROUND && imageDataUrl
? `center / cover no-repeat url("${imageDataUrl}")`
: bg.css || base["bg-app"];
root.setProperty("--app-bg", appBg);
// Panel glass: rgba(bg-panel, alpha) over the fill, plus optional backdrop blur.
// When inactive this is the solid bg-panel so the classic look is byte-identical.
const alpha = active ? (background === CUSTOM_BACKGROUND ? 0.5 : bg.panelAlpha) : 1;
const blur = active ? (background === CUSTOM_BACKGROUND ? 8 : bg.blur) : 0;
root.setProperty("--c-panel-glass", alpha < 1 ? hexToRgba(base["bg-panel"], alpha) : base["bg-panel"]);
root.setProperty("--c-panel-blur", blur > 0 ? `blur(${blur}px)` : "none");
// Chrome glass (TopBar / toolbar / sidebar / panel headers) — a touch more
// opaque than the panels so labels and controls stay legible over the fill.
const chromeAlpha = active ? Math.min(alpha + 0.22, 0.92) : 1;
root.setProperty("--c-elevated-glass", chromeAlpha < 1 ? hexToRgba(base["bg-elevated"], chromeAlpha) : base["bg-elevated"]);
root.setProperty("--c-sidebar-glass", chromeAlpha < 1 ? hexToRgba(base["bg-sidebar"], chromeAlpha) : base["bg-sidebar"]);
}