mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-13 04:59:51 +08:00
## Problem
Two audits of the repository (a workflow of finder, dedup and per-file verifier agents, run on 2026-10-11 against 1f4141e and again against 07f3e3c after a first round of refactoring) found dead code and features that were more machinery than the app needs: an Electron configuration-preset system with its own settings window, three copies of the about page, a hand-written `.env` parser, an "extended" system prompt nobody selected, two data migrations for formats from 2025-12 and 2026-01, a server-side example cache, type-to-confirm provider deletion, 81 unused dictionary keys, wrapper components around single call sites, and a long tail of unused exports, props, options and tests.
## Change
Every item below was either approved one by one or covered by "fix all of the others".
- **Electron:** the configuration-preset system is gone (settings window, preload, IPC, menu entry, dev watcher); `config-manager.ts` keeps only `userLocale`; `.env` files are parsed with `dotenv`. Entitlements file and a duplicate CI workflow removed; `test.yml` gets a type check and concurrency.
- **Server:** no extended prompt, no example cache, no `ENABLE_HISTORY_XML_REPLACE`, no message metadata or `hasConfig`; the connection test is one shared helper; EdgeOne loses its mock branch and model aliases; `DEBUG_LLM_PAYLOAD` logs message structure instead of payloads; Langfuse scoring helper; `apiKeyEnv` arrays kept and documented in `env.example` and the provider docs.
- **Pages:** one about page per language from a single component (old `/cn` and `/ja` URLs redirect with 308); layout uses `hasLocale`; the GitHub icon is a local component instead of `react-icons`; Cloudflare deploys without an R2 cache by default.
- **Chat and canvas:** the legacy `diagramHistory` conversion, the localStorage chat migration and the templates storage are gone; `chat-engine` reads the diagram through the context's refs; the diagram context has one tagged export path (`requestTaggedExport` / `fetchDiagramXml`); the screenshot check uses fetch + JSON and `generateText` with `Output.object`; `load-diagram` accepts `<svg:svg>` and strips the XML prolog.
- **Settings UI:** `model-selector` inlines its Dialog, Command and Tooltip; `ai-elements/model-selector`, `button-with-tooltip`, `error-toast` removed; no type-to-confirm, no footer, no `bedrockApiKey`; `use-model-config` loses the old-format migration; one reasoning component; the shimmer is CSS (no `motion`, no `@radix-ui/react-use-controllable-state`); 81 dictionary keys removed in all four languages.
- **MCP server:** `registerTool` / `ok` / `fail` replace per-tool try/catch; `syncFromBrowser`, `commitDocument`, `describePages`; the hand-written XML syntax check, unused result fields, `pickPageSelector`, `expandStyles` and the `host` attribute handling are gone; tests get a shared `setup.ts` and drive the server through the SDK client.
- **Prompt:** the web app's style rules come from the MCP drawing guide (`styleRules`), byte-identical to before.
- **Tests:** shared e2e helpers (`openApp`, `sse`, `toolCallEvents`, `chunkedReplies`); placeholder specs dropped; `settings-review-fixes` split by topic; unit tests use `vi.stubEnv`.
- **Dependencies removed:** `@xmldom/xmldom`, `remark-gfm`, `@anthropic-ai/tokenizer`, `@testing-library/user-event`, `concurrently`, `wait-on`, `eslint`, `eslint-config-next`, `@vitest/coverage-v8`, `shx`, `motion`, `@radix-ui/react-use-controllable-state`, `react-icons`.
Source lines: 74,983 before, 67,418 after (excluding lockfiles).
The last commit merges #984 (the canvas shell). Where the shell, the classic page or their tests depend on a wire format or signature this branch had changed, main's version wins (export fields of the poll response, `addHistory` signature); where main only extended code this branch restructured, main's change is carried into the new structure (`start_session`, `get_selection`, `prepareNewDiagram`, the diagram context's `requestExport`).
## Testing
- `tsc --noEmit` for the web app, `packages/mcp-server`, its shell and `electron`: clean
- `biome ci`: 0 errors (37 pre-existing warnings)
- Root `vitest run`: 719 passed; `packages/mcp-server` `vitest run`: 372 passed
- `next build`: clean
- Playwright, whole web suite on the dev server (4 workers): 280 tests, 279 passed; `language.spec.ts` "a stale offer to switch without saving does nothing once a message is being sent" failed under parallel load and passed alone, as it does on main
197 lines
6.8 KiB
TypeScript
197 lines
6.8 KiB
TypeScript
import { GoogleAnalytics } from "@next/third-parties/google"
|
|
import type { Metadata, Viewport } from "next"
|
|
import { Instrument_Sans, JetBrains_Mono } from "next/font/google"
|
|
import { notFound } from "next/navigation"
|
|
import Script from "next/script"
|
|
import { LocaleProvider } from "@/components/canvas/locale-context"
|
|
import { AppToaster } from "@/components/workspace/app-toaster"
|
|
import { DiagramProvider } from "@/contexts/diagram-context"
|
|
import { DictionaryProvider } from "@/hooks/use-dictionary"
|
|
import type { Locale } from "@/lib/i18n/config"
|
|
import { i18n } from "@/lib/i18n/config"
|
|
import { getDictionary, hasLocale } from "@/lib/i18n/dictionaries"
|
|
import { THEME_INIT_SCRIPT } from "@/stores/settings-store"
|
|
|
|
import "../globals.css"
|
|
|
|
const instrumentSans = Instrument_Sans({
|
|
variable: "--font-latin",
|
|
subsets: ["latin"],
|
|
weight: ["400", "500", "600", "700"],
|
|
// Chinese and Japanese text falls through to system fonts, in the
|
|
// order globals.css picks for the page's language
|
|
fallback: [],
|
|
})
|
|
|
|
const jetbrainsMono = JetBrains_Mono({
|
|
variable: "--font-mono",
|
|
subsets: ["latin"],
|
|
weight: ["400", "500"],
|
|
})
|
|
|
|
export const viewport: Viewport = {
|
|
width: "device-width",
|
|
initialScale: 1,
|
|
maximumScale: 1,
|
|
userScalable: false,
|
|
}
|
|
|
|
// Generate static params for all locales
|
|
export async function generateStaticParams() {
|
|
return i18n.locales.map((locale) => ({ lang: locale }))
|
|
}
|
|
|
|
const OG_LOCALE: Record<Locale, string> = {
|
|
en: "en_US",
|
|
zh: "zh_CN",
|
|
ja: "ja_JP",
|
|
"zh-Hant": "zh_HK",
|
|
}
|
|
|
|
// Generate metadata per locale
|
|
export async function generateMetadata({
|
|
params,
|
|
}: {
|
|
params: Promise<{ lang: string }>
|
|
}): Promise<Metadata> {
|
|
const { lang: rawLang } = await params
|
|
const lang: Locale = hasLocale(rawLang) ? rawLang : i18n.defaultLocale
|
|
|
|
const titles: Record<Locale, string> = {
|
|
en: "Next AI Draw.io - AI-Powered Diagram Generator",
|
|
zh: "Next AI Draw.io - AI 驱动的图表生成器",
|
|
ja: "Next AI Draw.io - AI 搭載ダイアグラム生成ツール",
|
|
"zh-Hant": "Next AI Draw.io - AI 驅動的圖表產生器",
|
|
}
|
|
|
|
const descriptions: Record<Locale, string> = {
|
|
en: "Create AWS architecture diagrams, flowcharts, and technical diagrams using AI. Free online tool integrating draw.io with AI assistance for professional diagram creation.",
|
|
zh: "使用 AI 创建 AWS 架构图、流程图和技术图表。免费在线工具,整合 draw.io 与 AI 辅助,轻松创建专业图表。",
|
|
ja: "AI で AWS アーキテクチャ図、フローチャート、技術図を作成。draw.io と AI アシスタントを統合した無料オンラインツールで、プロ品質の図を簡単に作成できます。",
|
|
"zh-Hant":
|
|
"使用 AI 建立 AWS 架構圖、流程圖和技術圖表。免費線上工具整合 draw.io 與 AI 輔助,輕鬆建立專業圖表。",
|
|
}
|
|
|
|
return {
|
|
title: titles[lang],
|
|
description: descriptions[lang],
|
|
keywords: [
|
|
"AI diagram generator",
|
|
"AWS architecture",
|
|
"flowchart creator",
|
|
"draw.io",
|
|
"AI drawing tool",
|
|
"technical diagrams",
|
|
"diagram automation",
|
|
"free diagram generator",
|
|
"online diagram maker",
|
|
],
|
|
authors: [{ name: "Next AI Draw.io" }],
|
|
creator: "Next AI Draw.io",
|
|
publisher: "Next AI Draw.io",
|
|
metadataBase: new URL("https://next-ai-drawio.jiang.jp"),
|
|
openGraph: {
|
|
title: titles[lang],
|
|
description: descriptions[lang],
|
|
type: "website",
|
|
url: "https://next-ai-drawio.jiang.jp",
|
|
siteName: "Next AI Draw.io",
|
|
locale: OG_LOCALE[lang],
|
|
images: [
|
|
{
|
|
url: "/architecture.png",
|
|
width: 1200,
|
|
height: 630,
|
|
alt: "Next AI Draw.io - AI-powered diagram creation tool",
|
|
},
|
|
],
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title: titles[lang],
|
|
description: descriptions[lang],
|
|
images: ["/architecture.png"],
|
|
},
|
|
robots: {
|
|
index: true,
|
|
follow: true,
|
|
googleBot: {
|
|
index: true,
|
|
follow: true,
|
|
"max-video-preview": -1,
|
|
"max-image-preview": "large",
|
|
"max-snippet": -1,
|
|
},
|
|
},
|
|
icons: {
|
|
icon: "/favicon.ico",
|
|
},
|
|
alternates: {
|
|
languages: {
|
|
en: "/en",
|
|
zh: "/zh",
|
|
ja: "/ja",
|
|
"zh-Hant": "/zh-Hant",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
export default async function RootLayout({
|
|
children,
|
|
params,
|
|
}: Readonly<{
|
|
children: React.ReactNode
|
|
params: Promise<{ lang: string }>
|
|
}>) {
|
|
const { lang } = await params
|
|
if (!hasLocale(lang)) notFound()
|
|
const validLang = lang as Locale
|
|
const dictionary = await getDictionary(validLang)
|
|
|
|
const jsonLd = {
|
|
"@context": "https://schema.org",
|
|
"@type": "SoftwareApplication",
|
|
name: "Next AI Draw.io",
|
|
applicationCategory: "DesignApplication",
|
|
operatingSystem: "Web Browser",
|
|
description:
|
|
"AI-powered diagram generator with targeted XML editing capabilities that integrates with draw.io for creating AWS architecture diagrams, flowcharts, and technical diagrams. Features diagram history, multi-provider AI support, and real-time collaboration.",
|
|
url: "https://next-ai-drawio.jiang.jp",
|
|
inLanguage: validLang,
|
|
offers: {
|
|
"@type": "Offer",
|
|
price: "0",
|
|
priceCurrency: "USD",
|
|
},
|
|
}
|
|
|
|
return (
|
|
<html lang={validLang} suppressHydrationWarning>
|
|
<head>
|
|
{/* Apply dark mode before the first paint (no white flash) */}
|
|
<Script id="theme-init" strategy="beforeInteractive">
|
|
{THEME_INIT_SCRIPT}
|
|
</Script>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
|
/>
|
|
</head>
|
|
<body
|
|
className={`${instrumentSans.variable} ${jetbrainsMono.variable} antialiased`}
|
|
>
|
|
<DictionaryProvider dictionary={dictionary}>
|
|
<LocaleProvider locale={validLang}>
|
|
<DiagramProvider>{children}</DiagramProvider>
|
|
</LocaleProvider>
|
|
</DictionaryProvider>
|
|
<AppToaster />
|
|
</body>
|
|
{process.env.NEXT_PUBLIC_GA_ID && (
|
|
<GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GA_ID} />
|
|
)}
|
|
</html>
|
|
)
|
|
}
|