mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-08 10:47:48 +08:00
Found by the second PR review: - With AWS_BEARER_TOKEN_BEDROCK set on the server, a request with the user's AWS keys ran on the server's token: the Bedrock SDK prefers it. Checked with Bedrock: invalid user keys used to get an answer. - An OpenAI key with the official URL filled in (the settings form does that) went to the Responses API. Back to main's rule: a configured base URL uses Chat Completions. - A user's Ollama key went to the server's OLLAMA_BASE_URL, for chat and for the model list. Like every other provider, it goes to the user's base URL or Ollama Cloud. - The server's keyless Ollama and EdgeOne were not counted in the quota. - AI_MODEL models on the server's keys ran on any provider with a server key, not only on AI_PROVIDER. - A user's Azure key without a base URL used the server's resource name. - The admin panel's Test button failed whenever access codes were set. - DeepSeek's errors in the stream (plain text) were shown as they were, without a hint and also on the server's keys. Bedrock's throttling in the stream was not recognised as a rate limit. - The EdgeOne function accepted text/plain; x=application/json, which other sites can send without a CORS preflight. - Desktop app: a launch that found the old port taken for a moment (the previous version still quitting after an update) remembered the new port for good. The new port is kept only when Windows reserves the old one. A failed read of the presets file moved it aside as corrupt, and a save could then replace the presets. Switching presets on the same port now reloads the page. The dev launcher no longer misses a preset change made before or during a restart.
135 lines
3.5 KiB
TypeScript
135 lines
3.5 KiB
TypeScript
import path from "node:path"
|
|
import { app, BrowserWindow, screen } from "electron"
|
|
|
|
let mainWindow: BrowserWindow | null = null
|
|
|
|
// URL of the app server the main window loads
|
|
let appUrl: string | null = null
|
|
|
|
/**
|
|
* Get the icon path based on platform
|
|
* Note: electron-builder converts icon.png during packaging,
|
|
* but at runtime we use PNG directly - Electron handles it
|
|
*/
|
|
function getIconPath(): string | undefined {
|
|
// macOS doesn't need explicit icon - it's embedded in the app bundle
|
|
if (process.platform === "darwin" && app.isPackaged) {
|
|
return undefined
|
|
}
|
|
|
|
const iconName = "icon.png"
|
|
|
|
if (app.isPackaged) {
|
|
return path.join(process.resourcesPath, iconName)
|
|
}
|
|
|
|
// Development: use icon.png from resources
|
|
return path.join(__dirname, "../../resources/icon.png")
|
|
}
|
|
|
|
/**
|
|
* Create the main application window
|
|
*/
|
|
export function createWindow(serverUrl: string): BrowserWindow {
|
|
appUrl = serverUrl
|
|
const { width, height } = screen.getPrimaryDisplay().workAreaSize
|
|
|
|
mainWindow = new BrowserWindow({
|
|
width: Math.min(1400, Math.floor(width * 0.9)),
|
|
height: Math.min(900, Math.floor(height * 0.9)),
|
|
minWidth: 800,
|
|
minHeight: 600,
|
|
title: "Next AI Draw.io",
|
|
icon: getIconPath(),
|
|
show: false, // Don't show until ready
|
|
webPreferences: {
|
|
preload: path.join(__dirname, "../preload/index.js"),
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
sandbox: true,
|
|
webSecurity: true,
|
|
},
|
|
})
|
|
|
|
// Load the Next.js application
|
|
mainWindow.loadURL(serverUrl)
|
|
|
|
// Show window when ready to prevent flashing
|
|
mainWindow.once("ready-to-show", () => {
|
|
mainWindow?.show()
|
|
})
|
|
|
|
// Open DevTools in development
|
|
if (!app.isPackaged) {
|
|
mainWindow.webContents.openDevTools()
|
|
}
|
|
|
|
// Override the draw.io iframe's beforeunload handler so the window can
|
|
// close after the user edits text in a shape (fixes #815). Diagrams are
|
|
// already persisted via autosave, so the prompt is unnecessary.
|
|
mainWindow.webContents.on("will-prevent-unload", (event) => {
|
|
event.preventDefault()
|
|
})
|
|
|
|
mainWindow.on("closed", () => {
|
|
mainWindow = null
|
|
})
|
|
|
|
// Handle page title updates
|
|
mainWindow.webContents.on("page-title-updated", (event, title) => {
|
|
if (
|
|
title &&
|
|
!title.includes("localhost") &&
|
|
!title.includes("127.0.0.1")
|
|
) {
|
|
mainWindow?.setTitle(title)
|
|
} else {
|
|
event.preventDefault()
|
|
}
|
|
})
|
|
|
|
return mainWindow
|
|
}
|
|
|
|
/**
|
|
* Get the main window instance
|
|
*/
|
|
export function getMainWindow(): BrowserWindow | null {
|
|
return mainWindow
|
|
}
|
|
|
|
/**
|
|
* Get the app server URL the main window loads
|
|
*/
|
|
export function getAppUrl(): string | null {
|
|
return appUrl
|
|
}
|
|
|
|
/**
|
|
* Point the main window at the restarted app server (it can come up on a
|
|
* different port). On the same port the page reloads, so it fetches the new
|
|
* preset's server models instead of sending the old preset's choice.
|
|
*/
|
|
export function setAppUrl(url: string): void {
|
|
if (url === appUrl) {
|
|
mainWindow?.webContents.reload()
|
|
return
|
|
}
|
|
appUrl = url
|
|
mainWindow?.loadURL(url)
|
|
}
|
|
|
|
/**
|
|
* Check if a URL belongs to the app server (same origin)
|
|
*/
|
|
export function isAppUrl(url: string | undefined): boolean {
|
|
if (!url || !appUrl) {
|
|
return false
|
|
}
|
|
try {
|
|
return new URL(url).origin === new URL(appUrl).origin
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|