дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
The main toolbar and status bar could not be hidden, Tool Window Bars was a Settings row only, and widgets could be switched only from the status bar's context menu. View > Appearance now has Toolbar, Tool Window Bars, Status Bar and Status Bar Widgets, with check marks in the macOS and in-app menus, plus Settings toggles. Both new settings default to shown. With the toolbar hidden, the window keeps a compact header with the title, which Distraction Free now shares. Controls stay reachable with a bar hidden: the Git branches popup and background tasks have their own hosts, and background tasks has an action. Co-authored-by: lleviathe <[email protected]>
208 рядки
7.9 KiB
TypeScript
208 рядки
7.9 KiB
TypeScript
/**
|
||
* Appearance & Behavior settings beyond the basics in `schema.ts`: extra
|
||
* Appearance / System Settings fields and the Notifications page. Mirrors the
|
||
* `ide-core::settings` defaults; validation never throws.
|
||
*/
|
||
|
||
import { isThemeChoiceId, type ThemeChoiceId } from "@/platform/themes";
|
||
import type { FileIconThemeOptions } from "@/bindings";
|
||
import { ZOOM_LIMITS } from "@/platform/zoom";
|
||
import { validateIconThemeOptionsMap } from "@/platform/iconThemeOptions";
|
||
|
||
export type Antialiasing = "subpixel" | "greyscale" | "none";
|
||
export type OpenProjectIn = "ask" | "new" | "same" | "attach";
|
||
export type NotificationDisplay = "balloon" | "sticky" | "none";
|
||
/** Themes selectable as "preferred" when syncing with the OS. */
|
||
export type ConcreteTheme = ThemeChoiceId;
|
||
|
||
export interface AppearanceExtras {
|
||
syncWithOs: boolean;
|
||
preferredLightTheme: ConcreteTheme;
|
||
preferredDarkTheme: ConcreteTheme;
|
||
/** A plugin's file icon theme (`<pluginId>/<themeId>`); empty for the built-in icons. */
|
||
fileIconTheme: string;
|
||
fileIconThemeOptions: Record<string, FileIconThemeOptions>;
|
||
zoom: number;
|
||
useCustomFont: boolean;
|
||
uiFontFamily: string;
|
||
uiFontFallback: string;
|
||
contrastScrollbars: boolean;
|
||
redGreenVision: boolean;
|
||
differentToolWindowBackground: boolean;
|
||
compactMode: boolean;
|
||
/** IDEA "Use smaller indents in trees": tool window trees indent less per level. */
|
||
smallTreeIndents: boolean;
|
||
fullPathInTitle: boolean;
|
||
projectColorsInToolbar: boolean;
|
||
dndWithAltOnly: boolean;
|
||
menuIcons: boolean;
|
||
mainMenuStyle: "hamburger" | "merged";
|
||
/** A lone Alt press focuses or opens the main menu (IDEA `ide.windows.main.menu.focus.on.alt`). */
|
||
mainMenuAlt: boolean;
|
||
ideAntialiasing: Antialiasing;
|
||
editorAntialiasing: Antialiasing;
|
||
showToolWindowBars: boolean;
|
||
showToolWindowNames: boolean;
|
||
showToolWindowBadges: boolean;
|
||
widescreenToolWindows: boolean;
|
||
/** View › Appearance › Toolbar; hidden leaves IDEA's compact window header. */
|
||
showMainToolbar: boolean;
|
||
showStatusBar: boolean;
|
||
}
|
||
|
||
export interface DateFormatValid {
|
||
overrideSystem: boolean;
|
||
pattern: string;
|
||
use24Hour: boolean;
|
||
pretty: boolean;
|
||
}
|
||
|
||
export type UpdateSourceSetting = "auto" | "github" | "cnb";
|
||
|
||
export interface GeneralExtras {
|
||
openProjectIn: OpenProjectIn;
|
||
updateSource: UpdateSourceSetting;
|
||
defaultProjectDirectory: string;
|
||
saveOnIdle: boolean;
|
||
idleSeconds: number;
|
||
dateFormat: DateFormatValid;
|
||
}
|
||
|
||
export interface NotificationGroupValid {
|
||
display: NotificationDisplay;
|
||
log: boolean;
|
||
}
|
||
|
||
export interface NotificationsValid {
|
||
balloons: boolean;
|
||
sound: boolean;
|
||
groups: Record<string, NotificationGroupValid>;
|
||
}
|
||
|
||
export const IDLE_LIMITS = { min: 1, max: 3600 } as const;
|
||
|
||
export const DEFAULT_APPEARANCE_EXTRAS: AppearanceExtras = {
|
||
syncWithOs: false,
|
||
preferredLightTheme: "light",
|
||
preferredDarkTheme: "dark",
|
||
fileIconTheme: "",
|
||
fileIconThemeOptions: {},
|
||
zoom: 100,
|
||
useCustomFont: false,
|
||
uiFontFamily: "Inter",
|
||
uiFontFallback: "",
|
||
contrastScrollbars: false,
|
||
redGreenVision: false,
|
||
differentToolWindowBackground: false,
|
||
compactMode: false,
|
||
smallTreeIndents: false,
|
||
fullPathInTitle: false,
|
||
projectColorsInToolbar: true,
|
||
dndWithAltOnly: false,
|
||
menuIcons: true,
|
||
mainMenuStyle: "hamburger",
|
||
mainMenuAlt: true,
|
||
ideAntialiasing: "greyscale",
|
||
editorAntialiasing: "greyscale",
|
||
showToolWindowBars: true,
|
||
showToolWindowNames: false,
|
||
showToolWindowBadges: true,
|
||
widescreenToolWindows: false,
|
||
showMainToolbar: true,
|
||
showStatusBar: true,
|
||
};
|
||
|
||
export const DEFAULT_GENERAL_EXTRAS: GeneralExtras = {
|
||
openProjectIn: "ask",
|
||
updateSource: "auto",
|
||
defaultProjectDirectory: "",
|
||
saveOnIdle: false,
|
||
idleSeconds: 15,
|
||
dateFormat: { overrideSystem: false, pattern: "dd.MM.yyyy", use24Hour: true, pretty: true },
|
||
};
|
||
|
||
export const DEFAULT_NOTIFICATIONS: NotificationsValid = { balloons: true, sound: false, groups: {} };
|
||
|
||
type Obj = Record<string, unknown>;
|
||
const obj = (v: unknown): Obj => (v && typeof v === "object" && !Array.isArray(v) ? (v as Obj) : {});
|
||
const bool = (v: unknown, d: boolean) => (typeof v === "boolean" ? v : d);
|
||
const oneOf = <T extends string>(v: unknown, allowed: readonly T[], d: T): T =>
|
||
typeof v === "string" && (allowed as readonly string[]).includes(v) ? (v as T) : d;
|
||
const int = (v: unknown, d: number, { min, max }: { min: number; max: number }) =>
|
||
typeof v === "number" && Number.isFinite(v) ? Math.round(Math.min(max, Math.max(min, v))) : d;
|
||
|
||
const AA: readonly Antialiasing[] = ["subpixel", "greyscale", "none"];
|
||
const concrete = (v: unknown, d: ConcreteTheme): ConcreteTheme => (isThemeChoiceId(v) ? v : d);
|
||
const DISPLAYS: readonly NotificationDisplay[] = ["balloon", "sticky", "none"];
|
||
|
||
export function validateAppearanceExtras(raw: unknown): AppearanceExtras {
|
||
const a = obj(raw);
|
||
const d = DEFAULT_APPEARANCE_EXTRAS;
|
||
const b = (k: keyof AppearanceExtras) => bool(a[k], d[k] as boolean);
|
||
return {
|
||
// A legacy `theme = "system"` means syncing too.
|
||
syncWithOs: bool(a.syncWithOs, d.syncWithOs) || a.theme === "system",
|
||
preferredLightTheme: concrete(a.preferredLightTheme, d.preferredLightTheme),
|
||
preferredDarkTheme: concrete(a.preferredDarkTheme, d.preferredDarkTheme),
|
||
fileIconTheme:
|
||
typeof a.fileIconTheme === "string" && /^[^/\s]+\/[^/\s]+$/.test(a.fileIconTheme) ? a.fileIconTheme : "",
|
||
fileIconThemeOptions: validateIconThemeOptionsMap(a.fileIconThemeOptions),
|
||
zoom: int(a.zoom, d.zoom, ZOOM_LIMITS),
|
||
useCustomFont: b("useCustomFont"),
|
||
uiFontFamily: typeof a.uiFontFamily === "string" && a.uiFontFamily.trim() ? a.uiFontFamily.trim() : d.uiFontFamily,
|
||
uiFontFallback: typeof a.uiFontFallback === "string" ? a.uiFontFallback.trim() : d.uiFontFallback,
|
||
contrastScrollbars: b("contrastScrollbars"),
|
||
redGreenVision: b("redGreenVision"),
|
||
differentToolWindowBackground: b("differentToolWindowBackground"),
|
||
compactMode: b("compactMode"),
|
||
smallTreeIndents: b("smallTreeIndents"),
|
||
fullPathInTitle: b("fullPathInTitle"),
|
||
projectColorsInToolbar: b("projectColorsInToolbar"),
|
||
dndWithAltOnly: b("dndWithAltOnly"),
|
||
menuIcons: b("menuIcons"),
|
||
mainMenuStyle: oneOf(a.mainMenuStyle, ["hamburger", "merged"], d.mainMenuStyle),
|
||
mainMenuAlt: b("mainMenuAlt"),
|
||
ideAntialiasing: oneOf(a.ideAntialiasing, AA, d.ideAntialiasing),
|
||
editorAntialiasing: oneOf(a.editorAntialiasing, AA, d.editorAntialiasing),
|
||
showToolWindowBars: b("showToolWindowBars"),
|
||
showToolWindowNames: b("showToolWindowNames"),
|
||
showToolWindowBadges: b("showToolWindowBadges"),
|
||
widescreenToolWindows: b("widescreenToolWindows"),
|
||
showMainToolbar: b("showMainToolbar"),
|
||
showStatusBar: b("showStatusBar"),
|
||
};
|
||
}
|
||
|
||
export function validateGeneralExtras(raw: unknown): GeneralExtras {
|
||
const g = obj(raw);
|
||
const d = DEFAULT_GENERAL_EXTRAS;
|
||
const df = obj(g.dateFormat);
|
||
return {
|
||
openProjectIn: oneOf(g.openProjectIn, ["ask", "new", "same", "attach"] as const, d.openProjectIn),
|
||
updateSource: oneOf(g.updateSource, ["auto", "github", "cnb"] as const, d.updateSource),
|
||
defaultProjectDirectory: typeof g.defaultProjectDirectory === "string" ? g.defaultProjectDirectory : "",
|
||
saveOnIdle: bool(g.saveOnIdle, d.saveOnIdle),
|
||
idleSeconds: int(g.idleSeconds, d.idleSeconds, IDLE_LIMITS),
|
||
dateFormat: {
|
||
overrideSystem: bool(df.overrideSystem, d.dateFormat.overrideSystem),
|
||
pattern: typeof df.pattern === "string" && df.pattern.trim() ? df.pattern : d.dateFormat.pattern,
|
||
use24Hour: bool(df.use24Hour, d.dateFormat.use24Hour),
|
||
pretty: bool(df.pretty, d.dateFormat.pretty),
|
||
},
|
||
};
|
||
}
|
||
|
||
export function validateNotifications(raw: unknown): NotificationsValid {
|
||
const n = obj(raw);
|
||
const groups: Record<string, NotificationGroupValid> = {};
|
||
for (const [id, v] of Object.entries(obj(n.groups))) {
|
||
const g = obj(v);
|
||
groups[id] = { display: oneOf(g.display, DISPLAYS, "balloon"), log: bool(g.log, true) };
|
||
}
|
||
return {
|
||
balloons: bool(n.balloons, DEFAULT_NOTIFICATIONS.balloons),
|
||
sound: bool(n.sound, DEFAULT_NOTIFICATIONS.sound),
|
||
groups,
|
||
};
|
||
}
|