Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5cd5d6685b | |||
| b7e096ae37 |
@@ -11,7 +11,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "out/main/bootstrap.cjs",
|
"main": "out/main/bootstrap.cjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "unset ELECTRON_RUN_AS_NODE && electron-vite dev --inspect=9229",
|
"dev": "electron-vite dev --inspect=9229",
|
||||||
"build": "electron-vite build",
|
"build": "electron-vite build",
|
||||||
"package:mac": "pnpm run build && electron-builder --mac --arm64,universal",
|
"package:mac": "pnpm run build && electron-builder --mac --arm64,universal",
|
||||||
"package:win": "pnpm run build && electron-builder --win --x64",
|
"package:win": "pnpm run build && electron-builder --win --x64",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
|
|
||||||
import { shell } from "electron";
|
import { shell } from "electron";
|
||||||
import { BrowserWindow, app, ipcMain, nativeTheme } from "electron/main";
|
import { BrowserWindow, Menu, app, ipcMain, nativeTheme } from "electron/main";
|
||||||
import type {
|
import type {
|
||||||
BrowserWindow as ElectronBrowserWindow,
|
BrowserWindow as ElectronBrowserWindow,
|
||||||
IpcMainInvokeEvent,
|
IpcMainInvokeEvent,
|
||||||
@@ -132,6 +132,32 @@ const SETTINGS_WINDOW_SIZE: WindowSizePreset = {
|
|||||||
resizable: true,
|
resizable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TITLE_BAR_OVERLAY_HEIGHT = 24;
|
||||||
|
|
||||||
|
function appTitleBarStyle(): "hidden" | "hiddenInset" {
|
||||||
|
return process.platform === "darwin" ? "hiddenInset" : "hidden";
|
||||||
|
}
|
||||||
|
|
||||||
|
function appTitleBarOverlay(color: string): { color: string; symbolColor: string; height: number } | undefined {
|
||||||
|
if (process.platform === "darwin") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
color,
|
||||||
|
symbolColor: nativeTheme.shouldUseDarkColors ? "#f8fafc" : "#111827",
|
||||||
|
height: TITLE_BAR_OVERLAY_HEIGHT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function suppressNativeWindowMenu(window?: ElectronBrowserWindow): void {
|
||||||
|
if (process.platform === "darwin") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Menu.setApplicationMenu(null);
|
||||||
|
window?.setMenu(null);
|
||||||
|
window?.setMenuBarVisibility(false);
|
||||||
|
}
|
||||||
|
|
||||||
function windowModePath(): string {
|
function windowModePath(): string {
|
||||||
return join(app.getPath("userData"), "window-mode.json");
|
return join(app.getPath("userData"), "window-mode.json");
|
||||||
}
|
}
|
||||||
@@ -450,6 +476,7 @@ async function mountRendererWebContents(window: ElectronBrowserWindow, mode: Ren
|
|||||||
|
|
||||||
async function createAppWindow(mode: WindowMode): Promise<ElectronBrowserWindow> {
|
async function createAppWindow(mode: WindowMode): Promise<ElectronBrowserWindow> {
|
||||||
const initial = WINDOW_SIZES[mode];
|
const initial = WINDOW_SIZES[mode];
|
||||||
|
const backgroundColor = mode === "login" ? "#eaf2ff" : nativeTheme.shouldUseDarkColors ? "#15191a" : "#f0f2f5";
|
||||||
const window = new BrowserWindow({
|
const window = new BrowserWindow({
|
||||||
width: initial.width,
|
width: initial.width,
|
||||||
height: initial.height,
|
height: initial.height,
|
||||||
@@ -458,12 +485,15 @@ async function createAppWindow(mode: WindowMode): Promise<ElectronBrowserWindow>
|
|||||||
show: false,
|
show: false,
|
||||||
title: "省心推",
|
title: "省心推",
|
||||||
icon: createDesktopHealthIcon("normal"),
|
icon: createDesktopHealthIcon("normal"),
|
||||||
titleBarStyle: "hiddenInset",
|
titleBarStyle: appTitleBarStyle(),
|
||||||
|
titleBarOverlay: appTitleBarOverlay(mode === "login" ? "#00000000" : backgroundColor),
|
||||||
trafficLightPosition: { x: 12, y: 14 },
|
trafficLightPosition: { x: 12, y: 14 },
|
||||||
resizable: initial.resizable,
|
resizable: initial.resizable,
|
||||||
|
minimizable: mode !== "login",
|
||||||
maximizable: initial.resizable,
|
maximizable: initial.resizable,
|
||||||
fullscreenable: initial.resizable,
|
fullscreenable: initial.resizable,
|
||||||
backgroundColor: mode === "login" ? "#eaf2ff" : nativeTheme.shouldUseDarkColors ? "#15191a" : "#f0f2f5",
|
autoHideMenuBar: true,
|
||||||
|
backgroundColor,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: preloadPath(),
|
preload: preloadPath(),
|
||||||
sandbox: true,
|
sandbox: true,
|
||||||
@@ -472,6 +502,7 @@ async function createAppWindow(mode: WindowMode): Promise<ElectronBrowserWindow>
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
appWindowModes.set(window, mode);
|
appWindowModes.set(window, mode);
|
||||||
|
suppressNativeWindowMenu(window);
|
||||||
|
|
||||||
if (!initial.resizable) {
|
if (!initial.resizable) {
|
||||||
window.setMinimumSize(initial.minWidth, initial.minHeight);
|
window.setMinimumSize(initial.minWidth, initial.minHeight);
|
||||||
@@ -525,6 +556,7 @@ async function createAppWindow(mode: WindowMode): Promise<ElectronBrowserWindow>
|
|||||||
|
|
||||||
async function createSettingsWindow(): Promise<ElectronBrowserWindow> {
|
async function createSettingsWindow(): Promise<ElectronBrowserWindow> {
|
||||||
const initial = SETTINGS_WINDOW_SIZE;
|
const initial = SETTINGS_WINDOW_SIZE;
|
||||||
|
const backgroundColor = nativeTheme.shouldUseDarkColors ? "#15191a" : "#f2f4f7";
|
||||||
const window = new BrowserWindow({
|
const window = new BrowserWindow({
|
||||||
width: initial.width,
|
width: initial.width,
|
||||||
height: initial.height,
|
height: initial.height,
|
||||||
@@ -533,13 +565,14 @@ async function createSettingsWindow(): Promise<ElectronBrowserWindow> {
|
|||||||
show: false,
|
show: false,
|
||||||
title: "省心推设置",
|
title: "省心推设置",
|
||||||
icon: createDesktopHealthIcon("normal"),
|
icon: createDesktopHealthIcon("normal"),
|
||||||
titleBarStyle: "hiddenInset",
|
titleBarStyle: appTitleBarStyle(),
|
||||||
|
titleBarOverlay: appTitleBarOverlay(backgroundColor),
|
||||||
trafficLightPosition: { x: 14, y: 15 },
|
trafficLightPosition: { x: 14, y: 15 },
|
||||||
resizable: initial.resizable,
|
resizable: initial.resizable,
|
||||||
maximizable: initial.resizable,
|
maximizable: initial.resizable,
|
||||||
fullscreenable: false,
|
fullscreenable: false,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
backgroundColor: nativeTheme.shouldUseDarkColors ? "#15191a" : "#f2f4f7",
|
backgroundColor,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: preloadPath(),
|
preload: preloadPath(),
|
||||||
sandbox: true,
|
sandbox: true,
|
||||||
@@ -548,6 +581,7 @@ async function createSettingsWindow(): Promise<ElectronBrowserWindow> {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
appWindowModes.set(window, "settings");
|
appWindowModes.set(window, "settings");
|
||||||
|
suppressNativeWindowMenu(window);
|
||||||
|
|
||||||
window.once("closed", () => {
|
window.once("closed", () => {
|
||||||
if (settingsWindow === window) {
|
if (settingsWindow === window) {
|
||||||
@@ -703,6 +737,7 @@ if (!hasSingleInstanceLock) {
|
|||||||
app.exit(0);
|
app.exit(0);
|
||||||
} else {
|
} else {
|
||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
|
suppressNativeWindowMenu();
|
||||||
currentWindowMode = readPersistedWindowMode();
|
currentWindowMode = readPersistedWindowMode();
|
||||||
initDesktopAppSettings();
|
initDesktopAppSettings();
|
||||||
registerBridgeHandlers();
|
registerBridgeHandlers();
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ function syncWindowMode(authenticated: boolean): void {
|
|||||||
|
|
||||||
const rendererWindowMode = resolveRendererWindowMode();
|
const rendererWindowMode = resolveRendererWindowMode();
|
||||||
document.documentElement.dataset.windowMode = rendererWindowMode;
|
document.documentElement.dataset.windowMode = rendererWindowMode;
|
||||||
|
document.documentElement.dataset.platform =
|
||||||
|
navigator.platform.toLowerCase().includes("mac") ? "darwin" : "win32";
|
||||||
const usesAutomaticWindowMode = rendererWindowMode === "auto";
|
const usesAutomaticWindowMode = rendererWindowMode === "auto";
|
||||||
const showsSettingsSurface = rendererWindowMode === "settings";
|
const showsSettingsSurface = rendererWindowMode === "settings";
|
||||||
const showsMainSurface = computed(() =>
|
const showsMainSurface = computed(() =>
|
||||||
|
|||||||
@@ -170,11 +170,17 @@ function openSettingsWindow() {
|
|||||||
.window-drag-bar {
|
.window-drag-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 144px;
|
||||||
|
height: 24px;
|
||||||
|
z-index: 1000;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-platform="darwin"]) .window-drag-bar {
|
||||||
left: 80px;
|
left: 80px;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
z-index: 1000;
|
|
||||||
-webkit-app-region: drag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-shell :where(button, a, input, select, textarea, [role="button"], [role="link"]) {
|
.app-shell :where(button, a, input, select, textarea, [role="button"], [role="link"]) {
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ async function syncRuntimeSessionToMain(next: DesktopSession | null = session.va
|
|||||||
function formatLoginError(err: unknown): string {
|
function formatLoginError(err: unknown): string {
|
||||||
if (err instanceof ApiClientError) {
|
if (err instanceof ApiClientError) {
|
||||||
const messages: Record<string, string> = {
|
const messages: Record<string, string> = {
|
||||||
invalid_credentials: "邮箱或密码错误",
|
invalid_credentials: "账号或密码错误",
|
||||||
login_rate_limited: "登录请求过于频繁",
|
login_rate_limited: "登录请求过于频繁",
|
||||||
login_locked: "登录已被临时保护",
|
login_locked: "登录已被临时保护",
|
||||||
login_in_progress: "登录请求正在处理中",
|
login_in_progress: "登录请求正在处理中",
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ const { apiBaseURL, error, pending, setApiBaseURL, login } = useDesktopSession()
|
|||||||
const menuOpen = ref(false);
|
const menuOpen = ref(false);
|
||||||
const showServerDialog = ref(false);
|
const showServerDialog = ref(false);
|
||||||
const rememberPassword = ref(!!localStorage.getItem("geo_rankly_saved_password"));
|
const rememberPassword = ref(!!localStorage.getItem("geo_rankly_saved_password"));
|
||||||
|
const savedIdentifier =
|
||||||
|
localStorage.getItem("geo_rankly_saved_identifier")
|
||||||
|
?? localStorage.getItem("geo_rankly_saved_email")
|
||||||
|
?? "";
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
apiBaseURL: apiBaseURL.value,
|
apiBaseURL: apiBaseURL.value,
|
||||||
email: localStorage.getItem("geo_rankly_saved_email") || "",
|
identifier: savedIdentifier,
|
||||||
password: localStorage.getItem("geo_rankly_saved_password") || "",
|
password: localStorage.getItem("geo_rankly_saved_password") || "",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,16 +53,17 @@ function saveServerSettings() {
|
|||||||
|
|
||||||
async function submitLogin() {
|
async function submitLogin() {
|
||||||
if (rememberPassword.value) {
|
if (rememberPassword.value) {
|
||||||
localStorage.setItem("geo_rankly_saved_email", form.email);
|
localStorage.setItem("geo_rankly_saved_identifier", form.identifier);
|
||||||
|
localStorage.removeItem("geo_rankly_saved_email");
|
||||||
localStorage.setItem("geo_rankly_saved_password", form.password);
|
localStorage.setItem("geo_rankly_saved_password", form.password);
|
||||||
} else {
|
} else {
|
||||||
|
localStorage.removeItem("geo_rankly_saved_identifier");
|
||||||
localStorage.removeItem("geo_rankly_saved_email");
|
localStorage.removeItem("geo_rankly_saved_email");
|
||||||
localStorage.removeItem("geo_rankly_saved_password");
|
localStorage.removeItem("geo_rankly_saved_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
await login({
|
await login({
|
||||||
identifier: form.email,
|
identifier: form.identifier,
|
||||||
email: form.email,
|
|
||||||
password: form.password,
|
password: form.password,
|
||||||
});
|
});
|
||||||
await window.desktopBridge?.app?.setWindowMode?.("main", { source: "login" });
|
await window.desktopBridge?.app?.setWindowMode?.("main", { source: "login" });
|
||||||
@@ -111,10 +116,10 @@ onBeforeUnmount(() => {
|
|||||||
<form class="login-form" @submit.prevent="submitLogin">
|
<form class="login-form" @submit.prevent="submitLogin">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input
|
<input
|
||||||
v-model.trim="form.email"
|
v-model.trim="form.identifier"
|
||||||
type="email"
|
type="text"
|
||||||
autocomplete="username"
|
autocomplete="username"
|
||||||
placeholder="邮箱 / 账号"
|
placeholder="邮箱 / 手机号"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -255,12 +260,17 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
.top-bar {
|
.top-bar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 34px;
|
||||||
right: 10px;
|
right: 14px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(html[data-platform="darwin"]) .top-bar {
|
||||||
|
left: 52px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.menu-trigger {
|
.menu-trigger {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
|||||||
Reference in New Issue
Block a user