First step of splitting the UI into two skin trees. All 12 services plus platform.ts, format.ts, file-icons.ts, icons.ts and app.config.ts move to ui/src/app/core/ — the layer both skins will share. This is a pure move: git records all 17 as renames, and no file content changed beyond import paths. The premise holds because `git diff main...feat/7css-skin` touches none of these files — the service layer was already skin-agnostic, so there is nothing to untangle, only to relocate. Components now reach it through a new `@core/*` tsconfig alias rather than a relative path. That is deliberate and load-bearing for the next stage: skin components are about to move under app/skins/<skin>/ at varying depths, and an alias keeps every one of these 67 imports correct wherever its file ends up. `@shared/*` (the bun-side src/shared) already set the precedent. Intra-core imports needed no edits at all — every ./sibling reference among the moved files points at another moved file. Verified: tsc clean, `bun run build:ui` clean (AOT resolves every import and template symbol), and the running window's opaque interior is pixel-identical to a pre-refactor capture. Note on that comparison: the Aero frame and title bar do NOT compare equal between launches, and that is not a regression — .window.glass::before runs at opacity .6, so the chrome composites over whatever desktop is behind the window and shifts by a uniform ~8% brightness. The check therefore diffs the opaque interior (inset 30px sides, 80px top), where anything this refactor could break would show. Co-Authored-By: Claude <noreply@anthropic.com>
58 lines
1.7 KiB
TypeScript
58 lines
1.7 KiB
TypeScript
import { Injectable, inject, signal } from '@angular/core';
|
|
import { RpcService } from './rpc.service';
|
|
|
|
export type UpdaterState = 'idle' | 'available' | 'downloading' | 'ready' | 'error';
|
|
|
|
/**
|
|
* Drives the tab-bar update pill. The backend pushes updateStatus events
|
|
* (available / downloading progress / error); "ready" is derived locally when
|
|
* downloadUpdate resolves ok. Two-step UX: available -> download -> ready ->
|
|
* apply (relaunch).
|
|
*/
|
|
@Injectable({ providedIn: 'root' })
|
|
export class UpdaterService {
|
|
private rpc = inject(RpcService);
|
|
|
|
readonly state = signal<UpdaterState>('idle');
|
|
readonly version = signal('');
|
|
readonly progress = signal(0);
|
|
|
|
constructor() {
|
|
if (!this.rpc.available) return;
|
|
this.rpc.onUpdateStatus((s) => {
|
|
if (s.phase === 'available') {
|
|
this.version.set(s.version ?? '');
|
|
this.state.set('available');
|
|
} else if (s.phase === 'downloading') {
|
|
this.state.set('downloading');
|
|
this.progress.set(s.progress ?? 0);
|
|
} else if (s.phase === 'error') {
|
|
this.state.set('error');
|
|
}
|
|
});
|
|
}
|
|
|
|
async download(): Promise<void> {
|
|
if (this.state() !== 'available') return;
|
|
this.state.set('downloading');
|
|
this.progress.set(0);
|
|
const r = await this.rpc.request.downloadUpdate({});
|
|
this.state.set(r?.ok ? 'ready' : 'error');
|
|
}
|
|
|
|
apply(): void {
|
|
if (this.state() !== 'ready') return;
|
|
void this.rpc.request.applyUpdate({}); // quits + relaunches
|
|
}
|
|
|
|
/** Manual re-check (menu item). */
|
|
async check(): Promise<void> {
|
|
if (!this.rpc.available) return;
|
|
const r = await this.rpc.request.checkForUpdate({});
|
|
if (r?.ok && r.updateAvailable) {
|
|
this.version.set(r.version);
|
|
this.state.set('available');
|
|
}
|
|
}
|
|
}
|