{
document.documentElement.dataset.fullscreen = String(fullscreen);
};
+ const handleActive = (active: boolean | undefined) => {
+ document.documentElement.dataset.active = String(active);
+ };
apis?.ui.isMaximized().then(handleMaximized).catch(console.error);
apis?.ui.isFullScreen().then(handleFullscreen).catch(console.error);
events?.ui.onMaximized(handleMaximized);
events?.ui.onFullScreen(handleFullscreen);
+ events?.ui.onTabShellViewActiveChange(handleActive);
await loadLanguage();
mountApp();
diff --git a/packages/frontend/electron/renderer/shell/shell.css.ts b/packages/frontend/electron/renderer/shell/shell.css.ts
index 6e1286f565..44a8872602 100644
--- a/packages/frontend/electron/renderer/shell/shell.css.ts
+++ b/packages/frontend/electron/renderer/shell/shell.css.ts
@@ -1,25 +1,16 @@
import { cssVar } from '@toeverything/theme';
-import { createVar, globalStyle, style } from '@vanilla-extract/css';
+import { createVar, style } from '@vanilla-extract/css';
export const sidebarOffsetVar = createVar();
export const root = style({
width: '100vw',
height: '100vh',
- opacity: 1,
display: 'flex',
- transition: 'opacity 0.1s',
background: cssVar('backgroundPrimaryColor'),
selectors: {
- '&[data-active="false"]': {
- opacity: 0,
- },
'&[data-translucent="true"]': {
background: 'transparent',
},
},
});
-
-globalStyle(`${root}[data-active="false"] *`, {
- ['WebkitAppRegion' as string]: 'no-drag !important',
-});
diff --git a/packages/frontend/electron/renderer/shell/shell.tsx b/packages/frontend/electron/renderer/shell/shell.tsx
index 324294d949..04dced50aa 100644
--- a/packages/frontend/electron/renderer/shell/shell.tsx
+++ b/packages/frontend/electron/renderer/shell/shell.tsx
@@ -1,38 +1,16 @@
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import { AppTabsHeader } from '@affine/core/modules/app-tabs-header';
-import { events } from '@affine/electron-api';
-import { useEffect, useState } from 'react';
import * as styles from './shell.css';
-const useIsShellActive = () => {
- const [active, setActive] = useState(false);
-
- useEffect(() => {
- const unsub = events?.ui.onTabShellViewActiveChange(active => {
- setActive(active);
- });
- return () => {
- unsub?.();
- };
- });
-
- return active;
-};
-
export function ShellRoot() {
- const active = useIsShellActive();
const { appSettings } = useAppSettingHelper();
const translucent =
environment.isDesktop &&
environment.isMacOs &&
appSettings.enableBlurBackground;
return (
-
+
);
diff --git a/packages/frontend/electron/src/main/events.ts b/packages/frontend/electron/src/main/events.ts
index a2bea51af3..07064aa4d9 100644
--- a/packages/frontend/electron/src/main/events.ts
+++ b/packages/frontend/electron/src/main/events.ts
@@ -19,6 +19,7 @@ function getActiveWindows() {
}
export function registerEvents() {
+ const unsubs: (() => void)[] = [];
// register events
for (const [namespace, namespaceEvents] of Object.entries(allEvents)) {
for (const [key, eventRegister] of Object.entries(namespaceEvents)) {
@@ -52,14 +53,15 @@ export function registerEvents() {
});
});
});
- app.on('before-quit', () => {
- // subscription on quit sometimes crashes the app
- try {
- unsubscribe();
- } catch (err) {
- logger.error('unsubscribe error', err);
- }
- });
+ unsubs.push(unsubscribe);
}
}
+ app.on('before-quit', () => {
+ // subscription on quit sometimes crashes the app
+ try {
+ unsubs.forEach(unsub => unsub());
+ } catch (err) {
+ logger.error('unsubscribe error', err);
+ }
+ });
}
diff --git a/packages/frontend/electron/src/main/windows-manager/context-menu.ts b/packages/frontend/electron/src/main/windows-manager/context-menu.ts
index 5cb88d4e92..94f1c9612f 100644
--- a/packages/frontend/electron/src/main/windows-manager/context-menu.ts
+++ b/packages/frontend/electron/src/main/windows-manager/context-menu.ts
@@ -62,7 +62,7 @@ export const showTabContextMenu = async (tabId: string, viewIndex: number) => {
},
},
- ...(workbenches.length > 0
+ ...(workbenches.length > 1
? ([
{ type: 'separator' },
{
diff --git a/packages/frontend/electron/src/main/windows-manager/tab-views.ts b/packages/frontend/electron/src/main/windows-manager/tab-views.ts
index 9e92770ef4..6a1365e913 100644
--- a/packages/frontend/electron/src/main/windows-manager/tab-views.ts
+++ b/packages/frontend/electron/src/main/windows-manager/tab-views.ts
@@ -140,6 +140,10 @@ export class WebContentViewsManager {
readonly tabViewsMeta$ = TabViewsMetaState.$;
readonly appTabsUIReady$ = new BehaviorSubject(new Set
());
+ get appTabsUIReady() {
+ return this.appTabsUIReady$.value;
+ }
+
// all web views
readonly webViewsMap$ = new BehaviorSubject(
new Map()
@@ -251,8 +255,12 @@ export class WebContentViewsManager {
}
setTabUIReady = (tabId: string) => {
- this.appTabsUIReady$.next(new Set([...this.appTabsUIReady$.value, tabId]));
+ this.appTabsUIReady$.next(new Set([...this.appTabsUIReady, tabId]));
this.reorderViews();
+ const view = this.tabViewsMap.get(tabId);
+ if (view) {
+ this.resizeView(view);
+ }
};
getViewIdFromWebContentsId = (id: number) => {
@@ -460,10 +468,6 @@ export class WebContentViewsManager {
showTab = async (id: string): Promise => {
if (this.activeWorkbenchId !== id) {
- // todo: this will cause the shell view to be on top and flickers the screen
- // this.appTabsUIReady$.next(
- // new Set([...this.appTabsUIReady$.value].filter(key => key !== id))
- // );
this.patchTabViewsMeta({
activeWorkbenchId: id,
});
@@ -601,26 +605,59 @@ export class WebContentViewsManager {
// if tab ui of the current active view is not ready,
// make sure shell view is on top
const activeView = this.activeWorkbenchView;
- const ready = this.activeWorkbenchId
- ? this.appTabsUIReady$.value.has(this.activeWorkbenchId)
- : false;
- // inactive < active view (not ready) < shell < active view (ready)
- const getScore = (view: View) => {
- if (view === this.shellView) {
- return 2;
- }
- if (view === activeView) {
- return ready ? 3 : 1;
- }
- return 0;
+ const getViewId = (view: View) => {
+ return [...this.tabViewsMap.entries()].find(
+ ([_, v]) => v === view
+ )?.[0];
};
- [...this.tabViewsMap.values()]
- .toSorted((a, b) => getScore(a) - getScore(b))
- .forEach((view, index) => {
- this.mainWindow?.contentView.addChildView(view, index);
- });
+ const isViewReady = (view: View) => {
+ if (view === this.shellView) {
+ return true;
+ }
+ const id = getViewId(view);
+ return id ? this.appTabsUIReady.has(id) : false;
+ };
+
+ // 2: active view (ready)
+ // 1: shell
+ // 0: inactive view (ready)
+ // -1 inactive view (not ready)
+ // -1 active view (not ready)
+ const getScore = (view: View) => {
+ if (view === this.shellView) {
+ return 1;
+ }
+ const viewReady = isViewReady(view);
+ if (view === activeView) {
+ return viewReady ? 2 : -1;
+ } else {
+ return viewReady ? 0 : -1;
+ }
+ };
+
+ const sorted = [...this.tabViewsMap.entries()]
+ .map(([id, view]) => {
+ return {
+ id,
+ view,
+ score: getScore(view),
+ };
+ })
+ .filter(({ score }) => score >= 0)
+ .toSorted((a, b) => a.score - b.score);
+
+ // remove inactive views
+ this.mainWindow?.contentView.children.forEach(view => {
+ if (!isViewReady(view)) {
+ this.mainWindow?.contentView.removeChildView(view);
+ }
+ });
+
+ sorted.forEach(({ view }, idx) => {
+ this.mainWindow?.contentView.addChildView(view, idx);
+ });
}
};
diff --git a/tests/kit/electron.ts b/tests/kit/electron.ts
index 1707cfee78..2e07733ab9 100644
--- a/tests/kit/electron.ts
+++ b/tests/kit/electron.ts
@@ -140,10 +140,8 @@ export const test = base.extend<{
await use(electronApp);
console.log('Cleaning up...');
const pages = electronApp.windows();
- for (let i = 0; i < pages.length; i++) {
- const page = pages[i];
+ for (const page of pages) {
await page.close();
- console.log(`Closed page ${i + 1}/${pages.length}`);
}
await electronApp.close();
await removeWithRetry(clonedDist);