mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-11 18:48:36 +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",
|
||||
"extends": ["config:recommended"],
|
||||
"schedule": ["after 10am on saturday"],
|
||||
"schedule": ["after 10am on the first day of the month"],
|
||||
"timezone": "Asia/Tokyo",
|
||||
"packageRules": [
|
||||
{
|
||||
@@ -13,6 +13,7 @@
|
||||
{
|
||||
"matchUpdateTypes": ["major"],
|
||||
"matchPackagePatterns": ["*"],
|
||||
"groupName": "major dependencies",
|
||||
"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 }}
|
||||
|
||||
- name: Upload unsigned artifacts for signing
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
id: upload-unsigned
|
||||
with:
|
||||
name: windows-unsigned
|
||||
|
||||
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
||||
name: Lint & Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
@@ -32,10 +32,10 @@ jobs:
|
||||
name: E2E Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
run: npm ci
|
||||
|
||||
- name: Cache Playwright browsers
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
CI: true
|
||||
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
|
||||
@@ -45,6 +45,7 @@ https://github.com/user-attachments/assets/9d60a3e8-4a1c-4b5e-acbb-26af2d3eabd1
|
||||
- [Multi-Provider Support](#multi-provider-support)
|
||||
- [How It Works](#how-it-works)
|
||||
- [Support \& Contact](#support--contact)
|
||||
- [FAQ](#faq)
|
||||
- [Star History](#star-history)
|
||||
|
||||
## Examples
|
||||
@@ -246,6 +247,10 @@ For support or inquiries, please open an issue on the GitHub repository or conta
|
||||
|
||||
- Email: me[at]jiang.jp
|
||||
|
||||
## FAQ
|
||||
|
||||
See [FAQ](./docs/en/FAQ.md) for common issues and solutions.
|
||||
|
||||
## Star History
|
||||
|
||||
[](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": {
|
||||
const sf = createOpenAI({
|
||||
apiKey,
|
||||
baseURL: baseUrl || "https://api.siliconflow.com/v1",
|
||||
baseURL: baseUrl || "https://api.siliconflow.cn/v1",
|
||||
})
|
||||
model = sf.chat(modelId)
|
||||
break
|
||||
|
||||
@@ -244,6 +244,19 @@
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
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 */
|
||||
|
||||
@@ -407,7 +407,7 @@ export function ChatInput({
|
||||
placeholder={dict.chat.placeholder}
|
||||
disabled={isDisabled}
|
||||
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">
|
||||
|
||||
@@ -1114,7 +1114,7 @@ export function ChatMessageDisplay({
|
||||
)}
|
||||
</button>
|
||||
{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">
|
||||
{
|
||||
section.content
|
||||
|
||||
@@ -43,7 +43,7 @@ export function HistoryDialog({
|
||||
|
||||
return (
|
||||
<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>
|
||||
<DialogTitle>{dict.history.title}</DialogTitle>
|
||||
<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历史)
|
||||
|
||||
## 示例
|
||||
@@ -238,6 +239,10 @@ npm run dev
|
||||
|
||||
- 邮箱:me[at]jiang.jp
|
||||
|
||||
## 常见问题
|
||||
|
||||
请参阅 [FAQ](./FAQ.md) 了解常见问题和解决方案。
|
||||
|
||||
## Star历史
|
||||
|
||||
[](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
|
||||
|
||||
## よくある質問
|
||||
|
||||
一般的な問題と解決策については [FAQ](./FAQ.md) をご覧ください。
|
||||
|
||||
## スター履歴
|
||||
|
||||
[](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 { DiagramOperation } from "@/components/chat/types"
|
||||
import { isMxCellXmlComplete, wrapWithMxFile } from "@/lib/utils"
|
||||
|
||||
const DEBUG = process.env.NODE_ENV === "development"
|
||||
@@ -29,12 +30,6 @@ type AddToolOutputParams = AddToolOutputSuccess | AddToolOutputError
|
||||
|
||||
type AddToolOutputFn = (params: AddToolOutputParams) => void
|
||||
|
||||
interface DiagramOperation {
|
||||
operation: "update" | "add" | "delete"
|
||||
cell_id: string
|
||||
new_xml?: string
|
||||
}
|
||||
|
||||
interface UseDiagramToolHandlersParams {
|
||||
partialXmlRef: MutableRefObject<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 { createOpenRouter } from "@openrouter/ai-sdk-provider"
|
||||
import { createOllama, ollama } from "ollama-ai-provider-v2"
|
||||
import type { ProviderName } from "@/lib/types/model-config"
|
||||
|
||||
export type ProviderName =
|
||||
| "bedrock"
|
||||
| "openai"
|
||||
| "anthropic"
|
||||
| "google"
|
||||
| "azure"
|
||||
| "ollama"
|
||||
| "openrouter"
|
||||
| "deepseek"
|
||||
| "siliconflow"
|
||||
| "sglang"
|
||||
| "gateway"
|
||||
| "edgeone"
|
||||
| "doubao"
|
||||
| "modelscope"
|
||||
export type { ProviderName }
|
||||
|
||||
interface ModelConfig {
|
||||
model: any
|
||||
@@ -464,7 +451,7 @@ function validateProviderCredentials(provider: ProviderName): void {
|
||||
* - DEEPSEEK_API_KEY: DeepSeek API key
|
||||
* - DEEPSEEK_BASE_URL: DeepSeek endpoint (optional)
|
||||
* - 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_BASE_URL: SGLang endpoint (optional)
|
||||
* - MODELSCOPE_API_KEY: ModelScope API key
|
||||
@@ -721,7 +708,7 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
const baseURL =
|
||||
overrides?.baseUrl ||
|
||||
process.env.SILICONFLOW_BASE_URL ||
|
||||
"https://api.siliconflow.com/v1"
|
||||
"https://api.siliconflow.cn/v1"
|
||||
const siliconflowProvider = createOpenAI({
|
||||
apiKey,
|
||||
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"
|
||||
| "azure"
|
||||
| "bedrock"
|
||||
| "ollama"
|
||||
| "openrouter"
|
||||
| "deepseek"
|
||||
| "siliconflow"
|
||||
@@ -76,11 +77,15 @@ export const PROVIDER_INFO: Record<
|
||||
google: { label: "Google" },
|
||||
azure: { label: "Azure OpenAI" },
|
||||
bedrock: { label: "Amazon Bedrock" },
|
||||
ollama: {
|
||||
label: "Ollama",
|
||||
defaultBaseUrl: "http://localhost:11434",
|
||||
},
|
||||
openrouter: { label: "OpenRouter" },
|
||||
deepseek: { label: "DeepSeek" },
|
||||
siliconflow: {
|
||||
label: "SiliconFlow",
|
||||
defaultBaseUrl: "https://api.siliconflow.com/v1",
|
||||
defaultBaseUrl: "https://api.siliconflow.cn/v1",
|
||||
},
|
||||
sglang: {
|
||||
label: "SGLang",
|
||||
@@ -99,7 +104,7 @@ export const PROVIDER_INFO: Record<
|
||||
}
|
||||
|
||||
// Suggested models per provider for quick add
|
||||
export const SUGGESTED_MODELS: Record<ProviderName, string[]> = {
|
||||
export const SUGGESTED_MODELS: Partial<Record<ProviderName, string[]>> = {
|
||||
openai: [
|
||||
"gpt-5.2-pro",
|
||||
"gpt-5.2-chat-latest",
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import * as pako from "pako"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import type { DiagramOperation } from "@/components/chat/types"
|
||||
|
||||
export type { DiagramOperation }
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
@@ -473,12 +476,6 @@ export function replaceNodes(currentXML: string, nodes: string): string {
|
||||
// ID-based Diagram Operations
|
||||
// ============================================================================
|
||||
|
||||
export interface DiagramOperation {
|
||||
operation: "update" | "add" | "delete"
|
||||
cell_id: string
|
||||
new_xml?: string
|
||||
}
|
||||
|
||||
export interface OperationError {
|
||||
type: "update" | "add" | "delete"
|
||||
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/tracing": "^4.4.9",
|
||||
"@next/third-parties": "^16.0.6",
|
||||
"@opennextjs/cloudflare": "1.14.7",
|
||||
"@opennextjs/cloudflare": "1.14.8",
|
||||
"@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",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
@@ -69,11 +69,10 @@
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"idb": "^8.0.3",
|
||||
"js-tiktoken": "^1.0.21",
|
||||
"jsonrepair": "^3.13.1",
|
||||
"lucide-react": "^0.562.0",
|
||||
"motion": "^12.23.25",
|
||||
"nanoid": "^3.3.11",
|
||||
"nanoid": "^5.0.0",
|
||||
"negotiator": "^1.0.0",
|
||||
"next": "^16.0.7",
|
||||
"ollama-ai-provider-v2": "^2.0.0",
|
||||
@@ -138,7 +137,7 @@
|
||||
"vite-tsconfig-paths": "^6.0.3",
|
||||
"vitest": "^4.0.16",
|
||||
"wait-on": "^9.0.3",
|
||||
"wrangler": "4.54.0"
|
||||
"wrangler": "4.58.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@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",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@next-ai-drawio/mcp-server",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.12",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.0.4",
|
||||
"linkedom": "^0.18.0",
|
||||
"open": "^11.0.0",
|
||||
"zod": "^3.24.0"
|
||||
"zod": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"next-ai-drawio-mcp": "dist/index.js"
|
||||
@@ -481,9 +481,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.1.tgz",
|
||||
"integrity": "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ==",
|
||||
"version": "1.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.2.tgz",
|
||||
"integrity": "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@hono/node-server": "^1.19.7",
|
||||
@@ -520,9 +520,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz",
|
||||
"integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==",
|
||||
"version": "24.10.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.6.tgz",
|
||||
"integrity": "sha512-B8h60xgJMR/xmgyX9fncRzEW9gCxoJjdenUhke2v1JGOd/V66KopmWrLPXi5oUI4VuiGK+d+HlXJjDRZMj21EQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2051,9 +2051,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
|
||||
"integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@modelcontextprotocol/sdk": "^1.0.4",
|
||||
"linkedom": "^0.18.0",
|
||||
"open": "^11.0.0",
|
||||
"zod": "^3.24.0"
|
||||
"zod": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.0.0",
|
||||
|
||||
@@ -38,4 +38,12 @@ const targetStaticDir = join(targetDir, ".next", "static")
|
||||
mkdirSync(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/")
|
||||
|
||||
Reference in New Issue
Block a user