Files
website/src/lib/desktop/windows.svelte.ts
T

118 lines
3.2 KiB
TypeScript

export const pageWindowId = 'page';
export interface TerminalWindow {
id: string;
startupCommand: string;
}
export interface ViewerImage {
src: string;
alt: string;
}
export interface ImageViewerWindow {
id: string;
images: ViewerImage[];
currentIndex: number;
}
export interface SystemMonitorWindow {
id: string;
}
export interface FloatingRect {
x: number;
y: number;
width: number;
height: number;
}
class WindowManager {
terminals = $state<TerminalWindow[]>([]);
imageViewers = $state<ImageViewerWindow[]>([]);
systemMonitors = $state<SystemMonitorWindow[]>([]);
floatingRects = $state<Record<string, FloatingRect | null>>({});
focusedId = $state<string>(pageWindowId);
maximizedId = $state<string | null>(null);
launcherOpen = $state(false);
private nextTerminalNumber = 1;
private nextImageViewerNumber = 1;
private nextSystemMonitorNumber = 1;
get extraWindowIds(): string[] {
return [
...this.terminals.map((terminal) => terminal.id),
...this.imageViewers.map((viewer) => viewer.id),
...this.systemMonitors.map((monitor) => monitor.id)
];
}
isFloating(id: string): boolean {
return id in this.floatingRects;
}
openTerminal(startupCommand = '') {
const terminal = { id: `kitty-${this.nextTerminalNumber++}`, startupCommand };
this.terminals.push(terminal);
this.focus(terminal.id);
}
openImageViewer(images: ViewerImage[], currentIndex: number) {
const viewer = { id: `imv-${this.nextImageViewerNumber++}`, images, currentIndex };
this.imageViewers.push(viewer);
this.focus(viewer.id);
}
openSystemMonitor() {
const monitor = { id: `btop-${this.nextSystemMonitorNumber++}` };
this.systemMonitors.push(monitor);
this.focus(monitor.id);
}
closeWindow(id: string) {
this.terminals = this.terminals.filter((terminal) => terminal.id !== id);
this.imageViewers = this.imageViewers.filter((viewer) => viewer.id !== id);
this.systemMonitors = this.systemMonitors.filter((monitor) => monitor.id !== id);
delete this.floatingRects[id];
if (this.maximizedId === id) this.maximizedId = null;
if (this.focusedId === id) this.focusedId = this.extraWindowIds.at(-1) ?? pageWindowId;
}
closeAllTerminals() {
this.terminals = [];
if (this.maximizedId?.startsWith('kitty-')) this.maximizedId = null;
if (this.focusedId.startsWith('kitty-')) this.focusedId = this.extraWindowIds.at(-1) ?? pageWindowId;
}
focus(id: string) {
this.focusedId = id;
if (this.maximizedId && this.maximizedId !== id) this.maximizedId = null;
}
toggleFloating(id: string) {
if (this.isFloating(id)) delete this.floatingRects[id];
else this.floatingRects[id] = null;
if (this.maximizedId === id) this.maximizedId = null;
this.focusedId = id;
}
setFloatingRect(id: string, rect: FloatingRect) {
if (this.isFloating(id)) this.floatingRects[id] = rect;
}
toggleMaximize(id: string) {
this.maximizedId = this.maximizedId === id ? null : id;
this.focusedId = id;
}
cycleFocus(openWindowIds: string[], step: number) {
if (openWindowIds.length === 0) return;
const index = openWindowIds.indexOf(this.focusedId);
const nextIndex = (index + step + openWindowIds.length) % openWindowIds.length;
this.focus(openWindowIds[nextIndex]);
}
}
export const windowManager = new WindowManager();