chore: bump version to 0.4.5 (#346)

* chore: bump version to 0.4.5 and add desktop app to README

* style: auto-format with Biome

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Dayuan Jiang
2025-12-22 10:39:28 +09:00
committed by GitHub
parent f58274bb84
commit fc5eca877a
6 changed files with 82 additions and 35 deletions

View File

@@ -33,6 +33,7 @@ https://github.com/user-attachments/assets/9d60a3e8-4a1c-4b5e-acbb-26af2d3eabd1
- [MCP Server (Preview)](#mcp-server-preview) - [MCP Server (Preview)](#mcp-server-preview)
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [Try it Online](#try-it-online) - [Try it Online](#try-it-online)
- [Desktop Application](#desktop-application)
- [Run with Docker (Recommended)](#run-with-docker-recommended) - [Run with Docker (Recommended)](#run-with-docker-recommended)
- [Installation](#installation) - [Installation](#installation)
- [Deployment](#deployment) - [Deployment](#deployment)
@@ -135,6 +136,28 @@ No installation needed! Try the app directly on our demo site:
> **Bring Your Own API Key**: You can use your own API key to bypass usage limits on the demo site. Click the Settings icon in the chat panel to configure your provider and API key. Your key is stored locally in your browser and is never stored on the server. > **Bring Your Own API Key**: You can use your own API key to bypass usage limits on the demo site. Click the Settings icon in the chat panel to configure your provider and API key. Your key is stored locally in your browser and is never stored on the server.
### Desktop Application
Download the native desktop app for your platform from the [Releases page](https://github.com/DayuanJiang/next-ai-draw-io/releases):
| Platform | Download |
|----------|----------|
| macOS | `.dmg` (Intel & Apple Silicon) |
| Windows | `.exe` installer (x64 & ARM64) |
| 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
**Quick Setup:**
1. Download and install for your platform
2. Open the app → **Menu → Configuration → Manage Presets**
3. Add your AI provider credentials
4. Start creating diagrams!
### Run with Docker (Recommended) ### Run with Docker (Recommended)
If you just want to run it locally, the best way is to use Docker. If you just want to run it locally, the best way is to use Docker.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "next-ai-draw-io", "name": "next-ai-draw-io",
"version": "0.4.4", "version": "0.4.5",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "next-ai-draw-io", "name": "next-ai-draw-io",
"version": "0.4.4", "version": "0.4.5",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@ai-sdk/amazon-bedrock": "^3.0.70", "@ai-sdk/amazon-bedrock": "^3.0.70",

View File

@@ -1,6 +1,6 @@
{ {
"name": "next-ai-draw-io", "name": "next-ai-draw-io",
"version": "0.4.4", "version": "0.4.5",
"license": "Apache-2.0", "license": "Apache-2.0",
"private": true, "private": true,
"main": "dist-electron/main/index.js", "main": "dist-electron/main/index.js",

View File

@@ -6,19 +6,19 @@
const { cpSync, existsSync } = require("fs") const { cpSync, existsSync } = require("fs")
const path = require("path") const path = require("path")
module.exports = async function (context) { module.exports = async (context) => {
const appOutDir = context.appOutDir const appOutDir = context.appOutDir
const resourcesDir = path.join( const resourcesDir = path.join(
appOutDir, appOutDir,
context.packager.platform.name === "mac" context.packager.platform.name === "mac"
? `${context.packager.appInfo.productFilename}.app/Contents/Resources` ? `${context.packager.appInfo.productFilename}.app/Contents/Resources`
: "resources" : "resources",
) )
const standaloneDir = path.join(resourcesDir, "standalone") const standaloneDir = path.join(resourcesDir, "standalone")
const sourceNodeModules = path.join( const sourceNodeModules = path.join(
context.packager.projectDir, context.packager.projectDir,
"electron-standalone", "electron-standalone",
"node_modules" "node_modules",
) )
const targetNodeModules = path.join(standaloneDir, "node_modules") const targetNodeModules = path.join(standaloneDir, "node_modules")
@@ -29,11 +29,15 @@ module.exports = async function (context) {
console.log("[afterPack] node_modules copied successfully") console.log("[afterPack] node_modules copied successfully")
} else { } else {
console.error("[afterPack] Source or target directory not found!") console.error("[afterPack] Source or target directory not found!")
console.error(` Source: ${sourceNodeModules} exists: ${existsSync(sourceNodeModules)}`) console.error(
console.error(` Target dir: ${standaloneDir} exists: ${existsSync(standaloneDir)}`) ` Source: ${sourceNodeModules} exists: ${existsSync(sourceNodeModules)}`,
)
console.error(
` Target dir: ${standaloneDir} exists: ${existsSync(standaloneDir)}`,
)
throw new Error( throw new Error(
"[afterPack] Failed: Required directories not found. " + "[afterPack] Failed: Required directories not found. " +
"Ensure 'npm run electron:prepare' was run before building." "Ensure 'npm run electron:prepare' was run before building.",
) )
} }
} }

View File

@@ -11,10 +11,10 @@
*/ */
import { spawn } from "node:child_process" import { spawn } from "node:child_process"
import { fileURLToPath } from "node:url"
import { existsSync, readFileSync, watch } from "node:fs" import { existsSync, readFileSync, watch } from "node:fs"
import path from "node:path"
import os from "node:os" import os from "node:os"
import path from "node:path"
import { fileURLToPath } from "node:url"
const __dirname = path.dirname(fileURLToPath(import.meta.url)) const __dirname = path.dirname(fileURLToPath(import.meta.url))
const rootDir = path.join(__dirname, "..") const rootDir = path.join(__dirname, "..")
@@ -29,9 +29,18 @@ function getUserDataPath() {
const appName = "next-ai-draw-io" const appName = "next-ai-draw-io"
switch (process.platform) { switch (process.platform) {
case "darwin": case "darwin":
return path.join(os.homedir(), "Library", "Application Support", appName) return path.join(
os.homedir(),
"Library",
"Application Support",
appName,
)
case "win32": case "win32":
return path.join(process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming"), appName) return path.join(
process.env.APPDATA ||
path.join(os.homedir(), "AppData", "Roaming"),
appName,
)
default: default:
return path.join(os.homedir(), ".config", appName) return path.join(os.homedir(), ".config", appName)
} }
@@ -57,7 +66,7 @@ function loadPresetConfig() {
return null return null
} }
const preset = data.presets.find(p => p.id === data.currentPresetId) const preset = data.presets.find((p) => p.id === data.currentPresetId)
if (!preset) { if (!preset) {
console.log("📋 Active preset not found, using .env.local") console.log("📋 Active preset not found, using .env.local")
return null return null
@@ -207,31 +216,42 @@ async function main() {
} }
try { try {
configWatcher = watch(configPath, { persistent: false }, async (eventType) => { configWatcher = watch(
if (eventType === "change" && !restartPending) { configPath,
restartPending = true { persistent: false },
console.log("\n🔄 Preset configuration changed, restarting Next.js server...") async (eventType) => {
if (eventType === "change" && !restartPending) {
restartPending = true
console.log(
"\n🔄 Preset configuration changed, restarting Next.js server...",
)
// Kill current Next.js process // Kill current Next.js process
nextProcess.kill() nextProcess.kill()
// Wait a bit for process to die // Wait a bit for process to die
await new Promise(r => setTimeout(r, 1000)) await new Promise((r) => setTimeout(r, 1000))
// Reload preset and restart // Reload preset and restart
const newPresetEnv = loadPresetConfig() const newPresetEnv = loadPresetConfig()
nextProcess = startNextServer(newPresetEnv) nextProcess = startNextServer(newPresetEnv)
try { try {
await waitForServer(NEXT_URL) await waitForServer(NEXT_URL)
console.log("✅ Next.js server restarted with new configuration\n") console.log(
} catch (err) { "✅ Next.js server restarted with new configuration\n",
console.error("❌ Failed to restart Next.js:", err.message) )
} catch (err) {
console.error(
"❌ Failed to restart Next.js:",
err.message,
)
}
restartPending = false
} }
},
restartPending = false )
}
})
console.log("👀 Watching for preset configuration changes...") console.log("👀 Watching for preset configuration changes...")
} catch (err) { } catch (err) {
// File might not exist yet, that's ok // File might not exist yet, that's ok

View File

@@ -6,7 +6,7 @@
* that electron-builder can properly include * that electron-builder can properly include
*/ */
import { cpSync, rmSync, existsSync, mkdirSync } from "node:fs" import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs"
import { join } from "node:path" import { join } from "node:path"
import { fileURLToPath } from "node:url" import { fileURLToPath } from "node:url"