From 461bd326e921d788ce0c45273580748d81e37eaf Mon Sep 17 00:00:00 2001 From: Blockyheadman <80011716+Blockyheadman@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:52:00 -0500 Subject: [PATCH 1/6] electron support (POC) --- src/apps/core/loginapp/runtime.ts | 4 +++- src/ts/daemon/contexts/notifications.ts | 3 +++ src/ts/electron.ts | 1 + src/types/electron.ts | 6 ++++++ src/window.d.ts | 4 ++++ 5 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/ts/electron.ts create mode 100644 src/types/electron.ts diff --git a/src/apps/core/loginapp/runtime.ts b/src/apps/core/loginapp/runtime.ts index a15996274..cc65d8a39 100755 --- a/src/apps/core/loginapp/runtime.ts +++ b/src/apps/core/loginapp/runtime.ts @@ -25,6 +25,7 @@ import dayjs from "dayjs"; import Cookies from "js-cookie"; import { LoginUserDaemonStartOptions } from "./store"; import type { LoginAppProps, PersistenceInfo } from "./types"; +import { IsElectron } from "$ts/electron"; export class LoginAppRuntime extends AppProcess implements ILoginAppRuntime { public DEFAULT_WALLPAPER = Store(""); @@ -251,7 +252,8 @@ export class LoginAppRuntime extends AppProcess implements ILoginAppRuntime { broadcast("Stopping User Daemon"); await userDaemon.killSelf(); } - State?.loadState("turnedOff"); + await State?.loadState("turnedOff"); + if (IsElectron()) electron.closeWindow(); } async restart(userDaemon?: IUserDaemon) { diff --git a/src/ts/daemon/contexts/notifications.ts b/src/ts/daemon/contexts/notifications.ts index 4517164fd..6a57927e0 100644 --- a/src/ts/daemon/contexts/notifications.ts +++ b/src/ts/daemon/contexts/notifications.ts @@ -1,5 +1,6 @@ import type { INotificationsUserContext } from "$interfaces/contexts/INotificationsUserContext"; import type { IUserDaemon } from "$interfaces/IUserDaemon"; +import { IsElectron } from "$ts/electron"; import { SysDispatch } from "$ts/env"; import type { Notification } from "$types/system/notification"; import { UserContext } from "../context"; @@ -24,6 +25,8 @@ export class NotificationsUserContext extends UserContext implements INotificati SysDispatch.dispatch("update-notifications", [this.notifications]); SysDispatch.dispatch("send-notification", [data]); + if (IsElectron()) electron.sendNotification(data); + return id; } diff --git a/src/ts/electron.ts b/src/ts/electron.ts new file mode 100644 index 000000000..4321ce8d3 --- /dev/null +++ b/src/ts/electron.ts @@ -0,0 +1 @@ +export const IsElectron = () => !!(window as any)["electron"] && navigator.userAgent.includes("Electron"); diff --git a/src/types/electron.ts b/src/types/electron.ts new file mode 100644 index 000000000..6109d8e15 --- /dev/null +++ b/src/types/electron.ts @@ -0,0 +1,6 @@ +import type { Notification } from "$types/system/notification"; + +export interface ElectronIPC { + closeWindow(): void; + sendNotification(data: Notification): void; +} diff --git a/src/window.d.ts b/src/window.d.ts index 9f5e8bab2..394d5d168 100644 --- a/src/window.d.ts +++ b/src/window.d.ts @@ -1,8 +1,12 @@ +import type { ElectronIPC } from "$types/electron"; + declare global { interface Window { __DW_INIT__: boolean; __DW_STATUS__: string; } + + export const electron: ElectronIPC | undefined; } export {}; From 3a52cfde23903427f222e660718009b7ba550646 Mon Sep 17 00:00:00 2001 From: Blockyheadman <80011716+Blockyheadman@users.noreply.github.com> Date: Wed, 8 Jul 2026 12:54:37 -0500 Subject: [PATCH 2/6] add close button shutdown support, desktop notifications, and progress bar support (i think) --- src/apps/components/fsprogress/runtime.ts | 9 +++++ src/apps/core/loginapp/runtime.ts | 5 ++- src/ts/daemon/contexts/notifications.ts | 9 ++++- src/ts/kernel/wavekernel.ts | 46 ++++++++++++++++++++++- src/types/electron.ts | 3 ++ 5 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/apps/components/fsprogress/runtime.ts b/src/apps/components/fsprogress/runtime.ts index f4a0f45f0..dd7f3501e 100755 --- a/src/apps/components/fsprogress/runtime.ts +++ b/src/apps/components/fsprogress/runtime.ts @@ -1,5 +1,6 @@ import type { IFsProgressRuntime } from "$interfaces/runtimes/IFsProgressRuntime"; import { AppProcess } from "$ts/apps/process"; +import { IsElectron } from "$ts/electron"; import { Stack } from "$ts/env"; import { Store } from "$ts/writable"; import type { AppProcessData } from "$types/apps/app"; @@ -29,6 +30,10 @@ export class FsProgressRuntime extends AppProcess implements IFsProgressRuntime this.windowTitle.set(v.caption); this.windowIcon.set(v.icon); + if (IsElectron()) { + electron!.updateIconProgress(v.done, v.max); + } + if (v.done >= v.max && v.max > 0 && !v.errors.length) { await this.closeWindow(); // Close the window if pending operations are done @@ -48,6 +53,10 @@ export class FsProgressRuntime extends AppProcess implements IFsProgressRuntime async onClose(): Promise { if (this.parentPid) Stack.renderer?.focusedPid.set(this.parentPid); // Focus the parent PID upon close + if (IsElectron()) { + electron!.updateIconProgress(0, 0); + } + return true; } diff --git a/src/apps/core/loginapp/runtime.ts b/src/apps/core/loginapp/runtime.ts index cc65d8a39..ac49d7af8 100755 --- a/src/apps/core/loginapp/runtime.ts +++ b/src/apps/core/loginapp/runtime.ts @@ -253,7 +253,10 @@ export class LoginAppRuntime extends AppProcess implements ILoginAppRuntime { await userDaemon.killSelf(); } await State?.loadState("turnedOff"); - if (IsElectron()) electron.closeWindow(); + if (IsElectron()) { + electron!.setCanClose(true); + electron!.closeWindow(); + } } async restart(userDaemon?: IUserDaemon) { diff --git a/src/ts/daemon/contexts/notifications.ts b/src/ts/daemon/contexts/notifications.ts index 6a57927e0..6f80d2ba5 100644 --- a/src/ts/daemon/contexts/notifications.ts +++ b/src/ts/daemon/contexts/notifications.ts @@ -1,7 +1,9 @@ import type { INotificationsUserContext } from "$interfaces/contexts/INotificationsUserContext"; import type { IUserDaemon } from "$interfaces/IUserDaemon"; +import { Logo } from "$ts/branding"; import { IsElectron } from "$ts/electron"; import { SysDispatch } from "$ts/env"; +import type { IconService } from "$ts/servicehost/services/IconService"; import type { Notification } from "$types/system/notification"; import { UserContext } from "../context"; @@ -25,8 +27,13 @@ export class NotificationsUserContext extends UserContext implements INotificati SysDispatch.dispatch("update-notifications", [this.notifications]); SysDispatch.dispatch("send-notification", [data]); - if (IsElectron()) electron.sendNotification(data); + const iconService = this.serviceHost?.getService("IconService"); + if (IsElectron()) { + electron!.sendNotification(data); + // if (iconService && data.icon) electron.sendNotification(data, await iconService.getIcon(data.icon)); + // else electron.sendNotification(data, Logo()); + } return id; } diff --git a/src/ts/kernel/wavekernel.ts b/src/ts/kernel/wavekernel.ts index 3e16f585a..711c43174 100644 --- a/src/ts/kernel/wavekernel.ts +++ b/src/ts/kernel/wavekernel.ts @@ -3,12 +3,14 @@ import type { IWaveKernel } from "$interfaces/IWaveKernel"; import type { IProcessHandler } from "$interfaces/modules/IProcessHandler"; import type { ISystemDispatch } from "$interfaces/modules/ISystemDispatch"; import { __Console__ } from "$ts/console"; -import { ArcOSVersion, SetCurrentKernel, SetKernelExports } from "$ts/env"; +import { IsElectron } from "$ts/electron"; +import { ArcOSVersion, Daemon, Kernel, SetCurrentKernel, SetKernelExports } from "$ts/env"; import { JsExec } from "$ts/jsexec"; import { getBuild } from "$ts/metadata/build"; import { ChangeLogs } from "$ts/metadata/changelog"; import { getLicense } from "$ts/metadata/license"; import { getMode } from "$ts/metadata/mode"; +import type { ArcTerminal } from "$ts/terminal"; import { LogLevel, ShortLogLevelCaptions, type LogItem } from "../../types/shared/logging"; import { handleGlobalErrors } from "../error"; import { InitProcess } from "./init"; @@ -96,6 +98,48 @@ export class WaveKernel implements IWaveKernel { await this.init?.jumpstart(); __Console__.timeEnd("** Kernel init"); + + if (IsElectron()) { + electron!.onPowerOff(async () => { + switch (this.state?.currentState) { + case "desktop": + await Daemon.power?.shutdown(); + break; + + case "boot": + case "turnedOff": + electron!.setCanClose(true); + electron!.closeWindow(); + break; + + case "arcterm": + function getSubProcesses(parentPid: number) { + const subProcesses = stack.getSubProcesses(parentPid); + for (const [key, val] of subProcesses) { + if (val.name === "ArcTerminal") { + (val as ArcTerminal).processLine("exit"); + + electron!.setCanClose(true); + setInterval(() => { + if (Kernel.state?.currentState === "turnedOff") electron!.closeWindow(); + Kernel.Log("v7-electron", "Attempting to close window..", LogLevel.info); + }, 100); + break; + } + + getSubProcesses(key); + } + } + + getSubProcesses(this.initPid); + break; + + case "login": + default: + break; + } + }); + } } getModule(id: string, dontCrash = false): T { diff --git a/src/types/electron.ts b/src/types/electron.ts index 6109d8e15..6c13e2c3a 100644 --- a/src/types/electron.ts +++ b/src/types/electron.ts @@ -3,4 +3,7 @@ import type { Notification } from "$types/system/notification"; export interface ElectronIPC { closeWindow(): void; sendNotification(data: Notification): void; + updateIconProgress(progress: number, max: number): void; + onPowerOff(callback: () => void): void; + setCanClose(canClose: boolean): void; } From 9a4f7e2afa4491fb49cd4f0ff38255acc1c3a674 Mon Sep 17 00:00:00 2001 From: Blockyheadman <80011716+Blockyheadman@users.noreply.github.com> Date: Thu, 9 Jul 2026 04:02:07 -0500 Subject: [PATCH 3/6] fuckin custom titlebar :p --- index.html | 62 ++++++++------- src/apps/core/loginapp/runtime.ts | 5 -- src/css/apps/core/bootscreen.css | 10 +-- src/css/electron.css | 105 +++++++++++++++++++++++++ src/css/main.css | 8 +- src/lib/ElectronTitleBar.svelte | 28 +++++++ src/main.ts | 3 + src/ts/apps/renderer.ts | 5 ++ src/ts/daemon/contexts/apprenderer.ts | 7 ++ src/ts/kernel/wavekernel.ts | 45 +---------- src/ts/state/index.ts | 6 ++ src/types/electron.ts | 109 ++++++++++++++++++++++++++ 12 files changed, 307 insertions(+), 86 deletions(-) create mode 100644 src/css/electron.css create mode 100644 src/lib/ElectronTitleBar.svelte diff --git a/index.html b/index.html index d3aceaf06..323b5c683 100755 --- a/index.html +++ b/index.html @@ -1,33 +1,35 @@ - - - - - - - - - ArcOS - - - - -
-
-
.
-
-
- - - - + + + + + + + + + ArcOS + + + + + + +
+
+
.
+
+
+ + + + + \ No newline at end of file diff --git a/src/apps/core/loginapp/runtime.ts b/src/apps/core/loginapp/runtime.ts index ac49d7af8..f0cf71fdf 100755 --- a/src/apps/core/loginapp/runtime.ts +++ b/src/apps/core/loginapp/runtime.ts @@ -25,7 +25,6 @@ import dayjs from "dayjs"; import Cookies from "js-cookie"; import { LoginUserDaemonStartOptions } from "./store"; import type { LoginAppProps, PersistenceInfo } from "./types"; -import { IsElectron } from "$ts/electron"; export class LoginAppRuntime extends AppProcess implements ILoginAppRuntime { public DEFAULT_WALLPAPER = Store(""); @@ -253,10 +252,6 @@ export class LoginAppRuntime extends AppProcess implements ILoginAppRuntime { await userDaemon.killSelf(); } await State?.loadState("turnedOff"); - if (IsElectron()) { - electron!.setCanClose(true); - electron!.closeWindow(); - } } async restart(userDaemon?: IUserDaemon) { diff --git a/src/css/apps/core/bootscreen.css b/src/css/apps/core/bootscreen.css index 2a879f793..a3e0f3cb6 100755 --- a/src/css/apps/core/bootscreen.css +++ b/src/css/apps/core/bootscreen.css @@ -3,7 +3,7 @@ --bg: #000; } -div.window#bootScreen > div.body { +div.window#bootScreen>div.body { display: flex; justify-content: center; align-items: center; @@ -14,11 +14,11 @@ div.window#bootScreen > div.body { justify-content: space-around; } -div.window#bootScreen div.body > img { +div.window#bootScreen div.body>img { height: 200px; } -div.window#bootScreen div.body > img.gray { +div.window#bootScreen div.body>img.gray { filter: grayscale(50%); background-image: url("../../../assets/branding/glow-red.png"); } @@ -58,7 +58,7 @@ div.window#bootScreen div.bottom { } div.window#bootScreen div.top { - position: fixed; + position: absolute; top: 0; left: 0; width: 100%; @@ -83,4 +83,4 @@ div.window#bootScreen div.top div.keys div.row div.key { font-weight: bold; opacity: 0.5; width: 20px; -} +} \ No newline at end of file diff --git a/src/css/electron.css b/src/css/electron.css new file mode 100644 index 000000000..659e02a86 --- /dev/null +++ b/src/css/electron.css @@ -0,0 +1,105 @@ +@import url("./themes.css"); + +@property --electron-titlebar-height { + syntax: " | "; + inherits: false; + initial-value: 40px; +} + +:root { + --electron-titlebar-height: 2.5rem; +} + +body.electron-app { + background-color: transparent; + overflow: hidden; + transform: translateZ(0); + border-radius: 0px; + + @media (prefers-reduced-motion: no-preference) { + transition: border-radius .2s; + } + +} + +/* Please, PLEASE don't judge me for this. it works. + * It's just a way for me to check if the titlebar is + * hidden or not and affect the apprenderer size + */ +body:has(#electron-titlebar.hidden) #appRenderer.electron-app { + height: 100%; +} + +body.electron-app.rounded { + border-radius: var(--win-border-rad) +} + +#appRenderer.electron-app { + top: unset; + bottom: 0; + height: calc(100% - var(--electron-titlebar-height)); +} + +#electron-titlebar { + display: flex; + position: relative; + top: 0; + + height: var(--electron-titlebar-height); + width: 100%; + + z-index: 10000 !important; + padding: .75rem; + + app-region: drag; + + user-select: none; + + background-color: var(--glass-bg); + backdrop-filter: blur(4px); + + &.hidden { + display: none; + + ~#appRenderer.electron-app { + height: 100%; + } + } + +} + +#electron-titlebar>.window-title { + flex: 1; + display: inline-flex; + gap: .5rem; + + &>img { + height: 100%; + } + + &>span { + align-self: center; + font-size: 1.15rem; + } +} + +#electron-titlebar>.controls { + align-self: center; + + & button { + margin: 0; + padding: .15rem; + font-size: 1.15rem; + border: none; + aspect-ratio: 1; + background-color: transparent; + color: var(--fg); + transition: opacity 0.2s; + + app-region: no-drag; + + &:hover { + color: var(--accent-light); + } + } +} \ No newline at end of file diff --git a/src/css/main.css b/src/css/main.css index 79140df3f..6905040ba 100755 --- a/src/css/main.css +++ b/src/css/main.css @@ -23,12 +23,13 @@ @import url("./state/serverdown.css"); @import url("./state/turnedoff.css"); @import url("./state/arcterm.css"); +@import url("./electron.css"); html, body { width: 100%; height: 100%; - background-color: #000; + background-color: transparent; color: var(--fg, #fff); margin: 0; overflow: hidden; @@ -85,7 +86,7 @@ main#main.hidden #appRenderer { font-family: var(--user-font, "Nunito Sans"), "Nunito Sans"; } -#appRenderer > div.window:not(.core) { +#appRenderer>div.window:not(.core) { pointer-events: initial; } @@ -99,6 +100,7 @@ main#main.hidden #appRenderer { opacity: 1; } } + .blink { animation: blink 1s step-start 0s infinite; } @@ -131,4 +133,4 @@ span.beta-pill { [class*="theme-"] { color: var(--fg); -} +} \ No newline at end of file diff --git a/src/lib/ElectronTitleBar.svelte b/src/lib/ElectronTitleBar.svelte new file mode 100644 index 000000000..e3ef75f84 --- /dev/null +++ b/src/lib/ElectronTitleBar.svelte @@ -0,0 +1,28 @@ + + +
+
+ ArcOS logo + {document.title} +
+
+ + + +
+
diff --git a/src/main.ts b/src/main.ts index 8368727d2..8fe5805d2 100755 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,11 @@ import "dseg/css/dseg.css"; import "./css/main.css"; +import { handleElectronInit } from "$types/electron"; // CODE EXECUTION STARTS HERE async function Main() { + await handleElectronInit(); + const { WaveKernel } = await import("$ts/kernel/wavekernel"); const kernel = new WaveKernel(); diff --git a/src/ts/apps/renderer.ts b/src/ts/apps/renderer.ts index 13307fd35..66265618d 100755 --- a/src/ts/apps/renderer.ts +++ b/src/ts/apps/renderer.ts @@ -18,6 +18,7 @@ import { Store } from "../writable"; import { AppRendererError } from "./error"; import { BuiltinAppImportPathAbsolutes } from "./store"; import { Sleep } from "$ts/sleep"; +import { IsElectron } from "$ts/electron"; export class AppRenderer extends Process implements IAppRenderer { currentState: number[] = []; @@ -66,6 +67,10 @@ export class AppRenderer extends Process implements IAppRenderer { if (process._disposed) return; + if (IsElectron()) { + this.target.classList.add("electron-app"); + } + this.Log(`Rendering PID ${process.pid}`); renderTarget ||= this.target; diff --git a/src/ts/daemon/contexts/apprenderer.ts b/src/ts/daemon/contexts/apprenderer.ts index 10f9a110a..ccc29eff7 100644 --- a/src/ts/daemon/contexts/apprenderer.ts +++ b/src/ts/daemon/contexts/apprenderer.ts @@ -1,5 +1,6 @@ import type { IAppRendererUserContext } from "$interfaces/contexts/IAppRendererUserContext"; import type { IUserDaemon } from "$interfaces/IUserDaemon"; +import { IsElectron } from "$ts/electron"; import { Daemon, Stack } from "$ts/env"; import { bestForeground, darkenColor, hex3to6, invertColor, lightenColor } from "$ts/util/color"; import type { CustomStylePreferences, UserPreferences } from "$types/user"; @@ -59,6 +60,12 @@ export class AppRendererUserContext extends UserContext implements IAppRendererU renderer.classList.toggle("safe-mode", this.safeMode); renderer.classList.toggle("traffic-lights", v.shell.visuals.trafficLights); renderer.classList.toggle("hide-altmenus", v.shell.visuals.hideAltmenus); + + if (IsElectron()) { + renderer.classList.add("electron-app"); + const titleBar = document.getElementById("electron-titlebar")! as HTMLDivElement; + titleBar.setAttribute("style", style); + } } setUserStyleLoader(style: CustomStylePreferences) { diff --git a/src/ts/kernel/wavekernel.ts b/src/ts/kernel/wavekernel.ts index 711c43174..d907a7aca 100644 --- a/src/ts/kernel/wavekernel.ts +++ b/src/ts/kernel/wavekernel.ts @@ -1,10 +1,11 @@ +import type { LoginAppRuntime } from "$apps/core/loginapp/runtime"; import type { IStateHandler } from "$interfaces/IStateHandler"; import type { IWaveKernel } from "$interfaces/IWaveKernel"; import type { IProcessHandler } from "$interfaces/modules/IProcessHandler"; import type { ISystemDispatch } from "$interfaces/modules/ISystemDispatch"; import { __Console__ } from "$ts/console"; import { IsElectron } from "$ts/electron"; -import { ArcOSVersion, Daemon, Kernel, SetCurrentKernel, SetKernelExports } from "$ts/env"; +import { ArcOSVersion, Daemon, Kernel, SetCurrentKernel, SetKernelExports, Stack } from "$ts/env"; import { JsExec } from "$ts/jsexec"; import { getBuild } from "$ts/metadata/build"; import { ChangeLogs } from "$ts/metadata/changelog"; @@ -98,48 +99,6 @@ export class WaveKernel implements IWaveKernel { await this.init?.jumpstart(); __Console__.timeEnd("** Kernel init"); - - if (IsElectron()) { - electron!.onPowerOff(async () => { - switch (this.state?.currentState) { - case "desktop": - await Daemon.power?.shutdown(); - break; - - case "boot": - case "turnedOff": - electron!.setCanClose(true); - electron!.closeWindow(); - break; - - case "arcterm": - function getSubProcesses(parentPid: number) { - const subProcesses = stack.getSubProcesses(parentPid); - for (const [key, val] of subProcesses) { - if (val.name === "ArcTerminal") { - (val as ArcTerminal).processLine("exit"); - - electron!.setCanClose(true); - setInterval(() => { - if (Kernel.state?.currentState === "turnedOff") electron!.closeWindow(); - Kernel.Log("v7-electron", "Attempting to close window..", LogLevel.info); - }, 100); - break; - } - - getSubProcesses(key); - } - } - - getSubProcesses(this.initPid); - break; - - case "login": - default: - break; - } - }); - } } getModule(id: string, dontCrash = false): T { diff --git a/src/ts/state/index.ts b/src/ts/state/index.ts index a9845089d..f3c257b03 100755 --- a/src/ts/state/index.ts +++ b/src/ts/state/index.ts @@ -1,6 +1,7 @@ import type { IAppProcess, IAppProcessConstructor } from "$interfaces/IAppProcess"; import type { IStateHandler } from "$interfaces/IStateHandler"; import type { IProcessHandler } from "$interfaces/modules/IProcessHandler"; +import { IsElectron } from "$ts/electron"; import { getKMod, Kernel, Stack } from "$ts/env"; import { LogLevel } from "$types/shared/logging"; import type { State } from "../../types/system/state"; @@ -94,6 +95,11 @@ export class StateHandler extends Process implements IStateHandler { this.currentState = id; + if (id == "turnedOff" && IsElectron()) { + electron!.setCanClose(true); + electron!.closeWindow(); + } + if (!data.appModule) { try { if (!data.render) { diff --git a/src/types/electron.ts b/src/types/electron.ts index 6c13e2c3a..caa68f4e2 100644 --- a/src/types/electron.ts +++ b/src/types/electron.ts @@ -1,9 +1,118 @@ +import type { LoginAppRuntime } from "$apps/core/loginapp/runtime"; +import ElectronTitleBar from "$lib/ElectronTitleBar.svelte"; +import { IsElectron } from "$ts/electron"; +import { Daemon, Kernel, Stack } from "$ts/env"; +import type { ArcTerminal } from "$ts/terminal"; import type { Notification } from "$types/system/notification"; +import { mount } from "svelte"; export interface ElectronIPC { closeWindow(): void; + minimizeWindow(): void; + maximizeWindow(): void; sendNotification(data: Notification): void; updateIconProgress(progress: number, max: number): void; onPowerOff(callback: () => void): void; setCanClose(canClose: boolean): void; + getSystem(): Promise; + handleMaximize(callback: (isMaximized: boolean) => void): void; + handleFullscreen(callback: (isFullscreen: boolean) => void): void; +} + +function disableRoundedWindow(platform: NodeJS.Platform, disableRounding: boolean) { + if (platform === "win32" || platform === "darwin") { + if (disableRounding) { + document.body.classList.remove("rounded"); + } else { + document.body.classList.add("rounded"); + } + } +} + +function hideTitleBar(titlebar: HTMLDivElement, hide: boolean) { + if (hide) { + titlebar.classList.add("hidden"); + } else { + titlebar.classList.remove("hidden"); + } +} + +export async function handleElectronInit() { + if (IsElectron()) { + mount(ElectronTitleBar, { target: document.body }); + + const titleBar = document.getElementById("electron-titlebar")! as HTMLDivElement; + + document.body.classList.add("electron-app"); + const platform = await electron!.getSystem(); + disableRoundedWindow(platform, false); + + electron!.handleMaximize(async (isMaximized) => { + disableRoundedWindow(platform, isMaximized); + }); + + electron!.handleFullscreen((isFullscreen) => { + disableRoundedWindow(platform, isFullscreen); + hideTitleBar(titleBar, isFullscreen); + }); + + electron!.onPowerOff(async () => { + if (!Kernel || !Kernel.state) { + electron!.setCanClose(true); + electron!.closeWindow(); + return; + } + + switch (Kernel.state?.currentState) { + case "desktop": + await Daemon.power?.shutdown(); + break; + + case "boot": + case "turnedOff": + electron!.setCanClose(true); + electron!.closeWindow(); + break; + + case "arcterm": + function getSubProcesses(parentPid: number) { + const subProcesses = Stack.getSubProcesses(parentPid); + for (const [key, val] of subProcesses) { + if (val.name === "ArcTerminal") { + (val as ArcTerminal).processLine("exit"); + break; + } + + getSubProcesses(key); + } + } + + getSubProcesses(Kernel.initPid); + break; + + case "login": + function getSubProcessess(parentPid: number) { + const subProcesses = Stack.getSubProcesses(parentPid); + for (const [key, val] of subProcesses) { + if (val.name === "loginApp") { + console.log(val); + console.log((val as LoginAppRuntime).loadingStatus()); + if ((val as LoginAppRuntime).loadingStatus().length === 0) { + (val as LoginAppRuntime).shutdown(); + } + break; + } + + getSubProcessess(key); + } + } + + getSubProcessess(Kernel.initPid); + break; + default: + console.log(`Couldn't handle state "${Kernel.state?.currentState}"`); + break; + } + }); + } } From 4b3ca7a62a260453b7d2d03fbd95ccabfcb0f8ed Mon Sep 17 00:00:00 2001 From: Blockyheadman <80011716+Blockyheadman@users.noreply.github.com> Date: Thu, 9 Jul 2026 20:38:28 -0500 Subject: [PATCH 4/6] whoops. it was in types --- src/main.ts | 2 +- src/ts/electron.ts | 104 ++++++++++++++++++++++++++++++++++++++++++ src/types/electron.ts | 104 ------------------------------------------ 3 files changed, 105 insertions(+), 105 deletions(-) diff --git a/src/main.ts b/src/main.ts index 8fe5805d2..35cc910df 100755 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,6 @@ import "dseg/css/dseg.css"; import "./css/main.css"; -import { handleElectronInit } from "$types/electron"; +import { handleElectronInit } from "$ts/electron"; // CODE EXECUTION STARTS HERE async function Main() { diff --git a/src/ts/electron.ts b/src/ts/electron.ts index 4321ce8d3..15acceea6 100644 --- a/src/ts/electron.ts +++ b/src/ts/electron.ts @@ -1 +1,105 @@ +import type { LoginAppRuntime } from "$apps/core/loginapp/runtime"; +import ElectronTitleBar from "$lib/ElectronTitleBar.svelte"; +import { Daemon, Kernel, Stack } from "$ts/env"; +import type { ArcTerminal } from "$ts/terminal"; +import { mount } from "svelte"; + export const IsElectron = () => !!(window as any)["electron"] && navigator.userAgent.includes("Electron"); + +function disableRoundedWindow(platform: NodeJS.Platform, disableRounding: boolean) { + if (platform === "win32" || platform === "darwin") { + if (disableRounding) { + document.body.classList.remove("rounded"); + } else { + document.body.classList.add("rounded"); + } + } +} + +function hideTitleBar(titlebar: HTMLDivElement, hide: boolean) { + if (hide) { + titlebar.classList.add("hidden"); + } else { + titlebar.classList.remove("hidden"); + } +} + +export async function handleElectronInit() { + if (IsElectron()) { + mount(ElectronTitleBar, { target: document.body }); + + const titleBar = document.getElementById("electron-titlebar")! as HTMLDivElement; + + document.body.classList.add("electron-app"); + const platform = await electron!.getSystem(); + disableRoundedWindow(platform, false); + + electron!.handleMaximize(async (isMaximized) => { + disableRoundedWindow(platform, isMaximized); + }); + + electron!.handleFullscreen((isFullscreen) => { + disableRoundedWindow(platform, isFullscreen); + hideTitleBar(titleBar, isFullscreen); + }); + + electron!.onPowerOff(async () => { + if (!Kernel || !Kernel.state) { + electron!.setCanClose(true); + electron!.closeWindow(); + return; + } + + switch (Kernel.state?.currentState) { + case "desktop": + await Daemon.power?.shutdown(); + break; + + case "boot": + case "turnedOff": + electron!.setCanClose(true); + electron!.closeWindow(); + break; + + case "arcterm": + function getSubProcesses(parentPid: number) { + const subProcesses = Stack.getSubProcesses(parentPid); + for (const [key, val] of subProcesses) { + if (val.name === "ArcTerminal") { + (val as ArcTerminal).processLine("exit"); + break; + } + + getSubProcesses(key); + } + } + + getSubProcesses(Kernel.initPid); + break; + + case "login": + function getSubProcessess(parentPid: number) { + const subProcesses = Stack.getSubProcesses(parentPid); + for (const [key, val] of subProcesses) { + if (val.name === "loginApp") { + console.log(val); + console.log((val as LoginAppRuntime).loadingStatus()); + if ((val as LoginAppRuntime).loadingStatus().length === 0) { + (val as LoginAppRuntime).shutdown(); + } + break; + } + + getSubProcessess(key); + } + } + + getSubProcessess(Kernel.initPid); + break; + default: + console.log(`Couldn't handle state "${Kernel.state?.currentState}"`); + break; + } + }); + } +} diff --git a/src/types/electron.ts b/src/types/electron.ts index caa68f4e2..46e7a11e6 100644 --- a/src/types/electron.ts +++ b/src/types/electron.ts @@ -1,10 +1,4 @@ -import type { LoginAppRuntime } from "$apps/core/loginapp/runtime"; -import ElectronTitleBar from "$lib/ElectronTitleBar.svelte"; -import { IsElectron } from "$ts/electron"; -import { Daemon, Kernel, Stack } from "$ts/env"; -import type { ArcTerminal } from "$ts/terminal"; import type { Notification } from "$types/system/notification"; -import { mount } from "svelte"; export interface ElectronIPC { closeWindow(): void; @@ -18,101 +12,3 @@ export interface ElectronIPC { handleMaximize(callback: (isMaximized: boolean) => void): void; handleFullscreen(callback: (isFullscreen: boolean) => void): void; } - -function disableRoundedWindow(platform: NodeJS.Platform, disableRounding: boolean) { - if (platform === "win32" || platform === "darwin") { - if (disableRounding) { - document.body.classList.remove("rounded"); - } else { - document.body.classList.add("rounded"); - } - } -} - -function hideTitleBar(titlebar: HTMLDivElement, hide: boolean) { - if (hide) { - titlebar.classList.add("hidden"); - } else { - titlebar.classList.remove("hidden"); - } -} - -export async function handleElectronInit() { - if (IsElectron()) { - mount(ElectronTitleBar, { target: document.body }); - - const titleBar = document.getElementById("electron-titlebar")! as HTMLDivElement; - - document.body.classList.add("electron-app"); - const platform = await electron!.getSystem(); - disableRoundedWindow(platform, false); - - electron!.handleMaximize(async (isMaximized) => { - disableRoundedWindow(platform, isMaximized); - }); - - electron!.handleFullscreen((isFullscreen) => { - disableRoundedWindow(platform, isFullscreen); - hideTitleBar(titleBar, isFullscreen); - }); - - electron!.onPowerOff(async () => { - if (!Kernel || !Kernel.state) { - electron!.setCanClose(true); - electron!.closeWindow(); - return; - } - - switch (Kernel.state?.currentState) { - case "desktop": - await Daemon.power?.shutdown(); - break; - - case "boot": - case "turnedOff": - electron!.setCanClose(true); - electron!.closeWindow(); - break; - - case "arcterm": - function getSubProcesses(parentPid: number) { - const subProcesses = Stack.getSubProcesses(parentPid); - for (const [key, val] of subProcesses) { - if (val.name === "ArcTerminal") { - (val as ArcTerminal).processLine("exit"); - break; - } - - getSubProcesses(key); - } - } - - getSubProcesses(Kernel.initPid); - break; - - case "login": - function getSubProcessess(parentPid: number) { - const subProcesses = Stack.getSubProcesses(parentPid); - for (const [key, val] of subProcesses) { - if (val.name === "loginApp") { - console.log(val); - console.log((val as LoginAppRuntime).loadingStatus()); - if ((val as LoginAppRuntime).loadingStatus().length === 0) { - (val as LoginAppRuntime).shutdown(); - } - break; - } - - getSubProcessess(key); - } - } - - getSubProcessess(Kernel.initPid); - break; - default: - console.log(`Couldn't handle state "${Kernel.state?.currentState}"`); - break; - } - }); - } -} From 6734c364a3730e834f281c49a2189c6b180a0c5c Mon Sep 17 00:00:00 2001 From: Blockyheadman <80011716+Blockyheadman@users.noreply.github.com> Date: Sat, 11 Jul 2026 10:57:54 -0500 Subject: [PATCH 5/6] added checks for electron integration --- src/css/electron.css | 7 +++++-- src/css/main.css | 2 +- src/ts/electron.ts | 21 +++++++++++++++++---- src/types/electron.ts | 2 ++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/css/electron.css b/src/css/electron.css index 659e02a86..c1b7ff287 100644 --- a/src/css/electron.css +++ b/src/css/electron.css @@ -7,11 +7,10 @@ } :root { - --electron-titlebar-height: 2.5rem; + --electron-titlebar-height: 40px; } body.electron-app { - background-color: transparent; overflow: hidden; transform: translateZ(0); border-radius: 0px; @@ -20,6 +19,10 @@ body.electron-app { transition: border-radius .2s; } + &.transparent { + background-color: transparent; + } + } /* Please, PLEASE don't judge me for this. it works. diff --git a/src/css/main.css b/src/css/main.css index 6905040ba..c4ad78aef 100755 --- a/src/css/main.css +++ b/src/css/main.css @@ -29,7 +29,7 @@ html, body { width: 100%; height: 100%; - background-color: transparent; + background-color: #000; color: var(--fg, #fff); margin: 0; overflow: hidden; diff --git a/src/ts/electron.ts b/src/ts/electron.ts index 15acceea6..3a051da6d 100644 --- a/src/ts/electron.ts +++ b/src/ts/electron.ts @@ -7,7 +7,7 @@ import { mount } from "svelte"; export const IsElectron = () => !!(window as any)["electron"] && navigator.userAgent.includes("Electron"); function disableRoundedWindow(platform: NodeJS.Platform, disableRounding: boolean) { - if (platform === "win32" || platform === "darwin") { + if (platform === "win32") { if (disableRounding) { document.body.classList.remove("rounded"); } else { @@ -26,12 +26,23 @@ function hideTitleBar(titlebar: HTMLDivElement, hide: boolean) { export async function handleElectronInit() { if (IsElectron()) { - mount(ElectronTitleBar, { target: document.body }); + // inform the electron process that native functions are available. + electron!.supportIntegration(); + + const platform = await electron!.getSystem(); + const isNative = await electron!.isNative(); + mount(ElectronTitleBar, { target: document.body }); const titleBar = document.getElementById("electron-titlebar")! as HTMLDivElement; + if (isNative) { + hideTitleBar(titleBar, true); + } document.body.classList.add("electron-app"); - const platform = await electron!.getSystem(); + if (platform === "win32") { + document.body.classList.add("transparent"); + } + disableRoundedWindow(platform, false); electron!.handleMaximize(async (isMaximized) => { @@ -40,7 +51,9 @@ export async function handleElectronInit() { electron!.handleFullscreen((isFullscreen) => { disableRoundedWindow(platform, isFullscreen); - hideTitleBar(titleBar, isFullscreen); + if (!isNative) { + hideTitleBar(titleBar, isFullscreen); + } }); electron!.onPowerOff(async () => { diff --git a/src/types/electron.ts b/src/types/electron.ts index 46e7a11e6..3696b0dee 100644 --- a/src/types/electron.ts +++ b/src/types/electron.ts @@ -11,4 +11,6 @@ export interface ElectronIPC { getSystem(): Promise; handleMaximize(callback: (isMaximized: boolean) => void): void; handleFullscreen(callback: (isFullscreen: boolean) => void): void; + supportIntegration(): void; + isNative(): Promise; } From d74401c92578fd4efa7b68559a774e22ec89eccc Mon Sep 17 00:00:00 2001 From: Blockyheadman <80011716+Blockyheadman@users.noreply.github.com> Date: Sat, 11 Jul 2026 11:07:13 -0500 Subject: [PATCH 6/6] console log :( --- src/ts/electron.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ts/electron.ts b/src/ts/electron.ts index 3a051da6d..40c9acff2 100644 --- a/src/ts/electron.ts +++ b/src/ts/electron.ts @@ -95,8 +95,6 @@ export async function handleElectronInit() { const subProcesses = Stack.getSubProcesses(parentPid); for (const [key, val] of subProcesses) { if (val.name === "loginApp") { - console.log(val); - console.log((val as LoginAppRuntime).loadingStatus()); if ((val as LoginAppRuntime).loadingStatus().length === 0) { (val as LoginAppRuntime).shutdown(); } @@ -110,7 +108,7 @@ export async function handleElectronInit() { getSubProcessess(Kernel.initPid); break; default: - console.log(`Couldn't handle state "${Kernel.state?.currentState}"`); + Kernel.Log("v7-electron", `Couldn't handle state "${Kernel.state?.currentState}"`); break; } });