feat(app): Show a themed splash until the launch project opens

Project windows are created in setup with the saved theme's frame colour, and the splash covers the first render while the launch project opens, so a window no longer flashes white or the Welcome screen. Single-instance is now release-only so a dev build can run beside an installed release.
Цей коміт міститься в:
l0ng-ai committed 2026-10-23 15:40:41 +08:00
1 джерело 63a03d1c08
коміт bf06ca7832
8 files changed
+166 -23

No files matched your search

+4 -1
Переглянути файл
@@ -7,6 +7,7 @@ import type { Action } from "@/platform/actions";
import { t } from "@/platform/i18n";
import { call, commands, isTauri } from "@/platform/ipc";
import { basename, confirmLeaveProject, openProject, provideStartupLaunch, useProject } from "@/platform/project";
import { holdSplash } from "@/platform/splash";
import { notifications } from "@/workbench/contributions";
import { ask } from "./prompt";
@@ -195,7 +196,9 @@ export function installLaunchHandling(): () => void {
if (!isTauri()) return () => {};
let disposed = false;
let unlisten: (() => void) | undefined;
provideStartupLaunch(consumeLaunch());
const launch = consumeLaunch();
provideStartupLaunch(launch);
holdSplash(launch);
void import("@tauri-apps/api/webviewWindow").then(async ({ getCurrentWebviewWindow }) => {
const off = await getCurrentWebviewWindow().listen(LAUNCH_EVENT, () => void consumeLaunch());
if (disposed) off();
+2
Переглянути файл
@@ -6,6 +6,7 @@ import { activateSecondaryWindow, activateWorkbench } from "./workbench/activate
import { activateFeatures, activateSettingsWindowFeatures, SettingsWindow, settingsWindowParent } from "./features";
import { installKeyboardDispatcher } from "./platform/keymap";
import { isTauri } from "./platform/ipc";
import { hideSplash } from "./platform/splash";
import { prefetchLazyComponents } from "./ui";
if (import.meta.env.DEV && isTauri()) {
@@ -42,5 +43,6 @@ if (settingsParent !== null) {
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>{settingsParent !== null ? <SettingsWindow /> : <Workbench />}</React.StrictMode>,
);
hideSplash();
// Views split out of the start-up bundle load in the background once the UI is up.
if (settingsParent === null) prefetchLazyComponents();
+29
Переглянути файл
@@ -0,0 +1,29 @@
/**
* The start-up splash (`#splash` in index.html). It covers the first render and
* stays up while start-up work holds it (opening the window's launch project),
* so a project window never flashes the Welcome screen first.
*/
const holds: Promise<unknown>[] = [];
/** Never keep the splash longer than this; a slow open shows its own progress. */
const MAX_WAIT_MS = 4000;
/** Keeps the splash up until `work` settles. Call before `hideSplash()`. */
export function holdSplash(work: Promise<unknown>): void {
holds.push(work.catch(() => {}));
}
/** Fades the splash out once every hold has settled and the page has painted. */
export function hideSplash(): void {
const el = document.getElementById("splash");
if (!el) return;
const timeout = new Promise((resolve) => setTimeout(resolve, MAX_WAIT_MS));
void Promise.race([Promise.all(holds), timeout]).then(() =>
// Two frames: let React commit the state the holds produced before uncovering it.
requestAnimationFrame(() =>
requestAnimationFrame(() => {
el.classList.add("splash-out");
setTimeout(() => el.remove(), 200);
}),
),
);
}