Text size setting for path bar + file list (Compact/Default/Large)
Segmented row in the options popover; applied via data-text on <html> driving CSS variables (--list-font / --list-meta-font / --path-font), persisted in settings.json like theme and speed unit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,11 +44,14 @@ export interface ConnectionsFile {
|
|||||||
export interface AppSettings {
|
export interface AppSettings {
|
||||||
theme: "system" | "light" | "dark";
|
theme: "system" | "light" | "dark";
|
||||||
speedUnit: "MBps" | "Mbps";
|
speedUnit: "MBps" | "Mbps";
|
||||||
|
/** Font size of the path bar and file list. */
|
||||||
|
textSize: "compact" | "default" | "large";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_SETTINGS: AppSettings = {
|
export const DEFAULT_SETTINGS: AppSettings = {
|
||||||
theme: "system",
|
theme: "system",
|
||||||
speedUnit: "MBps",
|
speedUnit: "MBps",
|
||||||
|
textSize: "default",
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TransferState =
|
export type TransferState =
|
||||||
|
|||||||
@@ -144,7 +144,7 @@
|
|||||||
(keydown.escape)="resetPath()"
|
(keydown.escape)="resetPath()"
|
||||||
(keydown)="$event.stopPropagation()"
|
(keydown)="$event.stopPropagation()"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
class="h-7 min-w-0 flex-1 rounded-md border border-input bg-transparent px-2 font-mono text-xs outline-none focus:ring-2 focus:ring-ring"
|
class="h-7 min-w-0 flex-1 rounded-md border border-input bg-transparent px-2 font-mono text-(length:--path-font) outline-none focus:ring-2 focus:ring-ring"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
(click)="panel().refresh()"
|
(click)="panel().refresh()"
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
[attr.data-index]="i"
|
[attr.data-index]="i"
|
||||||
(click)="rowClick($event, i)"
|
(click)="rowClick($event, i)"
|
||||||
(dblclick)="openAt(i)"
|
(dblclick)="openAt(i)"
|
||||||
class="grid h-7 grid-cols-[minmax(0,1fr)_5.5rem_8.5rem] items-center text-[13px]"
|
class="grid h-7 grid-cols-[minmax(0,1fr)_5.5rem_8.5rem] items-center text-(length:--list-font)"
|
||||||
[class.bg-accent]="i === panel().cursor() && !isSelected(entry)"
|
[class.bg-accent]="i === panel().cursor() && !isSelected(entry)"
|
||||||
[class.bg-primary/15]="isSelected(entry)"
|
[class.bg-primary/15]="isSelected(entry)"
|
||||||
[class.ring-1]="i === panel().cursor() && isActive()"
|
[class.ring-1]="i === panel().cursor() && isActive()"
|
||||||
@@ -202,10 +202,10 @@
|
|||||||
/>
|
/>
|
||||||
<span class="truncate" [class.italic]="entry.type === 'symlink'">{{ entry.name }}</span>
|
<span class="truncate" [class.italic]="entry.type === 'symlink'">{{ entry.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="px-2 text-right font-mono text-xs tabular-nums text-muted-foreground">
|
<span class="px-2 text-right font-mono text-(length:--list-meta-font) tabular-nums text-muted-foreground">
|
||||||
{{ entry.type === 'dir' ? '—' : formatSize(entry.size) }}
|
{{ entry.type === 'dir' ? '—' : formatSize(entry.size) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="px-2 font-mono text-xs tabular-nums text-muted-foreground">
|
<span class="px-2 font-mono text-(length:--list-meta-font) tabular-nums text-muted-foreground">
|
||||||
{{ formatDate(entry.mtime) }}
|
{{ formatDate(entry.mtime) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { RpcService } from './rpc.service';
|
|||||||
export class SettingsService {
|
export class SettingsService {
|
||||||
readonly theme = signal<AppSettings['theme']>(DEFAULT_SETTINGS.theme);
|
readonly theme = signal<AppSettings['theme']>(DEFAULT_SETTINGS.theme);
|
||||||
readonly speedUnit = signal<AppSettings['speedUnit']>(DEFAULT_SETTINGS.speedUnit);
|
readonly speedUnit = signal<AppSettings['speedUnit']>(DEFAULT_SETTINGS.speedUnit);
|
||||||
|
readonly textSize = signal<AppSettings['textSize']>(DEFAULT_SETTINGS.textSize);
|
||||||
|
|
||||||
private readonly rpc = inject(RpcService);
|
private readonly rpc = inject(RpcService);
|
||||||
|
|
||||||
@@ -15,7 +16,9 @@ export class SettingsService {
|
|||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
this.theme.set(res.settings.theme);
|
this.theme.set(res.settings.theme);
|
||||||
this.speedUnit.set(res.settings.speedUnit);
|
this.speedUnit.set(res.settings.speedUnit);
|
||||||
|
this.textSize.set(res.settings.textSize);
|
||||||
this.applyTheme();
|
this.applyTheme();
|
||||||
|
this.applyTextSize();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +33,12 @@ export class SettingsService {
|
|||||||
void this.persist();
|
void this.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTextSize(size: AppSettings['textSize']): void {
|
||||||
|
this.textSize.set(size);
|
||||||
|
this.applyTextSize();
|
||||||
|
void this.persist();
|
||||||
|
}
|
||||||
|
|
||||||
private applyTheme(): void {
|
private applyTheme(): void {
|
||||||
const theme = this.theme();
|
const theme = this.theme();
|
||||||
if (theme === 'system') {
|
if (theme === 'system') {
|
||||||
@@ -39,9 +48,24 @@ export class SettingsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private applyTextSize(): void {
|
||||||
|
const size = this.textSize();
|
||||||
|
if (size === 'default') {
|
||||||
|
delete document.documentElement.dataset['text'];
|
||||||
|
} else {
|
||||||
|
document.documentElement.dataset['text'] = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected settings(): AppSettings {
|
||||||
|
return {
|
||||||
|
theme: this.theme(),
|
||||||
|
speedUnit: this.speedUnit(),
|
||||||
|
textSize: this.textSize(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async persist(): Promise<void> {
|
private async persist(): Promise<void> {
|
||||||
await this.rpc.request.saveSettings({
|
await this.rpc.request.saveSettings({ settings: this.settings() });
|
||||||
settings: { theme: this.theme(), speedUnit: this.speedUnit() },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const SPEED_UNITS = [
|
|||||||
{ value: 'MBps', label: 'MB/s' },
|
{ value: 'MBps', label: 'MB/s' },
|
||||||
{ value: 'Mbps', label: 'Mbps' },
|
{ value: 'Mbps', label: 'Mbps' },
|
||||||
] as const;
|
] as const;
|
||||||
|
const TEXT_SIZES = ['compact', 'default', 'large'] as const;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tab-bar',
|
selector: 'app-tab-bar',
|
||||||
@@ -102,6 +103,23 @@ const SPEED_UNITS = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2.5 flex items-center justify-between gap-2">
|
||||||
|
<span class="text-xs text-muted-foreground">Text size</span>
|
||||||
|
<div class="flex rounded-md border border-border p-0.5">
|
||||||
|
@for (size of textSizes; track size) {
|
||||||
|
<button
|
||||||
|
(click)="settings.setTextSize(size)"
|
||||||
|
class="rounded px-2 py-0.5 text-[11px] capitalize"
|
||||||
|
[class.bg-accent]="settings.textSize() === size"
|
||||||
|
[class.text-foreground]="settings.textSize() === size"
|
||||||
|
[class.text-muted-foreground]="settings.textSize() !== size"
|
||||||
|
>
|
||||||
|
{{ size }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-2.5 flex items-center justify-between gap-2">
|
<div class="mt-2.5 flex items-center justify-between gap-2">
|
||||||
<span class="text-xs text-muted-foreground">Speed unit</span>
|
<span class="text-xs text-muted-foreground">Speed unit</span>
|
||||||
<div class="flex rounded-md border border-border p-0.5">
|
<div class="flex rounded-md border border-border p-0.5">
|
||||||
@@ -130,6 +148,7 @@ export class TabBar {
|
|||||||
readonly isMacChrome = IS_MAC_CHROME;
|
readonly isMacChrome = IS_MAC_CHROME;
|
||||||
readonly themes = THEMES;
|
readonly themes = THEMES;
|
||||||
readonly speedUnits = SPEED_UNITS;
|
readonly speedUnits = SPEED_UNITS;
|
||||||
|
readonly textSizes = TEXT_SIZES;
|
||||||
|
|
||||||
close(event: MouseEvent, id: number): void {
|
close(event: MouseEvent, id: number): void {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|||||||
@@ -100,6 +100,23 @@
|
|||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Text-size setting for the path bar + file list (data-text set by SettingsService). */
|
||||||
|
:root {
|
||||||
|
--list-font: 13px;
|
||||||
|
--list-meta-font: 12px;
|
||||||
|
--path-font: 12px;
|
||||||
|
}
|
||||||
|
:root[data-text='compact'] {
|
||||||
|
--list-font: 12px;
|
||||||
|
--list-meta-font: 11px;
|
||||||
|
--path-font: 11px;
|
||||||
|
}
|
||||||
|
:root[data-text='large'] {
|
||||||
|
--list-font: 14px;
|
||||||
|
--list-meta-font: 13px;
|
||||||
|
--path-font: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
Reference in New Issue
Block a user