Stage 1: make this branch Windows 7 only
The dual-skin arrangement existed to let one build toggle between the modern
shadcn look and 7.css. Keeping both correct is what forced the cascade tricks
that fight 7.css, so the branch drops the toggle: this build IS the Windows 7
build.
- `skin` is gone from AppSettings/DEFAULT_SETTINGS, the settings store, the
SettingsService (signal, applySkin, persist payload), the options popover
control, and the i18n keys.
- buildPreload adds `.win7` unconditionally; `chromeOptions()` no longer takes a
skin and always returns frameless `titleBarStyle: "hidden"`.
- `IS_WIN7_CHROME` and `IS_MAC_CHROME` are deleted along with every
`isMacChrome` branch in the tab bar, editor window and connection manager.
Those reserved `pl-24` for macOS traffic lights, which frameless chrome
removes on every platform, so the branches were dead by construction.
- The title bar is no longer conditional — it is the window's only chrome.
- index.html keeps just the win7 anti-flash arm (four theme states).
Not done here, deliberately: stripping Tailwind's element styling. The
neutralizers in styles.css (notably `.win7 button { min-width: 0 }`) are still
load-bearing until each component's markup is converted to 7.css's real
contract, so they come out per component rather than in one sweep.
Verified: backend `tsc --noEmit` clean, `bun run build:ui` clean; bundle
490.50 kB, down from 492.52 kB.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -9,11 +9,6 @@
|
||||
elsewhere the native titlebar sits above and this is a compact strip -->
|
||||
<header
|
||||
class="w7-toolbar flex shrink-0 items-center gap-2 border-b border-border bg-chrome pr-2"
|
||||
[class.h-10]="isMacChrome"
|
||||
[class.pl-24]="isMacChrome"
|
||||
[class.electrobun-webkit-app-region-drag]="isMacChrome"
|
||||
[class.h-9]="!isMacChrome"
|
||||
[class.pl-3]="!isMacChrome"
|
||||
>
|
||||
<ng-icon name="mynaServers" size="0.95rem" class="shrink-0 text-muted-foreground" />
|
||||
<h1 class="text-sm font-semibold">{{ i18n.t('connMgr.title') }}</h1>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ConnectionsService } from '../connections.service';
|
||||
import { ConfirmDialog } from '../dialogs/confirm-dialog';
|
||||
import { PromptDialog } from '../dialogs/prompt-dialog';
|
||||
import { DialogsService } from '../dialogs.service';
|
||||
import { blockNativeContextMenu, IS_MAC_CHROME } from '../platform';
|
||||
import { blockNativeContextMenu } from '../platform';
|
||||
import { RpcService } from '../rpc.service';
|
||||
import { SettingsService } from '../settings.service';
|
||||
import { TitleBar } from '../title-bar/title-bar';
|
||||
@@ -84,7 +84,6 @@ export class ConnectionManager {
|
||||
readonly i18n = inject(I18nService);
|
||||
readonly store = inject(ConnectionsService);
|
||||
readonly dialogs = inject(DialogsService);
|
||||
readonly isMacChrome = IS_MAC_CHROME;
|
||||
private readonly rpc = inject(RpcService);
|
||||
// injected for its side effects: fetches settings and applies theme/accent/
|
||||
// text-size data attributes to this window's document root
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
elsewhere the native titlebar sits above and this is a compact strip -->
|
||||
<header
|
||||
class="w7-toolbar flex shrink-0 items-center gap-2 border-b border-border bg-chrome pr-2"
|
||||
[class.h-10]="isMacChrome"
|
||||
[class.pl-24]="isMacChrome"
|
||||
[class.electrobun-webkit-app-region-drag]="isMacChrome"
|
||||
[class.h-9]="!isMacChrome"
|
||||
[class.pl-3]="!isMacChrome"
|
||||
>
|
||||
<ng-icon name="mynaFileText" size="0.95rem" class="shrink-0 text-muted-foreground" />
|
||||
<h1 class="min-w-0 truncate font-mono text-sm font-semibold">
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import { NgIcon } from '@ng-icons/core';
|
||||
import { I18nService } from '../i18n.service';
|
||||
import { ALT_LABEL, blockNativeContextMenu, EDITOR_CONTEXT, IS_MAC_CHROME, MOD_LABEL } from '../platform';
|
||||
import { ALT_LABEL, blockNativeContextMenu, EDITOR_CONTEXT, MOD_LABEL } from '../platform';
|
||||
import { RpcService } from '../rpc.service';
|
||||
import { SettingsService } from '../settings.service';
|
||||
import { ToastService } from '../toast.service';
|
||||
@@ -42,7 +42,6 @@ const DRAFT_DEBOUNCE_MS = 400;
|
||||
export class EditorWindow {
|
||||
readonly i18n = inject(I18nService);
|
||||
readonly ctx = EDITOR_CONTEXT;
|
||||
readonly isMacChrome = IS_MAC_CHROME;
|
||||
readonly modLabel = MOD_LABEL;
|
||||
readonly altLabel = ALT_LABEL;
|
||||
|
||||
|
||||
+1
-22
@@ -8,9 +8,6 @@ declare global {
|
||||
__APP_PLATFORM?: string;
|
||||
/** Which root component this window boots. */
|
||||
__APP_MODE?: 'main' | 'editor' | 'manager';
|
||||
/** Persisted skin — decides the window's native chrome, so it has to be
|
||||
* known before first paint (see chromeOptions in src/bun/windows.ts). */
|
||||
__APP_SKIN?: 'modern' | 'win7';
|
||||
/** Window-kind specific context (EditorOpenContext for editor windows). */
|
||||
__APP_CTX?: unknown;
|
||||
}
|
||||
@@ -20,30 +17,12 @@ export const PLATFORM = typeof window !== 'undefined' ? (window.__APP_PLATFORM ?
|
||||
|
||||
export const IS_MAC = PLATFORM === 'darwin';
|
||||
|
||||
/**
|
||||
* The win7 skin makes the window frameless on EVERY platform and draws its own
|
||||
* Aero title bar, so this is a window-chrome fact, not just a settings value —
|
||||
* it is baked into the preload rather than read from SettingsService, which
|
||||
* resolves too late for layout. Toggling the setting only affects windows
|
||||
* opened afterwards.
|
||||
*/
|
||||
export const IS_WIN7_CHROME =
|
||||
typeof window !== 'undefined' && window.__APP_SKIN === 'win7';
|
||||
|
||||
/**
|
||||
* True when macOS traffic lights overlay the app's header row (the fused
|
||||
* hiddenInset layout, which reserves pl-24 for them). False under the win7
|
||||
* skin even on macOS: titleBarStyle 'hidden' removes the traffic lights, so
|
||||
* reserving space for them would leave a gap.
|
||||
*/
|
||||
export const IS_MAC_CHROME = IS_MAC && !IS_WIN7_CHROME;
|
||||
|
||||
export const IS_WINDOWS = PLATFORM === 'win32';
|
||||
|
||||
export const IS_LINUX = PLATFORM === 'linux';
|
||||
|
||||
/** Shortcut-hint labels: "⌘W" on macOS, "Ctrl+W" elsewhere. Keyed on the real
|
||||
* platform, NOT IS_MAC_CHROME — the keyboard does not change with the skin. */
|
||||
* platform — the keyboard does not change with the window chrome. */
|
||||
export const MOD_LABEL = IS_MAC ? '⌘' : 'Ctrl+';
|
||||
export const ALT_LABEL = IS_MAC ? '⌥' : 'Alt+';
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import { RpcService } from './rpc.service';
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SettingsService {
|
||||
readonly locale = signal<AppSettings['locale']>(DEFAULT_SETTINGS.locale);
|
||||
readonly skin = signal<AppSettings['skin']>(DEFAULT_SETTINGS.skin);
|
||||
readonly theme = signal<AppSettings['theme']>(DEFAULT_SETTINGS.theme);
|
||||
readonly darkTone = signal<AppSettings['darkTone']>(DEFAULT_SETTINGS.darkTone);
|
||||
readonly accent = signal<AppSettings['accent']>(DEFAULT_SETTINGS.accent);
|
||||
@@ -41,7 +40,6 @@ export class SettingsService {
|
||||
.addEventListener('change', (e) => this.systemDark.set(e.matches));
|
||||
// the default accent is not the CSS default — apply before first paint
|
||||
this.applyAccent();
|
||||
this.applySkin();
|
||||
if (!this.rpc.available) return;
|
||||
void this.rpc.request.getSettings({}).then((res) => {
|
||||
if (res.ok) this.applyAll(res.settings);
|
||||
@@ -52,7 +50,6 @@ export class SettingsService {
|
||||
|
||||
private applyAll(settings: AppSettings): void {
|
||||
this.locale.set(settings.locale);
|
||||
this.skin.set(settings.skin);
|
||||
this.theme.set(settings.theme);
|
||||
this.darkTone.set(settings.darkTone);
|
||||
this.accent.set(settings.accent);
|
||||
@@ -65,7 +62,6 @@ export class SettingsService {
|
||||
this.autoClearCompleted.set(settings.autoClearCompleted);
|
||||
this.editor.set(settings.editor);
|
||||
this.editorApp.set(settings.editorApp);
|
||||
this.applySkin();
|
||||
this.applyTheme();
|
||||
this.applyDarkTone();
|
||||
this.applyAccent();
|
||||
@@ -79,12 +75,6 @@ export class SettingsService {
|
||||
void this.persist();
|
||||
}
|
||||
|
||||
setSkin(skin: AppSettings['skin']): void {
|
||||
this.skin.set(skin);
|
||||
this.applySkin();
|
||||
void this.persist();
|
||||
}
|
||||
|
||||
setTheme(theme: AppSettings['theme']): void {
|
||||
this.theme.set(theme);
|
||||
this.applyTheme();
|
||||
@@ -152,16 +142,6 @@ export class SettingsService {
|
||||
void this.persist();
|
||||
}
|
||||
|
||||
/**
|
||||
* The odd one out: a CLASS, not a data-* attribute. 7.scoped.css is
|
||||
* precompiled to require a literal `.win7` ancestor, so the class is not
|
||||
* ours to choose. Default ("modern") = class absent, matching the
|
||||
* delete-when-default rule the other axes follow.
|
||||
*/
|
||||
private applySkin(): void {
|
||||
document.documentElement.classList.toggle('win7', this.skin() === 'win7');
|
||||
}
|
||||
|
||||
private applyTheme(): void {
|
||||
const theme = this.theme();
|
||||
if (theme === 'system') {
|
||||
@@ -205,7 +185,6 @@ export class SettingsService {
|
||||
protected settings(): AppSettings {
|
||||
return {
|
||||
locale: this.locale(),
|
||||
skin: this.skin(),
|
||||
theme: this.theme(),
|
||||
darkTone: this.darkTone(),
|
||||
accent: this.accent(),
|
||||
|
||||
@@ -5,12 +5,11 @@ import { LOCALE_LABELS, SUPPORTED } from '@shared/i18n';
|
||||
import type { AccentTone } from '@shared/types';
|
||||
import { I18nService } from '../i18n.service';
|
||||
import { PanelsService } from '../panels.service';
|
||||
import { IS_MAC_CHROME, IS_WIN7_CHROME, MOD_LABEL } from '../platform';
|
||||
import { MOD_LABEL } from '../platform';
|
||||
import { RpcService } from '../rpc.service';
|
||||
import { SettingsService } from '../settings.service';
|
||||
import { UpdaterService } from '../updater.service';
|
||||
|
||||
const SKINS = ['modern', 'win7'] as const;
|
||||
const THEMES = ['system', 'light', 'dark'] as const;
|
||||
const DARK_TONES = ['dark', 'darker'] as const;
|
||||
// Swatch colors are fixed mid-tones so every dot reads on both light and dark
|
||||
@@ -43,10 +42,7 @@ const CONCURRENCY_CHOICES = [2, 3, 4, 6, 8] as const;
|
||||
traffic lights, and draggable. Elsewhere it's a normal compact row. -->
|
||||
<div
|
||||
class="w7-toolbar relative flex items-center gap-0.5 border-b border-border bg-chrome pr-1.5 electrobun-webkit-app-region-drag"
|
||||
[class.h-10]="isMacChrome"
|
||||
[class.pl-24]="isMacChrome"
|
||||
[class.pl-1.5]="!isMacChrome"
|
||||
[class.pt-1]="!isMacChrome"
|
||||
class="h-9 pt-1 pl-1.5"
|
||||
>
|
||||
<div
|
||||
#tabScroll
|
||||
@@ -181,30 +177,6 @@ const CONCURRENCY_CHOICES = [2, 3, 4, 6, 8] as const;
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mt-2.5 flex items-center justify-between gap-2">
|
||||
<span class="text-xs text-muted-foreground">{{ i18n.t('options.skin.label') }}</span>
|
||||
<div class="flex rounded-md border border-border p-0.5">
|
||||
@for (option of skins; track option) {
|
||||
<button
|
||||
(click)="settings.setSkin(option)"
|
||||
class="rounded px-2 py-0.5 text-[11px]"
|
||||
[class.bg-accent]="settings.skin() === option"
|
||||
[class.text-foreground]="settings.skin() === option"
|
||||
[class.text-muted-foreground]="settings.skin() !== option"
|
||||
>
|
||||
{{ i18n.t('options.skin.' + option) }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<!-- The CSS half of the skin flips live; the window frame does not
|
||||
(titleBarStyle is fixed at construction — see chromeOptions). -->
|
||||
@if (settings.skin() !== bootSkin) {
|
||||
<p class="mt-1 text-right text-[10px] text-muted-foreground">
|
||||
{{ i18n.t('options.skin.restartHint') }}
|
||||
</p>
|
||||
}
|
||||
|
||||
<div class="mt-2.5 flex items-center justify-between gap-2">
|
||||
<span class="text-xs text-muted-foreground">{{ i18n.t('options.theme.label') }}</span>
|
||||
<div class="flex rounded-md border border-border p-0.5">
|
||||
@@ -457,14 +429,11 @@ export class TabBar {
|
||||
readonly settingsOpen = signal(false);
|
||||
readonly localeOptions = SUPPORTED;
|
||||
readonly localeLabels = LOCALE_LABELS;
|
||||
readonly isMacChrome = IS_MAC_CHROME;
|
||||
readonly modLabel = MOD_LABEL;
|
||||
readonly appName = BRANDING.appName;
|
||||
readonly buildLabel = BRANDING.buildLabel;
|
||||
readonly skins = SKINS;
|
||||
/** What the window was BUILT with, not the current setting — the frameless
|
||||
* chrome only changes on reopen, so the hint compares against this. */
|
||||
readonly bootSkin: (typeof SKINS)[number] = IS_WIN7_CHROME ? 'win7' : 'modern';
|
||||
readonly themes = THEMES;
|
||||
readonly darkTones = DARK_TONES;
|
||||
readonly accents = ACCENTS;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Component, inject, input, output, signal } from '@angular/core';
|
||||
import { IS_WIN7_CHROME } from '../platform';
|
||||
import { RpcService } from '../rpc.service';
|
||||
|
||||
/**
|
||||
* The win7 skin's Aero title bar. Rendered by all three window kinds, and only
|
||||
* when `IS_WIN7_CHROME` — under that skin the native frame is gone
|
||||
* (titleBarStyle 'hidden'), so this row IS the window's chrome.
|
||||
* The Aero title bar. Rendered by all three window kinds. The native frame is
|
||||
* gone on every platform (titleBarStyle 'hidden'), so this row IS the window's
|
||||
* chrome.
|
||||
*
|
||||
* The markup is 7.css's contract, not ours: `.title-bar` > `.title-bar-text` +
|
||||
* `.title-bar-controls` > buttons keyed by `aria-label` (Minimize / Maximize /
|
||||
@@ -21,7 +20,6 @@ import { RpcService } from '../rpc.service';
|
||||
@Component({
|
||||
selector: 'app-title-bar',
|
||||
template: `
|
||||
@if (enabled) {
|
||||
<div class="title-bar electrobun-webkit-app-region-drag" (dblclick)="toggle()">
|
||||
<div class="title-bar-text">{{ title() }}</div>
|
||||
<div class="title-bar-controls electrobun-webkit-app-region-no-drag">
|
||||
@@ -35,7 +33,6 @@ import { RpcService } from '../rpc.service';
|
||||
<button aria-label="Close" (click)="closeRequested.emit()"></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class TitleBar {
|
||||
@@ -45,13 +42,12 @@ export class TitleBar {
|
||||
/** Hosts decide what closing means (quit / dirty prompt / managerClose). */
|
||||
readonly closeRequested = output<void>();
|
||||
|
||||
protected readonly enabled = IS_WIN7_CHROME;
|
||||
protected readonly maximized = signal(false);
|
||||
|
||||
private readonly rpc = inject(RpcService);
|
||||
|
||||
constructor() {
|
||||
if (!this.enabled || !this.rpc.available) return;
|
||||
if (!this.rpc.available) return;
|
||||
// No maximize event exists, so seed from a one-shot read and then follow
|
||||
// the resize-derived push (which also covers Aero Snap and Win+Up).
|
||||
void this.rpc.request.windowIsMaximized({}).then((res) => {
|
||||
|
||||
+4
-11
@@ -13,17 +13,10 @@
|
||||
attributes are baked in by the bun preload (buildPreload in
|
||||
src/bun/windows.ts) so forced themes are correct at first paint. -->
|
||||
<style>
|
||||
html { background: oklch(1 0 0); }
|
||||
html[data-theme='dark'] { background: oklch(0.2178 0.002 107); }
|
||||
html[data-theme='dark'][data-dark-tone='darker'] { background: oklch(0 0 0); }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html:not([data-theme='light']) { background: oklch(0.2178 0.002 107); }
|
||||
html:not([data-theme='light'])[data-dark-tone='darker'] { background: oklch(0 0 0); }
|
||||
}
|
||||
/* win7 skin: same four states, mirroring the .win7 --background values
|
||||
in styles.css. The preload adds this class before any page script, so
|
||||
it is already on <html> here. Omitting this arm makes every win7
|
||||
window flash white before styles.css parses. */
|
||||
/* Four theme states, mirroring the .win7 --background values in
|
||||
styles.css. The preload adds the class before any page script, so it is
|
||||
already on <html> here. Omitting this makes every window flash white
|
||||
before styles.css parses. */
|
||||
html.win7 { background: #ffffff; }
|
||||
html.win7[data-theme='dark'] { background: #1f1f1f; }
|
||||
html.win7[data-theme='dark'][data-dark-tone='darker'] { background: #121212; }
|
||||
|
||||
Reference in New Issue
Block a user