mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-11-13 12:13:42 +01:00
169 lines
5.6 KiB
TypeScript
169 lines
5.6 KiB
TypeScript
import { formatSize } from "@yaakapp-internal/lib/formatSize";
|
|
// The themes alone, not the package index, which reaches for the window as it loads
|
|
import { defaultDarkTheme, defaultLightTheme } from "@yaakapp-internal/theme/src/defaultThemes";
|
|
import type {
|
|
SettingsAppInfo,
|
|
SettingsThemeOption,
|
|
SettingsValues,
|
|
SettingsView,
|
|
} from "@yaakapp-internal/ui";
|
|
|
|
/** Yaak's own defaults for the shared tabs, so the two apps start the same */
|
|
export const DEFAULT_SETTINGS_VALUES: SettingsValues = {
|
|
appearance: "system",
|
|
themeLight: defaultLightTheme.id,
|
|
themeDark: defaultDarkTheme.id,
|
|
interfaceFont: null,
|
|
interfaceFontSize: 15,
|
|
editorFont: null,
|
|
editorFontSize: 13,
|
|
editorKeymap: "default",
|
|
editorSoftWrap: true,
|
|
coloredMethods: false,
|
|
useNativeTitlebar: false,
|
|
hideWindowControls: false,
|
|
updateChannel: "stable",
|
|
autoupdate: true,
|
|
autoDownloadUpdates: true,
|
|
checkNotifications: true,
|
|
promptFeedback: true,
|
|
};
|
|
|
|
/** Where the shared tabs' values are kept in the proxy */
|
|
export const SETTINGS_VALUES_KEY = "proxy_settings_values";
|
|
|
|
/** The stored values over the defaults; anything unreadable or of the wrong type is ignored */
|
|
export function readSettingsValues(raw: string | null): SettingsValues {
|
|
if (raw == null) return DEFAULT_SETTINGS_VALUES;
|
|
let parsed: unknown;
|
|
try {
|
|
parsed = JSON.parse(raw);
|
|
} catch {
|
|
return DEFAULT_SETTINGS_VALUES;
|
|
}
|
|
if (parsed == null || typeof parsed !== "object") return DEFAULT_SETTINGS_VALUES;
|
|
const out: Record<string, unknown> = { ...DEFAULT_SETTINGS_VALUES };
|
|
for (const [key, fallback] of Object.entries(DEFAULT_SETTINGS_VALUES)) {
|
|
const value = (parsed as Record<string, unknown>)[key];
|
|
const nullable = fallback === null;
|
|
if (value === undefined) continue;
|
|
if (
|
|
(nullable && (value === null || typeof value === "string")) ||
|
|
typeof value === typeof fallback
|
|
) {
|
|
out[key] = value;
|
|
}
|
|
}
|
|
return out as unknown as SettingsValues;
|
|
}
|
|
|
|
const THEMES: SettingsThemeOption[] = [defaultDarkTheme, defaultLightTheme].map((t) => ({
|
|
id: t.id,
|
|
label: t.label,
|
|
dark: t.dark,
|
|
}));
|
|
|
|
/** The appearance showing, with `system` resolved by the OS's */
|
|
export function resolveAppearance(appearance: string, system: "light" | "dark"): "light" | "dark" {
|
|
return appearance === "light" || appearance === "dark" ? appearance : system;
|
|
}
|
|
|
|
/**
|
|
* The proxy's settings as the shared tabs render them. The proxy has Yaak's two themes, no
|
|
* installed-font list, updater or feedback prompts yet, and its license comes with the license
|
|
* work, so those parts are left out, as are the Interface rows it doesn't act on.
|
|
*/
|
|
export function proxySettingsView(
|
|
values: SettingsValues,
|
|
app: SettingsAppInfo,
|
|
systemAppearance: "light" | "dark",
|
|
): SettingsView {
|
|
const light = THEMES.find((t) => t.id === values.themeLight && !t.dark) ?? lightOption();
|
|
const dark = THEMES.find((t) => t.id === values.themeDark && t.dark) ?? darkOption();
|
|
const resolvedAppearance = resolveAppearance(values.appearance, systemAppearance);
|
|
return {
|
|
app,
|
|
values,
|
|
themes: {
|
|
themes: THEMES,
|
|
light,
|
|
dark,
|
|
active: resolvedAppearance === "dark" ? dark : light,
|
|
resolvedAppearance,
|
|
},
|
|
fonts: null,
|
|
windowControlsSetting: false,
|
|
updates: null,
|
|
feedbackPrompts: false,
|
|
license: null,
|
|
// Not acted on in the proxy yet: its editors take no keymap or wrap setting, its list draws
|
|
// methods uncoloured, and it always draws its own title bar.
|
|
hiddenInterfaceRows: ["editorKeymap", "editorSoftWrap", "coloredMethods", "nativeTitlebar"],
|
|
};
|
|
}
|
|
|
|
function lightOption(): SettingsThemeOption {
|
|
return THEMES.find((t) => !t.dark) as SettingsThemeOption;
|
|
}
|
|
|
|
function darkOption(): SettingsThemeOption {
|
|
return THEMES.find((t) => t.dark) as SettingsThemeOption;
|
|
}
|
|
|
|
/** A size choice in a select, `null` for no limit */
|
|
export interface SizeOption {
|
|
label: string;
|
|
value: string;
|
|
bytes: number | null;
|
|
}
|
|
|
|
const MB = 1024 * 1024;
|
|
const GB = 1024 * MB;
|
|
|
|
function sizeOptions(sizes: (number | null)[], none: string): SizeOption[] {
|
|
return sizes.map((bytes) => ({
|
|
label: bytes == null ? none : sizeLabel(bytes),
|
|
value: bytes == null ? "none" : `${bytes}`,
|
|
bytes,
|
|
}));
|
|
}
|
|
|
|
/** A size in the binary units the limits are set in, so 10 MiB reads "10 MB", not "10.5 MB" */
|
|
export function sizeLabel(bytes: number): string {
|
|
if (bytes >= GB && bytes % (GB / 2) === 0) return `${bytes / GB} GB`;
|
|
if (bytes >= MB && bytes % MB === 0) return `${bytes / MB} MB`;
|
|
return formatSize(bytes);
|
|
}
|
|
|
|
/** How much captured traffic is kept before the oldest flows are dropped */
|
|
export const KEEP_TRAFFIC_OPTIONS = sizeOptions(
|
|
[null, 500 * MB, GB, 2 * GB, 5 * GB, 10 * GB, 20 * GB],
|
|
"Unlimited",
|
|
);
|
|
|
|
/** How much of each body is stored */
|
|
export const CAPTURE_LIMIT_OPTIONS = sizeOptions(
|
|
[MB, 5 * MB, 10 * MB, 50 * MB, 100 * MB],
|
|
"Unlimited",
|
|
);
|
|
|
|
/**
|
|
* The option a stored size selects. A size set elsewhere (the CLI or `settings.json`) that no
|
|
* option has is added as its own, so the select still shows it.
|
|
*/
|
|
export function sizeOptionsFor(options: SizeOption[], bytes: number | null): SizeOption[] {
|
|
if (options.some((o) => o.bytes === bytes)) return options;
|
|
const extra = sizeOptions([bytes], "Unlimited");
|
|
return [...options, ...extra].sort((a, b) => (a.bytes ?? Infinity) - (b.bytes ?? Infinity));
|
|
}
|
|
|
|
export function sizeOptionValue(bytes: number | null): string {
|
|
return bytes == null ? "none" : `${bytes}`;
|
|
}
|
|
|
|
export function sizeFromOptionValue(value: string): number | null {
|
|
if (value === "none") return null;
|
|
const n = Number(value);
|
|
return Number.isFinite(n) && n > 0 ? n : null;
|
|
}
|