mirror of
https://github.com/wavetermdev/waveterm
synced 2026-05-24 09:18:27 +00:00
This PR removes the legacy notification pipeline from the frontend
(`pushNotification`, removal helpers, notification atoms/types, and
notification UI components). The updater status notification path was
only wired through this system and dev-only rendering in `App`, so the
entire path is deleted.
- **Store/API cleanup**
- Removed notification functions from `frontend/app/store/global.ts`:
- `pushNotification`
- `removeNotificationById`
- `removeNotification`
- internal add/update helper
- Removed notification API exposure from `frontend/wave.ts`
(`window.pushNotification`, `window.removeNotificationById`).
- **Global atom/type cleanup**
- Removed notification atoms from `frontend/app/store/global-atoms.ts`:
- `notifications`
- `notificationPopoverMode`
- Removed notification-related fields/types from
`frontend/types/custom.d.ts`:
- `GlobalAtomsType.notifications`
- `GlobalAtomsType.notificationPopoverMode`
- `NotificationType`
- `NotificationActionType`
- **UI removal**
- Removed notification rendering from `frontend/app/app.tsx` by deleting
the `NotificationBubbles` import and dev-guarded render branch.
- Deleted the entire notification UI module under
`frontend/app/notification/`:
- `notificationbubbles.tsx/.scss`
- `notificationitem.tsx/.scss`
- `notificationpopover.tsx`
- `updatenotifier.tsx`
- `usenotification.tsx`
- **Representative diff**
```tsx
// before (app.tsx)
import { NotificationBubbles } from
"./notification/notificationbubbles";
...
{isDev() ? <NotificationBubbles></NotificationBubbles> : null}
// after
// notification import and render removed
```
<screenshot>
UI reference provided for this change set:
https://github.com/user-attachments/assets/6f56b16f-95c9-4a5e-ada8-18ecdc9a1ff8
</screenshot>
<!-- START COPILOT CODING AGENT TIPS -->
---
🔒 GitHub Advanced Security automatically protects Copilot coding agent
pull requests. You can protect all pull requests by enabling Advanced
Security for your repositories. [Learn more about Advanced
Security.](https://gh.io/cca-advanced-security)
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
277 lines
11 KiB
TypeScript
277 lines
11 KiB
TypeScript
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import { App } from "@/app/app";
|
|
import { loadMonaco } from "@/app/monaco/monaco-env";
|
|
import { GlobalModel } from "@/app/store/global-model";
|
|
import {
|
|
globalRefocus,
|
|
registerBuilderGlobalKeys,
|
|
registerControlShiftStateUpdateHandler,
|
|
registerElectronReinjectKeyHandler,
|
|
registerGlobalKeys,
|
|
} from "@/app/store/keymodel";
|
|
import { modalsModel } from "@/app/store/modalmodel";
|
|
import { RpcApi } from "@/app/store/wshclientapi";
|
|
import { makeBuilderRouteId, makeTabRouteId } from "@/app/store/wshrouter";
|
|
import { initWshrpc, TabRpcClient } from "@/app/store/wshrpcutil";
|
|
import { BuilderApp } from "@/builder/builder-app";
|
|
import { getLayoutModelForStaticTab } from "@/layout/index";
|
|
import { countersClear, countersPrint } from "@/store/counters";
|
|
import {
|
|
atoms,
|
|
getApi,
|
|
globalStore,
|
|
initGlobal,
|
|
initGlobalWaveEventSubs,
|
|
loadConnStatus,
|
|
loadTabIndicators,
|
|
subscribeToConnEvents,
|
|
} from "@/store/global";
|
|
import { activeTabIdAtom } from "@/store/tab-model";
|
|
import * as WOS from "@/store/wos";
|
|
import { loadFonts } from "@/util/fontutil";
|
|
import { setKeyUtilPlatform } from "@/util/keyutil";
|
|
import { createElement } from "react";
|
|
import { createRoot } from "react-dom/client";
|
|
|
|
const platform = getApi().getPlatform();
|
|
document.title = `Wave Terminal`;
|
|
let savedInitOpts: WaveInitOpts = null;
|
|
|
|
(window as any).WOS = WOS;
|
|
(window as any).globalStore = globalStore;
|
|
(window as any).globalAtoms = atoms;
|
|
(window as any).RpcApi = RpcApi;
|
|
(window as any).isFullScreen = false;
|
|
(window as any).countersPrint = countersPrint;
|
|
(window as any).countersClear = countersClear;
|
|
(window as any).getLayoutModelForStaticTab = getLayoutModelForStaticTab;
|
|
(window as any).modalsModel = modalsModel;
|
|
|
|
function updateZoomFactor(zoomFactor: number) {
|
|
console.log("update zoomfactor", zoomFactor);
|
|
document.documentElement.style.setProperty("--zoomfactor", String(zoomFactor));
|
|
document.documentElement.style.setProperty("--zoomfactor-inv", String(1 / zoomFactor));
|
|
}
|
|
|
|
async function initBare() {
|
|
getApi().sendLog("Init Bare");
|
|
document.body.style.visibility = "hidden";
|
|
document.body.style.opacity = "0";
|
|
document.body.classList.add("is-transparent");
|
|
getApi().onWaveInit(initWaveWrap);
|
|
getApi().onBuilderInit(initBuilderWrap);
|
|
setKeyUtilPlatform(platform);
|
|
loadFonts();
|
|
updateZoomFactor(getApi().getZoomFactor());
|
|
getApi().onZoomFactorChange((zoomFactor) => {
|
|
updateZoomFactor(zoomFactor);
|
|
});
|
|
document.fonts.ready.then(() => {
|
|
console.log("Init Bare Done");
|
|
getApi().setWindowInitStatus("ready");
|
|
});
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", initBare);
|
|
|
|
async function initWaveWrap(initOpts: WaveInitOpts) {
|
|
try {
|
|
if (savedInitOpts) {
|
|
await reinitWave();
|
|
return;
|
|
}
|
|
savedInitOpts = initOpts;
|
|
await initWave(initOpts);
|
|
} catch (e) {
|
|
getApi().sendLog("Error in initWave " + e.message + "\n" + e.stack);
|
|
console.error("Error in initWave", e);
|
|
} finally {
|
|
document.body.style.visibility = null;
|
|
document.body.style.opacity = null;
|
|
document.body.classList.remove("is-transparent");
|
|
}
|
|
}
|
|
|
|
async function reinitWave() {
|
|
console.log("Reinit Wave");
|
|
getApi().sendLog("Reinit Wave");
|
|
|
|
// We use this hack to prevent a flicker of the previously-hovered tab when this view was last active.
|
|
document.body.classList.add("nohover");
|
|
requestAnimationFrame(() =>
|
|
setTimeout(() => {
|
|
document.body.classList.remove("nohover");
|
|
}, 100)
|
|
);
|
|
|
|
await WOS.reloadWaveObject<Client>(WOS.makeORef("client", savedInitOpts.clientId));
|
|
const waveWindow = await WOS.reloadWaveObject<WaveWindow>(WOS.makeORef("window", savedInitOpts.windowId));
|
|
const ws = await WOS.reloadWaveObject<Workspace>(WOS.makeORef("workspace", waveWindow.workspaceid));
|
|
const initialTab = await WOS.reloadWaveObject<Tab>(WOS.makeORef("tab", savedInitOpts.tabId));
|
|
await WOS.reloadWaveObject<LayoutState>(WOS.makeORef("layout", initialTab.layoutstate));
|
|
reloadAllWorkspaceTabs(ws);
|
|
document.title = `Wave Terminal - ${initialTab.name}`; // TODO update with tab name change
|
|
getApi().setWindowInitStatus("wave-ready");
|
|
globalStore.set(atoms.reinitVersion, globalStore.get(atoms.reinitVersion) + 1);
|
|
globalStore.set(atoms.updaterStatusAtom, getApi().getUpdaterStatus());
|
|
setTimeout(() => {
|
|
globalRefocus();
|
|
}, 50);
|
|
}
|
|
|
|
function reloadAllWorkspaceTabs(ws: Workspace) {
|
|
if (ws == null || !ws.tabids?.length) {
|
|
return;
|
|
}
|
|
ws.tabids?.forEach((tabid) => {
|
|
WOS.reloadWaveObject<Tab>(WOS.makeORef("tab", tabid));
|
|
});
|
|
}
|
|
|
|
function loadAllWorkspaceTabs(ws: Workspace) {
|
|
if (ws == null || !ws.tabids?.length) {
|
|
return;
|
|
}
|
|
ws.tabids?.forEach((tabid) => {
|
|
WOS.getObjectValue<Tab>(WOS.makeORef("tab", tabid));
|
|
});
|
|
}
|
|
|
|
async function initWave(initOpts: WaveInitOpts) {
|
|
getApi().sendLog("Init Wave " + JSON.stringify(initOpts));
|
|
const globalInitOpts: GlobalInitOptions = {
|
|
tabId: initOpts.tabId,
|
|
clientId: initOpts.clientId,
|
|
windowId: initOpts.windowId,
|
|
platform,
|
|
environment: "renderer",
|
|
primaryTabStartup: initOpts.primaryTabStartup,
|
|
};
|
|
console.log("Wave Init", globalInitOpts);
|
|
globalStore.set(activeTabIdAtom, initOpts.tabId);
|
|
await GlobalModel.getInstance().initialize(globalInitOpts);
|
|
initGlobal(globalInitOpts);
|
|
(window as any).globalAtoms = atoms;
|
|
|
|
// Init WPS event handlers
|
|
const globalWS = initWshrpc(makeTabRouteId(initOpts.tabId));
|
|
(window as any).globalWS = globalWS;
|
|
(window as any).TabRpcClient = TabRpcClient;
|
|
await loadConnStatus();
|
|
await loadTabIndicators();
|
|
initGlobalWaveEventSubs(initOpts);
|
|
subscribeToConnEvents();
|
|
|
|
// ensures client/window/workspace are loaded into the cache before rendering
|
|
try {
|
|
const [client, waveWindow, initialTab] = await Promise.all([
|
|
WOS.loadAndPinWaveObject<Client>(WOS.makeORef("client", initOpts.clientId)),
|
|
WOS.loadAndPinWaveObject<WaveWindow>(WOS.makeORef("window", initOpts.windowId)),
|
|
WOS.loadAndPinWaveObject<Tab>(WOS.makeORef("tab", initOpts.tabId)),
|
|
]);
|
|
const [ws, layoutState] = await Promise.all([
|
|
WOS.loadAndPinWaveObject<Workspace>(WOS.makeORef("workspace", waveWindow.workspaceid)),
|
|
WOS.reloadWaveObject<LayoutState>(WOS.makeORef("layout", initialTab.layoutstate)),
|
|
]);
|
|
loadAllWorkspaceTabs(ws);
|
|
WOS.wpsSubscribeToObject(WOS.makeORef("workspace", waveWindow.workspaceid));
|
|
document.title = `Wave Terminal - ${initialTab.name}`; // TODO update with tab name change
|
|
} catch (e) {
|
|
console.error("Failed initialization error", e);
|
|
getApi().sendLog("Error in initialization (wave.ts, loading required objects) " + e.message + "\n" + e.stack);
|
|
}
|
|
registerGlobalKeys();
|
|
registerElectronReinjectKeyHandler();
|
|
registerControlShiftStateUpdateHandler();
|
|
await loadMonaco();
|
|
const fullConfig = await RpcApi.GetFullConfigCommand(TabRpcClient);
|
|
console.log("fullconfig", fullConfig);
|
|
globalStore.set(atoms.fullConfigAtom, fullConfig);
|
|
const waveaiModeConfig = await RpcApi.GetWaveAIModeConfigCommand(TabRpcClient);
|
|
globalStore.set(atoms.waveaiModeConfigAtom, waveaiModeConfig.configs);
|
|
console.log("Wave First Render");
|
|
let firstRenderResolveFn: () => void = null;
|
|
let firstRenderPromise = new Promise<void>((resolve) => {
|
|
firstRenderResolveFn = resolve;
|
|
});
|
|
const reactElem = createElement(App, { onFirstRender: firstRenderResolveFn }, null);
|
|
const elem = document.getElementById("main");
|
|
const root = createRoot(elem);
|
|
root.render(reactElem);
|
|
await firstRenderPromise;
|
|
console.log("Wave First Render Done");
|
|
getApi().setWindowInitStatus("wave-ready");
|
|
}
|
|
|
|
async function initBuilderWrap(initOpts: BuilderInitOpts) {
|
|
try {
|
|
await initBuilder(initOpts);
|
|
} catch (e) {
|
|
getApi().sendLog("Error in initBuilder " + e.message + "\n" + e.stack);
|
|
console.error("Error in initBuilder", e);
|
|
} finally {
|
|
document.body.style.visibility = null;
|
|
document.body.style.opacity = null;
|
|
document.body.classList.remove("is-transparent");
|
|
}
|
|
}
|
|
|
|
async function initBuilder(initOpts: BuilderInitOpts) {
|
|
getApi().sendLog("Init Builder " + JSON.stringify(initOpts));
|
|
const globalInitOpts: GlobalInitOptions = {
|
|
clientId: initOpts.clientId,
|
|
windowId: initOpts.windowId,
|
|
platform,
|
|
environment: "renderer",
|
|
builderId: initOpts.builderId,
|
|
};
|
|
console.log("Tsunami Builder Init", globalInitOpts);
|
|
await GlobalModel.getInstance().initialize(globalInitOpts);
|
|
initGlobal(globalInitOpts);
|
|
(window as any).globalAtoms = atoms;
|
|
|
|
const globalWS = initWshrpc(makeBuilderRouteId(initOpts.builderId));
|
|
(window as any).globalWS = globalWS;
|
|
(window as any).TabRpcClient = TabRpcClient;
|
|
await loadConnStatus();
|
|
|
|
let appIdToUse: string = null;
|
|
try {
|
|
const oref = WOS.makeORef("builder", initOpts.builderId);
|
|
const rtInfo = await RpcApi.GetRTInfoCommand(TabRpcClient, { oref });
|
|
if (rtInfo && rtInfo["builder:appid"]) {
|
|
appIdToUse = rtInfo["builder:appid"];
|
|
}
|
|
} catch (e) {
|
|
console.log("Could not load saved builder appId from rtinfo:", e);
|
|
}
|
|
|
|
document.title = appIdToUse ? `WaveApp Builder (${appIdToUse})` : "WaveApp Builder";
|
|
|
|
globalStore.set(atoms.builderAppId, appIdToUse);
|
|
|
|
const client = await WOS.loadAndPinWaveObject<Client>(WOS.makeORef("client", initOpts.clientId));
|
|
|
|
registerBuilderGlobalKeys();
|
|
registerElectronReinjectKeyHandler();
|
|
await loadMonaco();
|
|
const fullConfig = await RpcApi.GetFullConfigCommand(TabRpcClient);
|
|
console.log("fullconfig", fullConfig);
|
|
globalStore.set(atoms.fullConfigAtom, fullConfig);
|
|
const waveaiModeConfig = await RpcApi.GetWaveAIModeConfigCommand(TabRpcClient);
|
|
globalStore.set(atoms.waveaiModeConfigAtom, waveaiModeConfig.configs);
|
|
|
|
console.log("Tsunami Builder First Render");
|
|
let firstRenderResolveFn: () => void = null;
|
|
let firstRenderPromise = new Promise<void>((resolve) => {
|
|
firstRenderResolveFn = resolve;
|
|
});
|
|
const reactElem = createElement(BuilderApp, { initOpts, onFirstRender: firstRenderResolveFn }, null);
|
|
const elem = document.getElementById("main");
|
|
const root = createRoot(elem);
|
|
root.render(reactElem);
|
|
await firstRenderPromise;
|
|
console.log("Tsunami Builder First Render Done");
|
|
}
|