Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,13 @@ Download the native desktop app for your platform from the [Releases page](https
| Linux | `.AppImage` or `.deb` (x64 & ARM64) |

**Features:**
- **Secure API key storage**: Credentials encrypted using OS keychain
- **Configuration presets**: Save and switch between AI providers via menu
- **Native file dialogs**: Open/save `.drawio` files directly
- **Offline capable**: Works without internet after first launch
- **Built-in settings**: Configure AI providers directly in the app

**Quick Setup:**
1. Download and install for your platform
2. Open the app → **Menu → Configuration → Manage Presets**
2. Click the settings icon in the chat panel
3. Add your AI provider credentials
4. Start creating diagrams!

Expand Down
47 changes: 1 addition & 46 deletions electron.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,6 @@
* Type declarations for Electron API exposed via preload script
*/

/** Configuration preset interface */
interface ConfigPreset {
id: string
name: string
createdAt: number
updatedAt: number
config: {
AI_PROVIDER?: string
AI_MODEL?: string
AI_API_KEY?: string
AI_BASE_URL?: string
TEMPERATURE?: string
[key: string]: string | undefined
}
}

/** Result of applying a preset */
interface ApplyPresetResult {
success: boolean
error?: string
env?: Record<string, string>
}

declare global {
interface Window {
/** Main window Electron API */
Expand All @@ -46,29 +23,7 @@ declare global {
/** Save data to file via save dialog */
saveFile: (data: string) => Promise<boolean>
}

/** Settings window Electron API */
settingsAPI?: {
/** Get all configuration presets */
getPresets: () => Promise<ConfigPreset[]>
/** Get current preset ID */
getCurrentPresetId: () => Promise<string | null>
/** Get current preset */
getCurrentPreset: () => Promise<ConfigPreset | null>
/** Save (create or update) a preset */
savePreset: (preset: {
id?: string
name: string
config: Record<string, string | undefined>
}) => Promise<ConfigPreset>
/** Delete a preset */
deletePreset: (id: string) => Promise<boolean>
/** Apply a preset (sets environment variables and restarts server) */
applyPreset: (id: string) => Promise<ApplyPresetResult>
/** Close settings window */
close: () => void
}
}
}

export { ConfigPreset, ApplyPresetResult }
export {}
120 changes: 2 additions & 118 deletions electron/main/app-menu.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,4 @@
import {
app,
BrowserWindow,
dialog,
Menu,
type MenuItemConstructorOptions,
shell,
} from "electron"
import {
applyPresetToEnv,
getAllPresets,
getCurrentPresetId,
setCurrentPreset,
} from "./config-manager"
import { restartNextServer } from "./next-server"
import { showSettingsWindow } from "./settings-window"
import { app, Menu, type MenuItemConstructorOptions, shell } from "electron"

/**
* Build and set the application menu
Expand All @@ -24,13 +9,6 @@ export function buildAppMenu(): void {
Menu.setApplicationMenu(menu)
}

/**
* Rebuild the menu (call this when presets change)
*/
export function rebuildAppMenu(): void {
buildAppMenu()
}

/**
* Get the menu template
*/
Expand All @@ -46,15 +24,6 @@ function getMenuTemplate(): MenuItemConstructorOptions[] {
submenu: [
{ role: "about" },
{ type: "separator" },
{
label: "Settings...",
accelerator: "CmdOrCtrl+,",
click: () => {
const win = BrowserWindow.getFocusedWindow()
showSettingsWindow(win || undefined)
},
},
{ type: "separator" },
{ role: "services" },
{ type: "separator" },
{ role: "hide" },
Expand All @@ -69,22 +38,7 @@ function getMenuTemplate(): MenuItemConstructorOptions[] {
// File menu
template.push({
label: "File",
submenu: [
...(isMac
? []
: [
{
label: "Settings",
accelerator: "CmdOrCtrl+,",
click: () => {
const win = BrowserWindow.getFocusedWindow()
showSettingsWindow(win || undefined)
},
},
{ type: "separator" } as MenuItemConstructorOptions,
]),
isMac ? { role: "close" } : { role: "quit" },
],
submenu: [isMac ? { role: "close" } : { role: "quit" }],
})

// Edit menu
Expand Down Expand Up @@ -129,9 +83,6 @@ function getMenuTemplate(): MenuItemConstructorOptions[] {
],
})

// Configuration menu with presets
template.push(buildConfigMenu())

// Window menu
template.push({
label: "Window",
Expand Down Expand Up @@ -172,70 +123,3 @@ function getMenuTemplate(): MenuItemConstructorOptions[] {

return template
}

/**
* Build the Configuration menu with presets
*/
function buildConfigMenu(): MenuItemConstructorOptions {
const presets = getAllPresets()
const currentPresetId = getCurrentPresetId()

const presetItems: MenuItemConstructorOptions[] = presets.map((preset) => ({
label: preset.name,
type: "radio",
checked: preset.id === currentPresetId,
click: async () => {
const previousPresetId = getCurrentPresetId()
const env = applyPresetToEnv(preset.id)

if (env) {
try {
await restartNextServer()
rebuildAppMenu() // Rebuild menu to update checkmarks
} catch (error) {
console.error("Failed to restart server:", error)

// Revert to previous preset on failure
if (previousPresetId) {
applyPresetToEnv(previousPresetId)
} else {
setCurrentPreset(null)
}

// Rebuild menu to restore previous checkmark state
rebuildAppMenu()

// Show error dialog to notify user
dialog.showErrorBox(
"Configuration Error",
`Failed to apply preset "${preset.name}". The server could not be restarted.\n\nThe previous configuration has been restored.\n\nError: ${error instanceof Error ? error.message : String(error)}`,
)
}
}
},
}))

return {
label: "Configuration",
submenu: [
...(presetItems.length > 0
? [
{ label: "Switch Preset", enabled: false },
{ type: "separator" } as MenuItemConstructorOptions,
...presetItems,
{ type: "separator" } as MenuItemConstructorOptions,
]
: []),
{
label:
presetItems.length > 0
? "Manage Presets..."
: "Add Configuration Preset...",
click: () => {
const win = BrowserWindow.getFocusedWindow()
showSettingsWindow(win || undefined)
},
},
],
}
}
Loading