118 lines
3.2 KiB
TypeScript
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();
|