mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-12 02:58:34 +08:00
Compare commits
28 Commits
v0.4.10
...
chore/redu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42ecc35950 | ||
|
|
bc0f96d3c9 | ||
|
|
f8f197db7b | ||
|
|
518b72a0be | ||
|
|
5e503551e6 | ||
|
|
09d478f6b2 | ||
|
|
1860233923 | ||
|
|
7bdb43cd20 | ||
|
|
3d816442f6 | ||
|
|
f59a2f2b01 | ||
|
|
17b765bfd9 | ||
|
|
555a21033b | ||
|
|
ef6517b89a | ||
|
|
945dffc949 | ||
|
|
0b41084c24 | ||
|
|
860eabd593 | ||
|
|
4ec901e713 | ||
|
|
3959e909c4 | ||
|
|
4a0973a373 | ||
|
|
cb09f8c74e | ||
|
|
53dbd5320b | ||
|
|
32d1361ffa | ||
|
|
4cf9661adb | ||
|
|
9430618660 | ||
|
|
d71fe70cbe | ||
|
|
22f4c2e270 | ||
|
|
73f282e568 | ||
|
|
53a2b8a0be |
3
.github/renovate.json
vendored
3
.github/renovate.json
vendored
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": ["config:recommended"],
|
"extends": ["config:recommended"],
|
||||||
"schedule": ["after 10am on saturday"],
|
"schedule": ["after 10am on the first day of the month"],
|
||||||
"timezone": "Asia/Tokyo",
|
"timezone": "Asia/Tokyo",
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
{
|
{
|
||||||
"matchUpdateTypes": ["major"],
|
"matchUpdateTypes": ["major"],
|
||||||
"matchPackagePatterns": ["*"],
|
"matchPackagePatterns": ["*"],
|
||||||
|
"groupName": "major dependencies",
|
||||||
"automerge": false
|
"automerge": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
2
.github/workflows/electron-release.yml
vendored
2
.github/workflows/electron-release.yml
vendored
@@ -67,7 +67,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Upload unsigned artifacts for signing
|
- name: Upload unsigned artifacts for signing
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
id: upload-unsigned
|
id: upload-unsigned
|
||||||
with:
|
with:
|
||||||
name: windows-unsigned
|
name: windows-unsigned
|
||||||
|
|||||||
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
|||||||
name: Lint & Unit Tests
|
name: Lint & Unit Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: "20"
|
node-version: "20"
|
||||||
cache: "npm"
|
cache: "npm"
|
||||||
@@ -32,10 +32,10 @@ jobs:
|
|||||||
name: E2E Tests
|
name: E2E Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: "20"
|
node-version: "20"
|
||||||
cache: "npm"
|
cache: "npm"
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Cache Playwright browsers
|
- name: Cache Playwright browsers
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
id: playwright-cache
|
id: playwright-cache
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/ms-playwright
|
path: ~/.cache/ms-playwright
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
CI: true
|
CI: true
|
||||||
|
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report
|
name: playwright-report
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ https://github.com/user-attachments/assets/9d60a3e8-4a1c-4b5e-acbb-26af2d3eabd1
|
|||||||
- [Multi-Provider Support](#multi-provider-support)
|
- [Multi-Provider Support](#multi-provider-support)
|
||||||
- [How It Works](#how-it-works)
|
- [How It Works](#how-it-works)
|
||||||
- [Support \& Contact](#support--contact)
|
- [Support \& Contact](#support--contact)
|
||||||
|
- [FAQ](#faq)
|
||||||
- [Star History](#star-history)
|
- [Star History](#star-history)
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
@@ -246,6 +247,10 @@ For support or inquiries, please open an issue on the GitHub repository or conta
|
|||||||
|
|
||||||
- Email: me[at]jiang.jp
|
- Email: me[at]jiang.jp
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
See [FAQ](./docs/en/FAQ.md) for common issues and solutions.
|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
[](https://www.star-history.com/#DayuanJiang/next-ai-draw-io&type=date&legend=top-left)
|
[](https://www.star-history.com/#DayuanJiang/next-ai-draw-io&type=date&legend=top-left)
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ export async function POST(req: Request) {
|
|||||||
case "siliconflow": {
|
case "siliconflow": {
|
||||||
const sf = createOpenAI({
|
const sf = createOpenAI({
|
||||||
apiKey,
|
apiKey,
|
||||||
baseURL: baseUrl || "https://api.siliconflow.com/v1",
|
baseURL: baseUrl || "https://api.siliconflow.cn/v1",
|
||||||
})
|
})
|
||||||
model = sf.chat(modelId)
|
model = sf.chat(modelId)
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -244,6 +244,19 @@
|
|||||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||||
background-color: oklch(0.75 0.01 260);
|
background-color: oklch(0.75 0.01 260);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dark mode scrollbar */
|
||||||
|
.dark .scrollbar-thin {
|
||||||
|
scrollbar-color: oklch(0.35 0.015 260) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .scrollbar-thin::-webkit-scrollbar-thumb {
|
||||||
|
background-color: oklch(0.35 0.015 260);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: oklch(0.45 0.015 260);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Smooth page transitions */
|
/* Smooth page transitions */
|
||||||
|
|||||||
@@ -407,7 +407,7 @@ export function ChatInput({
|
|||||||
placeholder={dict.chat.placeholder}
|
placeholder={dict.chat.placeholder}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
aria-label="Chat input"
|
aria-label="Chat input"
|
||||||
className="min-h-[60px] max-h-[200px] resize-none border-0 bg-transparent px-4 py-3 text-sm focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60"
|
className="min-h-[60px] max-h-[200px] resize-none border-0 bg-transparent px-4 py-3 text-sm focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60 scrollbar-thin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex items-center justify-end gap-1 px-3 py-2 border-t border-border/50">
|
<div className="flex items-center justify-end gap-1 px-3 py-2 border-t border-border/50">
|
||||||
|
|||||||
@@ -1114,7 +1114,7 @@ export function ChatMessageDisplay({
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{isExpanded && (
|
{isExpanded && (
|
||||||
<div className="px-3 py-2 border-t border-border/40 max-h-48 overflow-y-auto bg-muted/30">
|
<div className="px-3 py-2 border-t border-border/40 max-h-48 overflow-y-auto bg-muted/30 scrollbar-thin">
|
||||||
<pre className="text-xs whitespace-pre-wrap text-foreground/80">
|
<pre className="text-xs whitespace-pre-wrap text-foreground/80">
|
||||||
{
|
{
|
||||||
section.content
|
section.content
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function HistoryDialog({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={showHistory} onOpenChange={onToggleHistory}>
|
<Dialog open={showHistory} onOpenChange={onToggleHistory}>
|
||||||
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
|
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto scrollbar-thin">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{dict.history.title}</DialogTitle>
|
<DialogTitle>{dict.history.title}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
|
|
||||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card"
|
|
||||||
className={cn(
|
|
||||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-header"
|
|
||||||
className={cn(
|
|
||||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-[data-slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-title"
|
|
||||||
className={cn("leading-none font-semibold", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-description"
|
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-action"
|
|
||||||
className={cn(
|
|
||||||
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-content"
|
|
||||||
className={cn("px-6", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
data-slot="card-footer"
|
|
||||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
CardFooter,
|
|
||||||
CardTitle,
|
|
||||||
CardAction,
|
|
||||||
CardDescription,
|
|
||||||
CardContent,
|
|
||||||
}
|
|
||||||
78
docs/cn/FAQ.md
Normal file
78
docs/cn/FAQ.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# 常见问题解答 (FAQ)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 无法导出 PDF
|
||||||
|
|
||||||
|
**问题**: Web 版点击导出 PDF 后跳转到 `convert.diagrams.net/node/export` 然后无响应
|
||||||
|
|
||||||
|
**原因**: 嵌入式 Draw.io 不支持直接 PDF 导出,依赖外部转换服务,在 iframe 中无法正常工作
|
||||||
|
|
||||||
|
**解决方案**: 先导出为图片(PNG),再打印转成 PDF
|
||||||
|
|
||||||
|
**相关 Issue**: #539, #125
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 无法访问 embed.diagrams.net(离线/内网部署)
|
||||||
|
|
||||||
|
**问题**: 内网环境提示"找不到 embed.diagrams.net 的服务器 IP 地址"
|
||||||
|
|
||||||
|
**关键点**: `NEXT_PUBLIC_*` 环境变量是**构建时**变量,会被打包到 JS 代码中,**运行时设置无效**!
|
||||||
|
|
||||||
|
**解决方案**: 必须在构建时通过 `args` 传入:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
drawio:
|
||||||
|
image: jgraph/drawio:latest
|
||||||
|
ports: ["8080:8080"]
|
||||||
|
next-ai-draw-io:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
args:
|
||||||
|
- NEXT_PUBLIC_DRAWIO_BASE_URL=http://你的服务器IP:8080/
|
||||||
|
ports: ["3000:3000"]
|
||||||
|
env_file: .env
|
||||||
|
```
|
||||||
|
|
||||||
|
**内网用户**: 在外网修改 Dockerfile 并构建镜像,再传到内网使用
|
||||||
|
|
||||||
|
**相关 Issue**: #295, #317
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 自建模型只思考不画图
|
||||||
|
|
||||||
|
**问题**: 本地部署的模型(如 Qwen、LiteLLM)只输出思考过程,不生成图表
|
||||||
|
|
||||||
|
**可能原因**:
|
||||||
|
1. **模型太小** - 小模型难以正确遵循 tool calling 指令,建议使用 32B+ 参数的模型
|
||||||
|
2. **未开启 tool calling** - 模型服务需要配置 tool use 功能
|
||||||
|
|
||||||
|
**解决方案**: 开启 tool calling,例如 vLLM:
|
||||||
|
```bash
|
||||||
|
python -m vllm.entrypoints.openai.api_server \
|
||||||
|
--model Qwen/Qwen3-32B \
|
||||||
|
--enable-auto-tool-choice \
|
||||||
|
--tool-call-parser hermes
|
||||||
|
```
|
||||||
|
|
||||||
|
**相关 Issue**: #269, #75
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 上传图片后提示"未提供图片"
|
||||||
|
|
||||||
|
**问题**: 上传图片后,系统显示"未提供图片"错误
|
||||||
|
|
||||||
|
**可能原因**:
|
||||||
|
1. 模型不支持视觉功能(如 Kimi K2、DeepSeek、Qwen 文本模型)
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
- 使用支持视觉的模型:GPT-5.2、Claude 4.5 Sonnet、Gemini 3 Pro
|
||||||
|
- 模型名带 `vision` 或 `vl` 的支持图片
|
||||||
|
- 更新到最新版本(v0.4.9+)
|
||||||
|
|
||||||
|
**相关 Issue**: #324, #421, #469
|
||||||
@@ -42,6 +42,7 @@ https://github.com/user-attachments/assets/b2eef5f3-b335-4e71-a755-dc2e80931979
|
|||||||
- [多提供商支持](#多提供商支持)
|
- [多提供商支持](#多提供商支持)
|
||||||
- [工作原理](#工作原理)
|
- [工作原理](#工作原理)
|
||||||
- [支持与联系](#支持与联系)
|
- [支持与联系](#支持与联系)
|
||||||
|
- [常见问题](#常见问题)
|
||||||
- [Star历史](#star历史)
|
- [Star历史](#star历史)
|
||||||
|
|
||||||
## 示例
|
## 示例
|
||||||
@@ -238,6 +239,10 @@ npm run dev
|
|||||||
|
|
||||||
- 邮箱:me[at]jiang.jp
|
- 邮箱:me[at]jiang.jp
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
请参阅 [FAQ](./FAQ.md) 了解常见问题和解决方案。
|
||||||
|
|
||||||
## Star历史
|
## Star历史
|
||||||
|
|
||||||
[](https://www.star-history.com/#DayuanJiang/next-ai-draw-io&type=date&legend=top-left)
|
[](https://www.star-history.com/#DayuanJiang/next-ai-draw-io&type=date&legend=top-left)
|
||||||
|
|||||||
78
docs/en/FAQ.md
Normal file
78
docs/en/FAQ.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# Frequently Asked Questions (FAQ)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Cannot Export PDF
|
||||||
|
|
||||||
|
**Problem**: Web version redirects to `convert.diagrams.net/node/export` when exporting PDF, then nothing happens
|
||||||
|
|
||||||
|
**Cause**: Embedded Draw.io doesn't support direct PDF export, it relies on external conversion service which doesn't work in iframe
|
||||||
|
|
||||||
|
**Solution**: Export as image (PNG) first, then print to PDF
|
||||||
|
|
||||||
|
**Related Issues**: #539, #125
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Cannot Access embed.diagrams.net (Offline/Intranet Deployment)
|
||||||
|
|
||||||
|
**Problem**: Intranet environment shows "Cannot find server IP address for embed.diagrams.net"
|
||||||
|
|
||||||
|
**Key Point**: `NEXT_PUBLIC_*` environment variables are **build-time** variables, they get bundled into JS code. **Runtime settings don't work!**
|
||||||
|
|
||||||
|
**Solution**: Must pass via `args` at build time:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
drawio:
|
||||||
|
image: jgraph/drawio:latest
|
||||||
|
ports: ["8080:8080"]
|
||||||
|
next-ai-draw-io:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
args:
|
||||||
|
- NEXT_PUBLIC_DRAWIO_BASE_URL=http://your-server-ip:8080/
|
||||||
|
ports: ["3000:3000"]
|
||||||
|
env_file: .env
|
||||||
|
```
|
||||||
|
|
||||||
|
**Intranet Users**: Modify Dockerfile and build image on external network, then transfer to intranet
|
||||||
|
|
||||||
|
**Related Issues**: #295, #317
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Self-hosted Model Only Thinks But Doesn't Draw
|
||||||
|
|
||||||
|
**Problem**: Locally deployed models (e.g., Qwen, LiteLLM) only output thinking process, don't generate diagrams
|
||||||
|
|
||||||
|
**Possible Causes**:
|
||||||
|
1. **Model too small** - Small models struggle to follow tool calling instructions correctly, recommend 32B+ parameter models
|
||||||
|
2. **Tool calling not enabled** - Model service needs tool use configuration
|
||||||
|
|
||||||
|
**Solution**: Enable tool calling, e.g., vLLM:
|
||||||
|
```bash
|
||||||
|
python -m vllm.entrypoints.openai.api_server \
|
||||||
|
--model Qwen/Qwen3-32B \
|
||||||
|
--enable-auto-tool-choice \
|
||||||
|
--tool-call-parser hermes
|
||||||
|
```
|
||||||
|
|
||||||
|
**Related Issues**: #269, #75
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. "No Image Provided" After Uploading Image
|
||||||
|
|
||||||
|
**Problem**: After uploading an image, the system shows "No image provided" error
|
||||||
|
|
||||||
|
**Possible Causes**:
|
||||||
|
1. Model doesn't support vision (e.g., Kimi K2, DeepSeek, Qwen text models)
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- Use vision-capable models: GPT-5.2, Claude 4.5 Sonnet, Gemini 3 Pro
|
||||||
|
- Models with `vision` or `vl` in name support images
|
||||||
|
- Update to latest version (v0.4.9+)
|
||||||
|
|
||||||
|
**Related Issues**: #324, #421, #469
|
||||||
78
docs/ja/FAQ.md
Normal file
78
docs/ja/FAQ.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# よくある質問 (FAQ)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. PDFをエクスポートできない
|
||||||
|
|
||||||
|
**問題**: Web版でPDFエクスポートをクリックすると `convert.diagrams.net/node/export` にリダイレクトされ、その後何も起こらない
|
||||||
|
|
||||||
|
**原因**: 埋め込みDraw.ioは直接PDFエクスポートをサポートしておらず、外部変換サービスに依存しているが、iframe内では正常に動作しない
|
||||||
|
|
||||||
|
**解決策**: まず画像(PNG)としてエクスポートし、その後PDFに印刷する
|
||||||
|
|
||||||
|
**関連Issue**: #539, #125
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. embed.diagrams.netにアクセスできない(オフライン/イントラネットデプロイ)
|
||||||
|
|
||||||
|
**問題**: イントラネット環境で「embed.diagrams.netのサーバーIPアドレスが見つかりません」と表示される
|
||||||
|
|
||||||
|
**重要**: `NEXT_PUBLIC_*` 環境変数は**ビルド時**変数であり、JSコードにバンドルされます。**実行時の設定は無効です!**
|
||||||
|
|
||||||
|
**解決策**: ビルド時に `args` で渡す必要があります:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
drawio:
|
||||||
|
image: jgraph/drawio:latest
|
||||||
|
ports: ["8080:8080"]
|
||||||
|
next-ai-draw-io:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
args:
|
||||||
|
- NEXT_PUBLIC_DRAWIO_BASE_URL=http://あなたのサーバーIP:8080/
|
||||||
|
ports: ["3000:3000"]
|
||||||
|
env_file: .env
|
||||||
|
```
|
||||||
|
|
||||||
|
**イントラネットユーザー**: 外部ネットワークでDockerfileを修正してイメージをビルドし、イントラネットに転送する
|
||||||
|
|
||||||
|
**関連Issue**: #295, #317
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 自前モデルが思考するだけで描画しない
|
||||||
|
|
||||||
|
**問題**: ローカルデプロイのモデル(Qwen、LiteLLMなど)が思考過程のみを出力し、図表を生成しない
|
||||||
|
|
||||||
|
**考えられる原因**:
|
||||||
|
1. **モデルが小さすぎる** - 小さいモデルはtool calling指示に正しく従うことが難しい、32B+パラメータのモデルを推奨
|
||||||
|
2. **tool callingが有効になっていない** - モデルサービスでtool use機能を設定する必要がある
|
||||||
|
|
||||||
|
**解決策**: tool callingを有効にする、例えばvLLM:
|
||||||
|
```bash
|
||||||
|
python -m vllm.entrypoints.openai.api_server \
|
||||||
|
--model Qwen/Qwen3-32B \
|
||||||
|
--enable-auto-tool-choice \
|
||||||
|
--tool-call-parser hermes
|
||||||
|
```
|
||||||
|
|
||||||
|
**関連Issue**: #269, #75
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 画像アップロード後「画像が提供されていません」と表示される
|
||||||
|
|
||||||
|
**問題**: 画像をアップロードした後、「画像が提供されていません」というエラーが表示される
|
||||||
|
|
||||||
|
**考えられる原因**:
|
||||||
|
1. モデルがビジョン機能をサポートしていない(Kimi K2、DeepSeek、Qwenテキストモデルなど)
|
||||||
|
|
||||||
|
**解決策**:
|
||||||
|
- ビジョン対応モデルを使用:GPT-5.2、Claude 4.5 Sonnet、Gemini 3 Pro
|
||||||
|
- モデル名に `vision` または `vl` が含まれているものは画像をサポート
|
||||||
|
- 最新バージョン(v0.4.9+)にアップデート
|
||||||
|
|
||||||
|
**関連Issue**: #324, #421, #469
|
||||||
@@ -42,6 +42,7 @@ https://github.com/user-attachments/assets/b2eef5f3-b335-4e71-a755-dc2e80931979
|
|||||||
- [マルチプロバイダーサポート](#マルチプロバイダーサポート)
|
- [マルチプロバイダーサポート](#マルチプロバイダーサポート)
|
||||||
- [仕組み](#仕組み)
|
- [仕組み](#仕組み)
|
||||||
- [サポート&お問い合わせ](#サポートお問い合わせ)
|
- [サポート&お問い合わせ](#サポートお問い合わせ)
|
||||||
|
- [よくある質問](#よくある質問)
|
||||||
- [スター履歴](#スター履歴)
|
- [スター履歴](#スター履歴)
|
||||||
|
|
||||||
## 例
|
## 例
|
||||||
@@ -239,6 +240,10 @@ AWS BedrockとOpenRouter以外のすべてのプロバイダーはカスタム
|
|||||||
|
|
||||||
- メール:me[at]jiang.jp
|
- メール:me[at]jiang.jp
|
||||||
|
|
||||||
|
## よくある質問
|
||||||
|
|
||||||
|
一般的な問題と解決策については [FAQ](./FAQ.md) をご覧ください。
|
||||||
|
|
||||||
## スター履歴
|
## スター履歴
|
||||||
|
|
||||||
[](https://www.star-history.com/#DayuanJiang/next-ai-draw-io&type=date&legend=top-left)
|
[](https://www.star-history.com/#DayuanJiang/next-ai-draw-io&type=date&legend=top-left)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { MutableRefObject } from "react"
|
import type { MutableRefObject } from "react"
|
||||||
|
import type { DiagramOperation } from "@/components/chat/types"
|
||||||
import { isMxCellXmlComplete, wrapWithMxFile } from "@/lib/utils"
|
import { isMxCellXmlComplete, wrapWithMxFile } from "@/lib/utils"
|
||||||
|
|
||||||
const DEBUG = process.env.NODE_ENV === "development"
|
const DEBUG = process.env.NODE_ENV === "development"
|
||||||
@@ -29,12 +30,6 @@ type AddToolOutputParams = AddToolOutputSuccess | AddToolOutputError
|
|||||||
|
|
||||||
type AddToolOutputFn = (params: AddToolOutputParams) => void
|
type AddToolOutputFn = (params: AddToolOutputParams) => void
|
||||||
|
|
||||||
interface DiagramOperation {
|
|
||||||
operation: "update" | "add" | "delete"
|
|
||||||
cell_id: string
|
|
||||||
new_xml?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UseDiagramToolHandlersParams {
|
interface UseDiagramToolHandlersParams {
|
||||||
partialXmlRef: MutableRefObject<string>
|
partialXmlRef: MutableRefObject<string>
|
||||||
editDiagramOriginalXmlRef: MutableRefObject<Map<string, string>>
|
editDiagramOriginalXmlRef: MutableRefObject<Map<string, string>>
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import { STORAGE_KEYS } from "./storage"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get AI configuration from localStorage.
|
|
||||||
* Returns API keys and settings for custom AI providers.
|
|
||||||
* Used to override server defaults when user provides their own API key.
|
|
||||||
*/
|
|
||||||
export function getAIConfig() {
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
return {
|
|
||||||
accessCode: "",
|
|
||||||
aiProvider: "",
|
|
||||||
aiBaseUrl: "",
|
|
||||||
aiApiKey: "",
|
|
||||||
aiModel: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
accessCode: localStorage.getItem(STORAGE_KEYS.accessCode) || "",
|
|
||||||
aiProvider: localStorage.getItem(STORAGE_KEYS.aiProvider) || "",
|
|
||||||
aiBaseUrl: localStorage.getItem(STORAGE_KEYS.aiBaseUrl) || "",
|
|
||||||
aiApiKey: localStorage.getItem(STORAGE_KEYS.aiApiKey) || "",
|
|
||||||
aiModel: localStorage.getItem(STORAGE_KEYS.aiModel) || "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,22 +8,9 @@ import { createOpenAI, openai } from "@ai-sdk/openai"
|
|||||||
import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
|
import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
|
||||||
import { createOpenRouter } from "@openrouter/ai-sdk-provider"
|
import { createOpenRouter } from "@openrouter/ai-sdk-provider"
|
||||||
import { createOllama, ollama } from "ollama-ai-provider-v2"
|
import { createOllama, ollama } from "ollama-ai-provider-v2"
|
||||||
|
import type { ProviderName } from "@/lib/types/model-config"
|
||||||
|
|
||||||
export type ProviderName =
|
export type { ProviderName }
|
||||||
| "bedrock"
|
|
||||||
| "openai"
|
|
||||||
| "anthropic"
|
|
||||||
| "google"
|
|
||||||
| "azure"
|
|
||||||
| "ollama"
|
|
||||||
| "openrouter"
|
|
||||||
| "deepseek"
|
|
||||||
| "siliconflow"
|
|
||||||
| "sglang"
|
|
||||||
| "gateway"
|
|
||||||
| "edgeone"
|
|
||||||
| "doubao"
|
|
||||||
| "modelscope"
|
|
||||||
|
|
||||||
interface ModelConfig {
|
interface ModelConfig {
|
||||||
model: any
|
model: any
|
||||||
@@ -464,7 +451,7 @@ function validateProviderCredentials(provider: ProviderName): void {
|
|||||||
* - DEEPSEEK_API_KEY: DeepSeek API key
|
* - DEEPSEEK_API_KEY: DeepSeek API key
|
||||||
* - DEEPSEEK_BASE_URL: DeepSeek endpoint (optional)
|
* - DEEPSEEK_BASE_URL: DeepSeek endpoint (optional)
|
||||||
* - SILICONFLOW_API_KEY: SiliconFlow API key
|
* - SILICONFLOW_API_KEY: SiliconFlow API key
|
||||||
* - SILICONFLOW_BASE_URL: SiliconFlow endpoint (optional, defaults to https://api.siliconflow.com/v1)
|
* - SILICONFLOW_BASE_URL: SiliconFlow endpoint (optional, defaults to https://api.siliconflow.cn/v1)
|
||||||
* - SGLANG_API_KEY: SGLang API key
|
* - SGLANG_API_KEY: SGLang API key
|
||||||
* - SGLANG_BASE_URL: SGLang endpoint (optional)
|
* - SGLANG_BASE_URL: SGLang endpoint (optional)
|
||||||
* - MODELSCOPE_API_KEY: ModelScope API key
|
* - MODELSCOPE_API_KEY: ModelScope API key
|
||||||
@@ -721,7 +708,7 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
|||||||
const baseURL =
|
const baseURL =
|
||||||
overrides?.baseUrl ||
|
overrides?.baseUrl ||
|
||||||
process.env.SILICONFLOW_BASE_URL ||
|
process.env.SILICONFLOW_BASE_URL ||
|
||||||
"https://api.siliconflow.com/v1"
|
"https://api.siliconflow.cn/v1"
|
||||||
const siliconflowProvider = createOpenAI({
|
const siliconflowProvider = createOpenAI({
|
||||||
apiKey,
|
apiKey,
|
||||||
baseURL,
|
baseURL,
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* Token counting utilities using js-tiktoken
|
|
||||||
*
|
|
||||||
* Uses cl100k_base encoding (GPT-4) which is close to Claude's tokenization.
|
|
||||||
* This is a pure JavaScript implementation, no WASM required.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { encodingForModel } from "js-tiktoken"
|
|
||||||
import { DEFAULT_SYSTEM_PROMPT, EXTENDED_SYSTEM_PROMPT } from "./system-prompts"
|
|
||||||
|
|
||||||
const encoder = encodingForModel("gpt-4o")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Count the number of tokens in a text string
|
|
||||||
* @param text - The text to count tokens for
|
|
||||||
* @returns The number of tokens
|
|
||||||
*/
|
|
||||||
export function countTextTokens(text: string): number {
|
|
||||||
return encoder.encode(text).length
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get token counts for the system prompts
|
|
||||||
* Useful for debugging and optimizing prompt sizes
|
|
||||||
* @returns Object with token counts for default and extended prompts
|
|
||||||
*/
|
|
||||||
export function getSystemPromptTokenCounts(): {
|
|
||||||
default: number
|
|
||||||
extended: number
|
|
||||||
additions: number
|
|
||||||
} {
|
|
||||||
const defaultTokens = countTextTokens(DEFAULT_SYSTEM_PROMPT)
|
|
||||||
const extendedTokens = countTextTokens(EXTENDED_SYSTEM_PROMPT)
|
|
||||||
return {
|
|
||||||
default: defaultTokens,
|
|
||||||
extended: extendedTokens,
|
|
||||||
additions: extendedTokens - defaultTokens,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ export type ProviderName =
|
|||||||
| "google"
|
| "google"
|
||||||
| "azure"
|
| "azure"
|
||||||
| "bedrock"
|
| "bedrock"
|
||||||
|
| "ollama"
|
||||||
| "openrouter"
|
| "openrouter"
|
||||||
| "deepseek"
|
| "deepseek"
|
||||||
| "siliconflow"
|
| "siliconflow"
|
||||||
@@ -76,11 +77,15 @@ export const PROVIDER_INFO: Record<
|
|||||||
google: { label: "Google" },
|
google: { label: "Google" },
|
||||||
azure: { label: "Azure OpenAI" },
|
azure: { label: "Azure OpenAI" },
|
||||||
bedrock: { label: "Amazon Bedrock" },
|
bedrock: { label: "Amazon Bedrock" },
|
||||||
|
ollama: {
|
||||||
|
label: "Ollama",
|
||||||
|
defaultBaseUrl: "http://localhost:11434",
|
||||||
|
},
|
||||||
openrouter: { label: "OpenRouter" },
|
openrouter: { label: "OpenRouter" },
|
||||||
deepseek: { label: "DeepSeek" },
|
deepseek: { label: "DeepSeek" },
|
||||||
siliconflow: {
|
siliconflow: {
|
||||||
label: "SiliconFlow",
|
label: "SiliconFlow",
|
||||||
defaultBaseUrl: "https://api.siliconflow.com/v1",
|
defaultBaseUrl: "https://api.siliconflow.cn/v1",
|
||||||
},
|
},
|
||||||
sglang: {
|
sglang: {
|
||||||
label: "SGLang",
|
label: "SGLang",
|
||||||
@@ -99,7 +104,7 @@ export const PROVIDER_INFO: Record<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Suggested models per provider for quick add
|
// Suggested models per provider for quick add
|
||||||
export const SUGGESTED_MODELS: Record<ProviderName, string[]> = {
|
export const SUGGESTED_MODELS: Partial<Record<ProviderName, string[]>> = {
|
||||||
openai: [
|
openai: [
|
||||||
"gpt-5.2-pro",
|
"gpt-5.2-pro",
|
||||||
"gpt-5.2-chat-latest",
|
"gpt-5.2-chat-latest",
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { type ClassValue, clsx } from "clsx"
|
import { type ClassValue, clsx } from "clsx"
|
||||||
import * as pako from "pako"
|
import * as pako from "pako"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
|
import type { DiagramOperation } from "@/components/chat/types"
|
||||||
|
|
||||||
|
export type { DiagramOperation }
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs))
|
||||||
@@ -473,12 +476,6 @@ export function replaceNodes(currentXML: string, nodes: string): string {
|
|||||||
// ID-based Diagram Operations
|
// ID-based Diagram Operations
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
export interface DiagramOperation {
|
|
||||||
operation: "update" | "add" | "delete"
|
|
||||||
cell_id: string
|
|
||||||
new_xml?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OperationError {
|
export interface OperationError {
|
||||||
type: "update" | "add" | "delete"
|
type: "update" | "add" | "delete"
|
||||||
cellId: string
|
cellId: string
|
||||||
|
|||||||
3522
package-lock.json
generated
3522
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -47,9 +47,9 @@
|
|||||||
"@langfuse/otel": "^4.4.4",
|
"@langfuse/otel": "^4.4.4",
|
||||||
"@langfuse/tracing": "^4.4.9",
|
"@langfuse/tracing": "^4.4.9",
|
||||||
"@next/third-parties": "^16.0.6",
|
"@next/third-parties": "^16.0.6",
|
||||||
"@opennextjs/cloudflare": "1.14.7",
|
"@opennextjs/cloudflare": "1.14.8",
|
||||||
"@openrouter/ai-sdk-provider": "^1.5.4",
|
"@openrouter/ai-sdk-provider": "^1.5.4",
|
||||||
"@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
|
"@opentelemetry/exporter-trace-otlp-http": "^0.209.0",
|
||||||
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
@@ -69,11 +69,10 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"js-tiktoken": "^1.0.21",
|
|
||||||
"jsonrepair": "^3.13.1",
|
"jsonrepair": "^3.13.1",
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"motion": "^12.23.25",
|
"motion": "^12.23.25",
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^5.0.0",
|
||||||
"negotiator": "^1.0.0",
|
"negotiator": "^1.0.0",
|
||||||
"next": "^16.0.7",
|
"next": "^16.0.7",
|
||||||
"ollama-ai-provider-v2": "^2.0.0",
|
"ollama-ai-provider-v2": "^2.0.0",
|
||||||
@@ -138,7 +137,7 @@
|
|||||||
"vite-tsconfig-paths": "^6.0.3",
|
"vite-tsconfig-paths": "^6.0.3",
|
||||||
"vitest": "^4.0.16",
|
"vitest": "^4.0.16",
|
||||||
"wait-on": "^9.0.3",
|
"wait-on": "^9.0.3",
|
||||||
"wrangler": "4.54.0"
|
"wrangler": "4.58.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@openrouter/ai-sdk-provider": {
|
"@openrouter/ai-sdk-provider": {
|
||||||
|
|||||||
11
packages/claude-plugin/.claude-plugin/plugin.json
Normal file
11
packages/claude-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "next-ai-drawio",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "AI-powered Draw.io diagram generation with real-time browser preview. Create flowcharts, architecture diagrams, and more through natural language.",
|
||||||
|
"author": {
|
||||||
|
"name": "DayuanJiang"
|
||||||
|
},
|
||||||
|
"repository": "https://github.com/DayuanJiang/next-ai-draw-io",
|
||||||
|
"homepage": "https://next-ai-drawio.jiang.jp",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
}
|
||||||
8
packages/claude-plugin/.mcp.json
Normal file
8
packages/claude-plugin/.mcp.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"drawio": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["@next-ai-drawio/mcp-server@latest"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
107
packages/claude-plugin/README.md
Normal file
107
packages/claude-plugin/README.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# Next AI Draw.io - Claude Code Plugin
|
||||||
|
|
||||||
|
AI-powered Draw.io diagram generation with real-time browser preview for Claude Code.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### From Plugin Directory (Coming Soon)
|
||||||
|
|
||||||
|
Once approved, install via:
|
||||||
|
```
|
||||||
|
/plugin install next-ai-drawio
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude --plugin-dir /path/to/packages/claude-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
Or add the MCP server directly:
|
||||||
|
```bash
|
||||||
|
claude mcp add drawio -- npx @next-ai-drawio/mcp-server@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Real-time Preview**: Diagrams appear and update in your browser as Claude creates them
|
||||||
|
- **Version History**: Restore previous diagram versions with visual thumbnails
|
||||||
|
- **Natural Language**: Describe diagrams in plain text - flowcharts, architecture diagrams, etc.
|
||||||
|
- **Edit Support**: Modify existing diagrams with natural language instructions
|
||||||
|
- **Export**: Save diagrams as `.drawio` files
|
||||||
|
- **Self-contained**: Embedded server, no external dependencies required
|
||||||
|
|
||||||
|
## Use Case Examples
|
||||||
|
|
||||||
|
### 1. Create Architecture Diagrams
|
||||||
|
|
||||||
|
```
|
||||||
|
Generate an AWS architecture diagram with Lambda, API Gateway, DynamoDB,
|
||||||
|
and S3 for a serverless REST API
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Flowchart Generation
|
||||||
|
|
||||||
|
```
|
||||||
|
Create a flowchart showing the CI/CD pipeline: code commit -> build ->
|
||||||
|
test -> staging deploy -> production deploy with approval gates
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. System Design Documentation
|
||||||
|
|
||||||
|
```
|
||||||
|
Design a microservices e-commerce system with user service, product catalog,
|
||||||
|
shopping cart, order processing, and payment gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Cloud Architecture (AWS/GCP/Azure)
|
||||||
|
|
||||||
|
```
|
||||||
|
Generate a GCP architecture diagram with Cloud Run, Cloud SQL, and
|
||||||
|
Cloud Storage for a web application
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Sequence Diagrams
|
||||||
|
|
||||||
|
```
|
||||||
|
Create a sequence diagram showing OAuth 2.0 authorization code flow
|
||||||
|
between user, client app, auth server, and resource server
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Tools
|
||||||
|
|
||||||
|
| Tool | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `start_session` | Opens browser with real-time diagram preview |
|
||||||
|
| `create_new_diagram` | Create a new diagram from XML |
|
||||||
|
| `edit_diagram` | Edit diagram by ID-based operations |
|
||||||
|
| `get_diagram` | Get the current diagram XML |
|
||||||
|
| `export_diagram` | Save diagram to a `.drawio` file |
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
```
|
||||||
|
Claude Code <--stdio--> MCP Server <--http--> Browser (draw.io)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Ask Claude to create a diagram
|
||||||
|
2. Claude calls `start_session` to open a browser window
|
||||||
|
3. Claude generates diagram XML and sends it to the browser
|
||||||
|
4. You see the diagram update in real-time!
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `PORT` | `6002` | Port for the embedded HTTP server |
|
||||||
|
| `DRAWIO_BASE_URL` | `https://embed.diagrams.net` | Base URL for draw.io (for self-hosted deployments) |
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- [Homepage](https://next-ai-drawio.jiang.jp)
|
||||||
|
- [GitHub Repository](https://github.com/DayuanJiang/next-ai-draw-io)
|
||||||
|
- [MCP Server Documentation](https://github.com/DayuanJiang/next-ai-draw-io/tree/main/packages/mcp-server)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Apache-2.0
|
||||||
24
packages/mcp-server/package-lock.json
generated
24
packages/mcp-server/package-lock.json
generated
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@next-ai-drawio/mcp-server",
|
"name": "@next-ai-drawio/mcp-server",
|
||||||
"version": "0.1.6",
|
"version": "0.1.12",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@next-ai-drawio/mcp-server",
|
"name": "@next-ai-drawio/mcp-server",
|
||||||
"version": "0.1.6",
|
"version": "0.1.12",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.0.4",
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
||||||
"linkedom": "^0.18.0",
|
"linkedom": "^0.18.0",
|
||||||
"open": "^11.0.0",
|
"open": "^11.0.0",
|
||||||
"zod": "^3.24.0"
|
"zod": "^4.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"next-ai-drawio-mcp": "dist/index.js"
|
"next-ai-drawio-mcp": "dist/index.js"
|
||||||
@@ -481,9 +481,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@modelcontextprotocol/sdk": {
|
"node_modules/@modelcontextprotocol/sdk": {
|
||||||
"version": "1.25.1",
|
"version": "1.25.2",
|
||||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.2.tgz",
|
||||||
"integrity": "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ==",
|
"integrity": "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hono/node-server": "^1.19.7",
|
"@hono/node-server": "^1.19.7",
|
||||||
@@ -520,9 +520,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "24.10.4",
|
"version": "24.10.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.6.tgz",
|
||||||
"integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==",
|
"integrity": "sha512-B8h60xgJMR/xmgyX9fncRzEW9gCxoJjdenUhke2v1JGOd/V66KopmWrLPXi5oUI4VuiGK+d+HlXJjDRZMj21EQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2051,9 +2051,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "3.25.76",
|
"version": "4.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
|
||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
"integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
"@modelcontextprotocol/sdk": "^1.0.4",
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
||||||
"linkedom": "^0.18.0",
|
"linkedom": "^0.18.0",
|
||||||
"open": "^11.0.0",
|
"open": "^11.0.0",
|
||||||
"zod": "^3.24.0"
|
"zod": "^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.0.0",
|
"@types/node": "^24.0.0",
|
||||||
|
|||||||
@@ -38,4 +38,12 @@ const targetStaticDir = join(targetDir, ".next", "static")
|
|||||||
mkdirSync(targetStaticDir, { recursive: true })
|
mkdirSync(targetStaticDir, { recursive: true })
|
||||||
cpSync(staticDir, targetStaticDir, { recursive: true })
|
cpSync(staticDir, targetStaticDir, { recursive: true })
|
||||||
|
|
||||||
|
// Copy public folder (required for favicon-white.svg and other assets)
|
||||||
|
console.log("Copying public folder...")
|
||||||
|
const publicDir = join(rootDir, "public")
|
||||||
|
const targetPublicDir = join(targetDir, "public")
|
||||||
|
if (existsSync(publicDir)) {
|
||||||
|
cpSync(publicDir, targetPublicDir, { recursive: true })
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Done! Files prepared in electron-standalone/")
|
console.log("Done! Files prepared in electron-standalone/")
|
||||||
|
|||||||
Reference in New Issue
Block a user