feat(desktop-client): suppress native menu bar on Windows/Linux and support phone number login
- Add suppressNativeWindowMenu() to hide native menu bar on all windows (Win/Linux only) - Set autoHideMenuBar: true on BrowserWindow creation as belt-and-suspenders - Import Menu from electron/main to support menu suppression - Login form: rename field from email to identifier, support phone number input - Migrate saved credential key from geo_rankly_saved_email to geo_rankly_saved_identifier with fallback - Update login error message: "邮箱或密码错误" → "账号或密码错误" - Remove unset ELECTRON_RUN_AS_NODE from dev script (not needed on Windows) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
"type": "module",
|
||||
"main": "out/main/bootstrap.cjs",
|
||||
"scripts": {
|
||||
"dev": "unset ELECTRON_RUN_AS_NODE && electron-vite dev --inspect=9229",
|
||||
"dev": "electron-vite dev --inspect=9229",
|
||||
"build": "electron-vite build",
|
||||
"package:mac": "pnpm run build && electron-builder --mac --arm64,universal",
|
||||
"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 { shell } from "electron";
|
||||
import { BrowserWindow, app, ipcMain, nativeTheme } from "electron/main";
|
||||
import { BrowserWindow, Menu, app, ipcMain, nativeTheme } from "electron/main";
|
||||
import type {
|
||||
BrowserWindow as ElectronBrowserWindow,
|
||||
IpcMainInvokeEvent,
|
||||
@@ -132,6 +132,15 @@ const SETTINGS_WINDOW_SIZE: WindowSizePreset = {
|
||||
resizable: true,
|
||||
};
|
||||
|
||||
function suppressNativeWindowMenu(window?: ElectronBrowserWindow): void {
|
||||
if (process.platform === "darwin") {
|
||||
return;
|
||||
}
|
||||
Menu.setApplicationMenu(null);
|
||||
window?.setMenu(null);
|
||||
window?.setMenuBarVisibility(false);
|
||||
}
|
||||
|
||||
function windowModePath(): string {
|
||||
return join(app.getPath("userData"), "window-mode.json");
|
||||
}
|
||||
@@ -463,6 +472,7 @@ async function createAppWindow(mode: WindowMode): Promise<ElectronBrowserWindow>
|
||||
resizable: initial.resizable,
|
||||
maximizable: initial.resizable,
|
||||
fullscreenable: initial.resizable,
|
||||
autoHideMenuBar: true,
|
||||
backgroundColor: mode === "login" ? "#eaf2ff" : nativeTheme.shouldUseDarkColors ? "#15191a" : "#f0f2f5",
|
||||
webPreferences: {
|
||||
preload: preloadPath(),
|
||||
@@ -472,6 +482,7 @@ async function createAppWindow(mode: WindowMode): Promise<ElectronBrowserWindow>
|
||||
},
|
||||
});
|
||||
appWindowModes.set(window, mode);
|
||||
suppressNativeWindowMenu(window);
|
||||
|
||||
if (!initial.resizable) {
|
||||
window.setMinimumSize(initial.minWidth, initial.minHeight);
|
||||
@@ -548,6 +559,7 @@ async function createSettingsWindow(): Promise<ElectronBrowserWindow> {
|
||||
},
|
||||
});
|
||||
appWindowModes.set(window, "settings");
|
||||
suppressNativeWindowMenu(window);
|
||||
|
||||
window.once("closed", () => {
|
||||
if (settingsWindow === window) {
|
||||
@@ -703,6 +715,7 @@ if (!hasSingleInstanceLock) {
|
||||
app.exit(0);
|
||||
} else {
|
||||
app.whenReady().then(async () => {
|
||||
suppressNativeWindowMenu();
|
||||
currentWindowMode = readPersistedWindowMode();
|
||||
initDesktopAppSettings();
|
||||
registerBridgeHandlers();
|
||||
|
||||
@@ -375,7 +375,7 @@ async function syncRuntimeSessionToMain(next: DesktopSession | null = session.va
|
||||
function formatLoginError(err: unknown): string {
|
||||
if (err instanceof ApiClientError) {
|
||||
const messages: Record<string, string> = {
|
||||
invalid_credentials: "邮箱或密码错误",
|
||||
invalid_credentials: "账号或密码错误",
|
||||
login_rate_limited: "登录请求过于频繁",
|
||||
login_locked: "登录已被临时保护",
|
||||
login_in_progress: "登录请求正在处理中",
|
||||
|
||||
@@ -7,10 +7,14 @@ const { apiBaseURL, error, pending, setApiBaseURL, login } = useDesktopSession()
|
||||
const menuOpen = ref(false);
|
||||
const showServerDialog = ref(false);
|
||||
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({
|
||||
apiBaseURL: apiBaseURL.value,
|
||||
email: localStorage.getItem("geo_rankly_saved_email") || "",
|
||||
identifier: savedIdentifier,
|
||||
password: localStorage.getItem("geo_rankly_saved_password") || "",
|
||||
});
|
||||
|
||||
@@ -49,16 +53,17 @@ function saveServerSettings() {
|
||||
|
||||
async function submitLogin() {
|
||||
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);
|
||||
} else {
|
||||
localStorage.removeItem("geo_rankly_saved_identifier");
|
||||
localStorage.removeItem("geo_rankly_saved_email");
|
||||
localStorage.removeItem("geo_rankly_saved_password");
|
||||
}
|
||||
|
||||
await login({
|
||||
identifier: form.email,
|
||||
email: form.email,
|
||||
identifier: form.identifier,
|
||||
password: form.password,
|
||||
});
|
||||
await window.desktopBridge?.app?.setWindowMode?.("main", { source: "login" });
|
||||
@@ -111,10 +116,10 @@ onBeforeUnmount(() => {
|
||||
<form class="login-form" @submit.prevent="submitLogin">
|
||||
<div class="field">
|
||||
<input
|
||||
v-model.trim="form.email"
|
||||
type="email"
|
||||
v-model.trim="form.identifier"
|
||||
type="text"
|
||||
autocomplete="username"
|
||||
placeholder="邮箱 / 账号"
|
||||
placeholder="邮箱 / 手机号"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user