mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 22:32:27 +08:00
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:
23
README.md
23
README.md
@@ -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
4
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,16 +216,21 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configWatcher = watch(configPath, { persistent: false }, async (eventType) => {
|
configWatcher = watch(
|
||||||
|
configPath,
|
||||||
|
{ persistent: false },
|
||||||
|
async (eventType) => {
|
||||||
if (eventType === "change" && !restartPending) {
|
if (eventType === "change" && !restartPending) {
|
||||||
restartPending = true
|
restartPending = true
|
||||||
console.log("\n🔄 Preset configuration changed, restarting Next.js server...")
|
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()
|
||||||
@@ -224,14 +238,20 @@ async function main() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await waitForServer(NEXT_URL)
|
await waitForServer(NEXT_URL)
|
||||||
console.log("✅ Next.js server restarted with new configuration\n")
|
console.log(
|
||||||
|
"✅ Next.js server restarted with new configuration\n",
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("❌ Failed to restart Next.js:", err.message)
|
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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user