From fc5eca877a4c41f5182f9866bd7c461f29334ade Mon Sep 17 00:00:00 2001 From: Dayuan Jiang <34411969+DayuanJiang@users.noreply.github.com> Date: Mon, 22 Dec 2025 10:39:28 +0900 Subject: [PATCH] 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] --- README.md | 23 ++++++++++ package-lock.json | 4 +- package.json | 2 +- scripts/afterPack.cjs | 16 ++++--- scripts/electron-dev.mjs | 70 +++++++++++++++++++----------- scripts/prepare-electron-build.mjs | 2 +- 6 files changed, 82 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 42903b6..ec17213 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ https://github.com/user-attachments/assets/9d60a3e8-4a1c-4b5e-acbb-26af2d3eabd1 - [MCP Server (Preview)](#mcp-server-preview) - [Getting Started](#getting-started) - [Try it Online](#try-it-online) + - [Desktop Application](#desktop-application) - [Run with Docker (Recommended)](#run-with-docker-recommended) - [Installation](#installation) - [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. +### 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) If you just want to run it locally, the best way is to use Docker. diff --git a/package-lock.json b/package-lock.json index c3f6f49..1731ee9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "next-ai-draw-io", - "version": "0.4.4", + "version": "0.4.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "next-ai-draw-io", - "version": "0.4.4", + "version": "0.4.5", "license": "Apache-2.0", "dependencies": { "@ai-sdk/amazon-bedrock": "^3.0.70", diff --git a/package.json b/package.json index faee511..4c39e26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-ai-draw-io", - "version": "0.4.4", + "version": "0.4.5", "license": "Apache-2.0", "private": true, "main": "dist-electron/main/index.js", diff --git a/scripts/afterPack.cjs b/scripts/afterPack.cjs index e11d96f..839c46b 100644 --- a/scripts/afterPack.cjs +++ b/scripts/afterPack.cjs @@ -6,19 +6,19 @@ const { cpSync, existsSync } = require("fs") const path = require("path") -module.exports = async function (context) { +module.exports = async (context) => { const appOutDir = context.appOutDir const resourcesDir = path.join( appOutDir, context.packager.platform.name === "mac" ? `${context.packager.appInfo.productFilename}.app/Contents/Resources` - : "resources" + : "resources", ) const standaloneDir = path.join(resourcesDir, "standalone") const sourceNodeModules = path.join( context.packager.projectDir, "electron-standalone", - "node_modules" + "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") } else { console.error("[afterPack] Source or target directory not found!") - console.error(` Source: ${sourceNodeModules} exists: ${existsSync(sourceNodeModules)}`) - console.error(` Target dir: ${standaloneDir} exists: ${existsSync(standaloneDir)}`) + console.error( + ` Source: ${sourceNodeModules} exists: ${existsSync(sourceNodeModules)}`, + ) + console.error( + ` Target dir: ${standaloneDir} exists: ${existsSync(standaloneDir)}`, + ) throw new Error( "[afterPack] Failed: Required directories not found. " + - "Ensure 'npm run electron:prepare' was run before building." + "Ensure 'npm run electron:prepare' was run before building.", ) } } diff --git a/scripts/electron-dev.mjs b/scripts/electron-dev.mjs index 5c1e6e9..c8eb946 100644 --- a/scripts/electron-dev.mjs +++ b/scripts/electron-dev.mjs @@ -11,10 +11,10 @@ */ import { spawn } from "node:child_process" -import { fileURLToPath } from "node:url" import { existsSync, readFileSync, watch } from "node:fs" -import path from "node:path" import os from "node:os" +import path from "node:path" +import { fileURLToPath } from "node:url" const __dirname = path.dirname(fileURLToPath(import.meta.url)) const rootDir = path.join(__dirname, "..") @@ -29,9 +29,18 @@ function getUserDataPath() { const appName = "next-ai-draw-io" switch (process.platform) { case "darwin": - return path.join(os.homedir(), "Library", "Application Support", appName) + return path.join( + os.homedir(), + "Library", + "Application Support", + appName, + ) 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: return path.join(os.homedir(), ".config", appName) } @@ -57,7 +66,7 @@ function loadPresetConfig() { return null } - const preset = data.presets.find(p => p.id === data.currentPresetId) + const preset = data.presets.find((p) => p.id === data.currentPresetId) if (!preset) { console.log("šŸ“‹ Active preset not found, using .env.local") return null @@ -207,31 +216,42 @@ async function main() { } try { - configWatcher = watch(configPath, { persistent: false }, async (eventType) => { - if (eventType === "change" && !restartPending) { - restartPending = true - console.log("\nšŸ”„ Preset configuration changed, restarting Next.js server...") + configWatcher = watch( + configPath, + { persistent: false }, + async (eventType) => { + if (eventType === "change" && !restartPending) { + restartPending = true + console.log( + "\nšŸ”„ Preset configuration changed, restarting Next.js server...", + ) - // Kill current Next.js process - nextProcess.kill() + // Kill current Next.js process + nextProcess.kill() - // Wait a bit for process to die - await new Promise(r => setTimeout(r, 1000)) + // Wait a bit for process to die + await new Promise((r) => setTimeout(r, 1000)) - // Reload preset and restart - const newPresetEnv = loadPresetConfig() - nextProcess = startNextServer(newPresetEnv) + // Reload preset and restart + const newPresetEnv = loadPresetConfig() + nextProcess = startNextServer(newPresetEnv) - try { - await waitForServer(NEXT_URL) - console.log("āœ… Next.js server restarted with new configuration\n") - } catch (err) { - console.error("āŒ Failed to restart Next.js:", err.message) + try { + await waitForServer(NEXT_URL) + console.log( + "āœ… Next.js server restarted with new configuration\n", + ) + } catch (err) { + console.error( + "āŒ Failed to restart Next.js:", + err.message, + ) + } + + restartPending = false } - - restartPending = false - } - }) + }, + ) console.log("šŸ‘€ Watching for preset configuration changes...") } catch (err) { // File might not exist yet, that's ok diff --git a/scripts/prepare-electron-build.mjs b/scripts/prepare-electron-build.mjs index d2e8d0b..6cb5eaf 100644 --- a/scripts/prepare-electron-build.mjs +++ b/scripts/prepare-electron-build.mjs @@ -6,7 +6,7 @@ * 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 { fileURLToPath } from "node:url"