The palette now follows Oxide's design system (docs.oxide.computer): a cool neutral scale at hue 260 with almost no chroma, opaque strokes, and Oxide's green as the only accent. The accent-color option is gone: selection, the cursor row, focus rings and the active-panel line all read green. The file panel's header strip now steps toward the list, one shade per row (session strip, path bar, column headers), derived by color-mix so every theme, AMOLED included, follows on its own. Everything is square: every rounded utility and the radius tokens are gone, and checkboxes, radios, selects, number spinners and scrollbars are restyled to match (square scrollbars are always visible on macOS, the cost of styling them). Co-Authored-By: Claude <noreply@anthropic.com>
91 lines
3.5 KiB
TypeScript
91 lines
3.5 KiB
TypeScript
import { Component, computed, inject, signal } from '@angular/core';
|
|
import { NgIcon } from '@ng-icons/core';
|
|
import { BRANDING } from '@shared/branding';
|
|
import { LOCALE_LABELS, SUPPORTED } from '@shared/i18n';
|
|
import { COLUMN_KEYS } from '@core/context-menu.service';
|
|
import { I18nService } from '@core/i18n.service';
|
|
import { blockNativeContextMenu, IS_MAC_CHROME } from '@core/platform';
|
|
import { RpcService } from '@core/rpc.service';
|
|
import { SettingsService } from '@core/settings.service';
|
|
import { ShortcutsService } from '@core/shortcuts.service';
|
|
|
|
const SECTIONS = ['appearance', 'transfers', 'editor', 'fileList', 'about'] as const;
|
|
type Section = (typeof SECTIONS)[number];
|
|
|
|
const THEMES = ['system', 'light', 'dark'] as const;
|
|
const DARK_TONES = ['dark', 'darker'] as const;
|
|
const SPEED_UNITS = [
|
|
{ value: 'MBps', label: 'MB/s' },
|
|
{ value: 'Mbps', label: 'Mbps' },
|
|
] as const;
|
|
const TRANSFER_MODES = ['single', 'multi'] as const;
|
|
const CONCURRENCY_CHOICES = [2, 3, 4, 6, 8] as const;
|
|
|
|
/**
|
|
* Root of the Settings window (APP_MODE 'settings'). Every control is a plain
|
|
* SettingsService setter: the save goes to bun, which rebroadcasts it, so the
|
|
* other windows follow live while this one stays open.
|
|
*/
|
|
@Component({
|
|
selector: 'app-root',
|
|
imports: [NgIcon],
|
|
templateUrl: './settings-window.html',
|
|
host: {
|
|
'(window:keydown)': 'onWindowKeydown($event)',
|
|
'(window:contextmenu)': 'blockContextMenu($event)',
|
|
},
|
|
})
|
|
export class SettingsWindow {
|
|
readonly settings = inject(SettingsService);
|
|
readonly i18n = inject(I18nService);
|
|
private readonly rpc = inject(RpcService);
|
|
private readonly shortcuts = inject(ShortcutsService);
|
|
|
|
readonly isMacChrome = IS_MAC_CHROME;
|
|
readonly sections = SECTIONS;
|
|
readonly section = signal<Section>('appearance');
|
|
readonly localeOptions = SUPPORTED;
|
|
readonly localeLabels = LOCALE_LABELS;
|
|
readonly themes = THEMES;
|
|
readonly darkTones = DARK_TONES;
|
|
readonly speedUnits = SPEED_UNITS;
|
|
readonly transferModes = TRANSFER_MODES;
|
|
readonly concurrencyChoices = CONCURRENCY_CHOICES;
|
|
readonly columnKeys = COLUMN_KEYS;
|
|
readonly appName = BRANDING.appName;
|
|
readonly buildLabel = BRANDING.buildLabel;
|
|
/** "100%" for the stepper readout; the value itself lives in settings. */
|
|
readonly scalePercent = computed(() => `${Math.round(this.settings.uiScale() * 100)}%`);
|
|
|
|
/** "Visual Studio Code.app" -> "Visual Studio Code"; empty -> system default. */
|
|
editorAppName(): string {
|
|
const path = this.settings.editorApp();
|
|
if (!path) return this.i18n.t('options.systemDefault');
|
|
const base = path.split('/').filter(Boolean).pop() ?? path;
|
|
return base.replace(/\.app$/i, '');
|
|
}
|
|
|
|
async pickEditorApp(): Promise<void> {
|
|
const res = await this.rpc.request.pickEditorApp({});
|
|
if (res.ok && res.path) this.settings.setEditorApp(res.path);
|
|
}
|
|
|
|
showAbout(): void {
|
|
void this.rpc.request.showAbout({});
|
|
}
|
|
|
|
/** Any-window shortcuts (interface scale); ⌘W arrives as a menu action bun-side. */
|
|
onWindowKeydown(event: KeyboardEvent): void {
|
|
const match = this.shortcuts.resolve(event, 'settings');
|
|
if (!match || this.shortcuts.isTypingTarget(event)) return;
|
|
event.preventDefault();
|
|
if (match.id === 'scale-up') this.settings.setUiScale(this.settings.uiScale() + 0.1);
|
|
else if (match.id === 'scale-down') this.settings.setUiScale(this.settings.uiScale() - 0.1);
|
|
else if (match.id === 'scale-reset') this.settings.setUiScale(1);
|
|
}
|
|
|
|
blockContextMenu(event: Event): void {
|
|
blockNativeContextMenu(event);
|
|
}
|
|
}
|