mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-10 19:49:52 +08:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97a1c0b1d7 |
@@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
Download,
|
Download,
|
||||||
|
FilePlus,
|
||||||
MessageSquareDashed,
|
MessageSquareDashed,
|
||||||
PanelRightClose,
|
PanelRightClose,
|
||||||
Settings2,
|
Settings,
|
||||||
SquarePen,
|
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { useRef } from "react"
|
import { useRef } from "react"
|
||||||
import { DevXmlSimulator } from "@/components/dev-xml-simulator"
|
import { DevXmlSimulator } from "@/components/dev-xml-simulator"
|
||||||
@@ -139,14 +139,14 @@ export function ChatPanel({
|
|||||||
disabled={engine.isBusy}
|
disabled={engine.isBusy}
|
||||||
data-testid="new-chat-button"
|
data-testid="new-chat-button"
|
||||||
>
|
>
|
||||||
<SquarePen />
|
<FilePlus />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
label={dict.nav.settings}
|
label={dict.nav.settings}
|
||||||
onClick={() => openSettings()}
|
onClick={() => openSettings()}
|
||||||
data-testid="settings-button"
|
data-testid="settings-button"
|
||||||
>
|
>
|
||||||
<Settings2 />
|
<Settings />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{!mobile && (
|
{!mobile && (
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -173,7 +173,7 @@ export function ChatPanel({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="shrink-0 p-2.5 pt-1">
|
<div className="shrink-0 px-4 pt-1 pb-3">
|
||||||
<Composer
|
<Composer
|
||||||
placeholder={
|
placeholder={
|
||||||
engine.messages.length > 0 &&
|
engine.messages.length > 0 &&
|
||||||
|
|||||||
@@ -18,14 +18,18 @@ interface Example {
|
|||||||
file?: { path: string; name: string; type: string }
|
file?: { path: string; name: string; type: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Secondary start buttons: outlined, so they read as clickable */
|
||||||
|
const startButtonClass =
|
||||||
|
"inline-flex h-8 items-center gap-1.5 rounded-lg border border-border bg-card px-3 text-[13px] text-foreground transition-colors hover:bg-accent [&_svg]:text-muted-foreground"
|
||||||
|
|
||||||
async function loadExampleFile(file: NonNullable<Example["file"]>) {
|
async function loadExampleFile(file: NonNullable<Example["file"]>) {
|
||||||
const response = await fetch(getAssetUrl(file.path))
|
const response = await fetch(getAssetUrl(file.path))
|
||||||
const blob = await response.blob()
|
const blob = await response.blob()
|
||||||
return new File([blob], file.name, { type: file.type })
|
return new File([blob], file.name, { type: file.type })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** One start-screen choice: a small picture, a title and one line */
|
/** One start-screen example: a small picture over its title */
|
||||||
function ExampleCard({
|
function ExampleTile({
|
||||||
onClick,
|
onClick,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
@@ -35,6 +39,7 @@ function ExampleCard({
|
|||||||
}: {
|
}: {
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
title: string
|
title: string
|
||||||
|
/** Shown as the tooltip: the title alone names the example */
|
||||||
description: string
|
description: string
|
||||||
picture: React.ReactNode
|
picture: React.ReactNode
|
||||||
/** An icon on a tinted tile instead of a diagram on paper */
|
/** An icon on a tinted tile instead of a diagram on paper */
|
||||||
@@ -45,24 +50,20 @@ function ExampleCard({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className="group/example flex min-w-0 items-center gap-2.5 rounded-xl border border-border bg-card p-1.5 pr-2.5 text-left transition-colors hover:border-foreground/25"
|
title={description}
|
||||||
|
className="group/example flex min-w-0 flex-col gap-2 rounded-xl border border-border bg-card p-1.5 pb-2.5 text-left transition-colors hover:border-foreground/25"
|
||||||
data-testid={testId}
|
data-testid={testId}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-12 w-16 shrink-0 items-center justify-center overflow-hidden rounded-lg",
|
"flex h-16 w-full items-center justify-center overflow-hidden rounded-lg",
|
||||||
plain ? "bg-muted" : "sheet-light bg-white",
|
plain ? "bg-muted" : "sheet-light bg-white",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{picture}
|
{picture}
|
||||||
</span>
|
</span>
|
||||||
<span className="min-w-0 flex-1">
|
<span className="px-1 text-[13px] leading-snug font-medium text-foreground">
|
||||||
<span className="line-clamp-2 text-[13px] leading-snug font-medium text-foreground">
|
{title}
|
||||||
{title}
|
|
||||||
</span>
|
|
||||||
<span className="mt-0.5 block truncate text-xs text-muted-foreground">
|
|
||||||
{description}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
@@ -92,6 +93,7 @@ export function LobbyHero({
|
|||||||
key: "paper",
|
key: "paper",
|
||||||
title: dict.examples.paperToDiagram,
|
title: dict.examples.paperToDiagram,
|
||||||
description: dict.examples.paperDescription,
|
description: dict.examples.paperDescription,
|
||||||
|
image: "/paper_demo.svg",
|
||||||
prompt: "Summarize this paper as a diagram",
|
prompt: "Summarize this paper as a diagram",
|
||||||
file: {
|
file: {
|
||||||
path: "/chain-of-thought.txt",
|
path: "/chain-of-thought.txt",
|
||||||
@@ -185,10 +187,12 @@ export function LobbyHero({
|
|||||||
placeholder={t.placeholder}
|
placeholder={t.placeholder}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Examples: one row of tiles; the ways to bring your own
|
||||||
|
diagram follow as a separate group */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"mt-5 grid gap-2",
|
"mt-5 grid gap-2",
|
||||||
compact ? "grid-cols-1" : "grid-cols-3",
|
compact ? "grid-cols-1" : "grid-cols-5",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{examples.map((example) =>
|
{examples.map((example) =>
|
||||||
@@ -209,7 +213,7 @@ export function LobbyHero({
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<ExampleCard
|
<ExampleTile
|
||||||
key={example.key}
|
key={example.key}
|
||||||
onClick={() => pickExample(example)}
|
onClick={() => pickExample(example)}
|
||||||
title={example.title}
|
title={example.title}
|
||||||
@@ -221,7 +225,7 @@ export function LobbyHero({
|
|||||||
<img
|
<img
|
||||||
src={getAssetUrl(example.image)}
|
src={getAssetUrl(example.image)}
|
||||||
alt=""
|
alt=""
|
||||||
className="max-h-10 max-w-[56px] object-contain transition-transform duration-300 group-hover/example:scale-[1.06]"
|
className="max-h-14 max-w-[88%] object-contain transition-transform duration-300 group-hover/example:scale-[1.06]"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FileText className="size-5 text-muted-foreground" />
|
<FileText className="size-5 text-muted-foreground" />
|
||||||
@@ -231,48 +235,34 @@ export function LobbyHero({
|
|||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
{!compact && (
|
|
||||||
<ExampleCard
|
|
||||||
onClick={onOpenFile}
|
|
||||||
title={dict.workspace.openFile}
|
|
||||||
description={t.openFileHint}
|
|
||||||
picture={
|
|
||||||
<FileUp className="size-5 text-muted-foreground" />
|
|
||||||
}
|
|
||||||
plain
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-3 text-center text-xs text-faint">
|
<p className="mt-2.5 text-center text-xs text-faint">
|
||||||
{dict.examples.cachedNote}
|
{dict.examples.cachedNote}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="mt-6 flex flex-wrap items-center justify-center gap-x-5 gap-y-2 text-[13px] text-muted-foreground">
|
{/* Ways to start without an example, as plain buttons */}
|
||||||
|
<div className="mt-6 flex flex-wrap items-center justify-center gap-2">
|
||||||
{engine.sessions.length > 0 && (
|
{engine.sessions.length > 0 && (
|
||||||
<SessionMenu align="center" onOpenFile={onOpenFile}>
|
<SessionMenu align="center" onOpenFile={onOpenFile}>
|
||||||
<button
|
<button type="button" className={startButtonClass}>
|
||||||
type="button"
|
|
||||||
className="inline-flex items-center gap-1.5 hover:text-foreground"
|
|
||||||
>
|
|
||||||
<History className="size-4" />
|
<History className="size-4" />
|
||||||
{t.recent}
|
{t.recent}
|
||||||
</button>
|
</button>
|
||||||
</SessionMenu>
|
</SessionMenu>
|
||||||
)}
|
)}
|
||||||
{compact && (
|
<button
|
||||||
<button
|
type="button"
|
||||||
type="button"
|
onClick={onOpenFile}
|
||||||
onClick={onOpenFile}
|
className={startButtonClass}
|
||||||
className="inline-flex items-center gap-1.5 hover:text-foreground"
|
data-testid="open-file"
|
||||||
>
|
>
|
||||||
<FileUp className="size-4" />
|
<FileUp className="size-4" />
|
||||||
{dict.workspace.openFile}
|
{dict.workspace.openFile}
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onDrawYourself}
|
onClick={onDrawYourself}
|
||||||
className="inline-flex items-center gap-1.5 hover:text-foreground"
|
className={startButtonClass}
|
||||||
data-testid="draw-yourself"
|
data-testid="draw-yourself"
|
||||||
>
|
>
|
||||||
<Pencil className="size-4" />
|
<Pencil className="size-4" />
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
AlertCircle,
|
AlertCircle,
|
||||||
Check,
|
Check,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
Code2,
|
|
||||||
Copy,
|
Copy,
|
||||||
Library,
|
Library,
|
||||||
PenTool,
|
PenTool,
|
||||||
@@ -180,17 +179,23 @@ function VersionCard({
|
|||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
||||||
// What the AI wrote, on every card (main showed it on every tool call)
|
// What the AI wrote: a text link in the summary line, so the card's
|
||||||
const codeButton = (
|
// only button is the action (undo or restore)
|
||||||
<button
|
const summaryLine = (
|
||||||
type="button"
|
<div className="text-xs text-muted-foreground">
|
||||||
onClick={() => setShowCode((v) => !v)}
|
{summary}
|
||||||
aria-label={showCode ? t.hideCode : t.showCode}
|
{/* The link wraps as one unit with its separator */}
|
||||||
title={showCode ? t.hideCode : t.showCode}
|
<span className="whitespace-nowrap">
|
||||||
className="inline-flex size-7 shrink-0 items-center justify-center rounded-lg text-muted-foreground hover:bg-accent hover:text-foreground"
|
<span aria-hidden> · </span>
|
||||||
>
|
<button
|
||||||
<Code2 className="size-4" />
|
type="button"
|
||||||
</button>
|
onClick={() => setShowCode((v) => !v)}
|
||||||
|
className="underline decoration-foreground/30 underline-offset-2 hover:text-foreground hover:decoration-foreground"
|
||||||
|
>
|
||||||
|
{showCode ? t.hideCode : t.showCode}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
const codePanel = showCode && (
|
const codePanel = showCode && (
|
||||||
<div className="relative border-t border-border bg-surface-1 px-3 py-2.5">
|
<div className="relative border-t border-border bg-surface-1 px-3 py-2.5">
|
||||||
@@ -231,15 +236,12 @@ function VersionCard({
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{badge}
|
{badge}
|
||||||
<div className="min-w-0 flex-1" title={summary}>
|
<div className="min-w-0 flex-1">
|
||||||
<div className="truncate text-[13px] text-foreground">
|
<div className="truncate text-[13px] text-foreground">
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
<div className="truncate text-xs text-muted-foreground">
|
{summaryLine}
|
||||||
{summary}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{codeButton}
|
|
||||||
{restoreButton}
|
{restoreButton}
|
||||||
</div>
|
</div>
|
||||||
{codePanel}
|
{codePanel}
|
||||||
@@ -271,17 +273,12 @@ function VersionCard({
|
|||||||
{t.compare}
|
{t.compare}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<div className="flex items-center gap-2.5 px-3 py-2.5">
|
{/* Title row with the action; the summary runs under both */}
|
||||||
|
<div className="grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-x-2.5 px-3 py-2.5">
|
||||||
{badge}
|
{badge}
|
||||||
<div className="min-w-0 flex-1" title={summary}>
|
<div className="truncate text-[13px] font-medium text-foreground">
|
||||||
<div className="truncate text-[13px] font-medium text-foreground">
|
{title}
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
<div className="line-clamp-2 text-xs text-muted-foreground">
|
|
||||||
{summary}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{codeButton}
|
|
||||||
{canUndo ? (
|
{canUndo ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -300,6 +297,9 @@ function VersionCard({
|
|||||||
) : (
|
) : (
|
||||||
restoreButton
|
restoreButton
|
||||||
)}
|
)}
|
||||||
|
<div className="col-span-2 col-start-2 mt-0.5">
|
||||||
|
{summaryLine}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{codePanel}
|
{codePanel}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -220,14 +220,14 @@ export function ModelSelector({
|
|||||||
<span className="min-w-0 truncate text-xs">
|
<span className="min-w-0 truncate text-xs">
|
||||||
{selectedModel
|
{selectedModel
|
||||||
? shortModelName(selectedModel.modelId)
|
? shortModelName(selectedModel.modelId)
|
||||||
: dict.modelConfig.default}
|
: dict.modelConfig.serverDefault}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
// Keep an sr-only label for screen readers when hidden
|
// Keep an sr-only label for screen readers when hidden
|
||||||
<span className="sr-only">
|
<span className="sr-only">
|
||||||
{selectedModel
|
{selectedModel
|
||||||
? selectedModel.modelId
|
? selectedModel.modelId
|
||||||
: dict.modelConfig.default}
|
: dict.modelConfig.serverDefault}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<ChevronDown className="h-3 w-3 flex-shrink-0 text-muted-foreground" />
|
<ChevronDown className="h-3 w-3 flex-shrink-0 text-muted-foreground" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { MessageSquare, PanelRightOpen, Settings2, Shapes } from "lucide-react"
|
import { MessageSquare, PanelRightOpen, Settings, Shapes } from "lucide-react"
|
||||||
import { usePathname, useRouter } from "next/navigation"
|
import { usePathname, useRouter } from "next/navigation"
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
import { CanvasStage } from "@/components/canvas/canvas-stage"
|
import { CanvasStage } from "@/components/canvas/canvas-stage"
|
||||||
@@ -100,7 +100,7 @@ function StartBar({
|
|||||||
onClick={() => openSettings()}
|
onClick={() => openSettings()}
|
||||||
data-testid="settings-button"
|
data-testid="settings-button"
|
||||||
>
|
>
|
||||||
<Settings2 />
|
<Settings />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
"replicateDescription": "Upload and replicate an existing flowchart",
|
"replicateDescription": "Upload and replicate an existing flowchart",
|
||||||
"creativeDrawing": "Creative drawing",
|
"creativeDrawing": "Creative drawing",
|
||||||
"creativeDescription": "Draw something fun and creative",
|
"creativeDescription": "Draw something fun and creative",
|
||||||
"cachedNote": "Examples open instantly and don't use your model.",
|
"cachedNote": "Examples use saved answers: they appear instantly and don't call the AI.",
|
||||||
"mcpServer": "MCP Server",
|
"mcpServer": "MCP Server",
|
||||||
"mcpDescription": "Use in Claude Desktop, VS Code & Cursor"
|
"mcpDescription": "Use in Claude Desktop, VS Code & Cursor"
|
||||||
},
|
},
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"language": "Language",
|
"language": "Language",
|
||||||
"languageDescription": "Choose your interface language.",
|
"languageDescription": "Choose your interface language.",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
"themeDescription": "Light or dark interface. The diagram always stays on a white sheet, the way it exports.",
|
"themeDescription": "Light or dark interface. The canvas follows the same theme.",
|
||||||
"diagramStyle": "Diagram style",
|
"diagramStyle": "Diagram style",
|
||||||
"diagramStyleDescription": "How the AI draws new diagrams: Styled uses colors and icons, Minimal keeps shapes plain.",
|
"diagramStyleDescription": "How the AI draws new diagrams: Styled uses colors and icons, Minimal keeps shapes plain.",
|
||||||
"sendShortcut": "Send shortcut",
|
"sendShortcut": "Send shortcut",
|
||||||
@@ -400,7 +400,7 @@
|
|||||||
"searchModels": "Search models...",
|
"searchModels": "Search models...",
|
||||||
"noModelsFound": "No models found.",
|
"noModelsFound": "No models found.",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
"serverDefault": "Server Default",
|
"serverDefault": "Server model",
|
||||||
"serverModels": "Server Models",
|
"serverModels": "Server Models",
|
||||||
"userModels": "User Models",
|
"userModels": "User Models",
|
||||||
"configureModels": "Configure Models...",
|
"configureModels": "Configure Models...",
|
||||||
@@ -695,7 +695,7 @@
|
|||||||
},
|
},
|
||||||
"lobby": {
|
"lobby": {
|
||||||
"title": "What would you like to draw?",
|
"title": "What would you like to draw?",
|
||||||
"lede": "Describe a diagram, or upload a screenshot or PDF. You get a regular draw.io diagram that you can keep editing and export.",
|
"lede": "Describe a diagram or upload a screenshot or PDF. You get a draw.io diagram you can keep editing and export.",
|
||||||
"placeholder": "For example: a login sequence diagram with SMS codes and retries",
|
"placeholder": "For example: a login sequence diagram with SMS codes and retries",
|
||||||
"recent": "Recent diagrams",
|
"recent": "Recent diagrams",
|
||||||
"drawYourself": "Start with a blank canvas",
|
"drawYourself": "Start with a blank canvas",
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
"replicateDescription": "既存のフローチャートをアップロードして複製",
|
"replicateDescription": "既存のフローチャートをアップロードして複製",
|
||||||
"creativeDrawing": "自由なイラスト",
|
"creativeDrawing": "自由なイラスト",
|
||||||
"creativeDescription": "楽しくてクリエイティブなものを描く",
|
"creativeDescription": "楽しくてクリエイティブなものを描く",
|
||||||
"cachedNote": "例はすぐに開き、モデルは使いません。",
|
"cachedNote": "例は保存済みの回答を使うので、すぐに表示され、AI を呼び出しません。",
|
||||||
"mcpServer": "MCP サーバー",
|
"mcpServer": "MCP サーバー",
|
||||||
"mcpDescription": "Claude Desktop、VS Code、Cursor で使用"
|
"mcpDescription": "Claude Desktop、VS Code、Cursor で使用"
|
||||||
},
|
},
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"language": "言語",
|
"language": "言語",
|
||||||
"languageDescription": "インターフェース言語を選択します。",
|
"languageDescription": "インターフェース言語を選択します。",
|
||||||
"theme": "テーマ",
|
"theme": "テーマ",
|
||||||
"themeDescription": "画面をライトにするかダークにするか。図はエクスポートしたときと同じく、常に白い用紙の上に表示されます。",
|
"themeDescription": "画面をライトにするかダークにするか。キャンバスも同じテーマになります。",
|
||||||
"diagramStyle": "図のスタイル",
|
"diagramStyle": "図のスタイル",
|
||||||
"diagramStyleDescription": "AI が新しい図を描くときのスタイル。スタイル付きは色やアイコンを使い、ミニマルは素朴な図形だけにします。",
|
"diagramStyleDescription": "AI が新しい図を描くときのスタイル。スタイル付きは色やアイコンを使い、ミニマルは素朴な図形だけにします。",
|
||||||
"sendShortcut": "送信のショートカット",
|
"sendShortcut": "送信のショートカット",
|
||||||
@@ -355,7 +355,7 @@
|
|||||||
"searchModels": "モデルを検索...",
|
"searchModels": "モデルを検索...",
|
||||||
"noModelsFound": "モデルが見つかりません。",
|
"noModelsFound": "モデルが見つかりません。",
|
||||||
"default": "デフォルト",
|
"default": "デフォルト",
|
||||||
"serverDefault": "サーバーデフォルト",
|
"serverDefault": "サーバーのモデル",
|
||||||
"serverModels": "サーバーモデル",
|
"serverModels": "サーバーモデル",
|
||||||
"userModels": "ユーザーモデル",
|
"userModels": "ユーザーモデル",
|
||||||
"configureModels": "モデルを設定...",
|
"configureModels": "モデルを設定...",
|
||||||
@@ -695,7 +695,7 @@
|
|||||||
},
|
},
|
||||||
"lobby": {
|
"lobby": {
|
||||||
"title": "どんな図を描きますか?",
|
"title": "どんな図を描きますか?",
|
||||||
"lede": "図の内容を説明するか、スクリーンショットや PDF をアップロードしてください。できあがるのは通常の draw.io の図なので、続けて編集もエクスポートもできます。",
|
"lede": "図を説明するか、スクリーンショットや PDF をアップロードしてください。できあがるのは編集もエクスポートもできる draw.io の図です。",
|
||||||
"placeholder": "例:SMS 認証とリトライを含むログインのシーケンス図",
|
"placeholder": "例:SMS 認証とリトライを含むログインのシーケンス図",
|
||||||
"recent": "最近の図",
|
"recent": "最近の図",
|
||||||
"drawYourself": "白紙のキャンバスから始める",
|
"drawYourself": "白紙のキャンバスから始める",
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
"replicateDescription": "上傳並複製現有流程圖",
|
"replicateDescription": "上傳並複製現有流程圖",
|
||||||
"creativeDrawing": "創意繪畫",
|
"creativeDrawing": "創意繪畫",
|
||||||
"creativeDescription": "繪製有趣且富有創意的內容",
|
"creativeDescription": "繪製有趣且富有創意的內容",
|
||||||
"cachedNote": "範例會立即開啟,不呼叫模型。",
|
"cachedNote": "範例使用保存好的答案,立即顯示,不呼叫 AI。",
|
||||||
"mcpServer": "MCP 伺服器",
|
"mcpServer": "MCP 伺服器",
|
||||||
"mcpDescription": "在 Claude Desktop、VS Code 和 Cursor 中使用"
|
"mcpDescription": "在 Claude Desktop、VS Code 和 Cursor 中使用"
|
||||||
},
|
},
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"language": "語言",
|
"language": "語言",
|
||||||
"languageDescription": "選擇介面語言。",
|
"languageDescription": "選擇介面語言。",
|
||||||
"theme": "主題",
|
"theme": "主題",
|
||||||
"themeDescription": "介面用淺色還是深色。圖紙始終是白底,和匯出後的樣子一致。",
|
"themeDescription": "介面用淺色還是深色,畫布跟隨同一主題。",
|
||||||
"diagramStyle": "圖形風格",
|
"diagramStyle": "圖形風格",
|
||||||
"diagramStyleDescription": "AI 畫新圖時的風格:精緻會用顏色和圖示,簡約只用樸素的圖形。",
|
"diagramStyleDescription": "AI 畫新圖時的風格:精緻會用顏色和圖示,簡約只用樸素的圖形。",
|
||||||
"sendShortcut": "傳送快速鍵",
|
"sendShortcut": "傳送快速鍵",
|
||||||
@@ -400,7 +400,7 @@
|
|||||||
"searchModels": "搜尋模型...",
|
"searchModels": "搜尋模型...",
|
||||||
"noModelsFound": "未找到模型。",
|
"noModelsFound": "未找到模型。",
|
||||||
"default": "預設",
|
"default": "預設",
|
||||||
"serverDefault": "伺服器預設",
|
"serverDefault": "伺服器模型",
|
||||||
"serverModels": "伺服器模型",
|
"serverModels": "伺服器模型",
|
||||||
"userModels": "使用者模型",
|
"userModels": "使用者模型",
|
||||||
"configureModels": "配置模型...",
|
"configureModels": "配置模型...",
|
||||||
@@ -695,7 +695,7 @@
|
|||||||
},
|
},
|
||||||
"lobby": {
|
"lobby": {
|
||||||
"title": "說說你要畫什麼",
|
"title": "說說你要畫什麼",
|
||||||
"lede": "描述一張圖,或上傳截圖、PDF。畫出來的是標準的 draw.io 圖表,可以接著改,也可以匯出。",
|
"lede": "描述一張圖,或上傳截圖、PDF。得到的是可以繼續編輯和匯出的 draw.io 圖。",
|
||||||
"placeholder": "例如:使用者登入的時序圖,包含簡訊驗證碼和失敗重試",
|
"placeholder": "例如:使用者登入的時序圖,包含簡訊驗證碼和失敗重試",
|
||||||
"recent": "最近的圖紙",
|
"recent": "最近的圖紙",
|
||||||
"drawYourself": "從空白畫布開始",
|
"drawYourself": "從空白畫布開始",
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
"replicateDescription": "上传并复制现有流程图",
|
"replicateDescription": "上传并复制现有流程图",
|
||||||
"creativeDrawing": "创意绘画",
|
"creativeDrawing": "创意绘画",
|
||||||
"creativeDescription": "绘制有趣且富有创意的内容",
|
"creativeDescription": "绘制有趣且富有创意的内容",
|
||||||
"cachedNote": "示例会立即打开,不调用模型。",
|
"cachedNote": "示例使用保存好的答案,立即显示,不调用 AI。",
|
||||||
"mcpServer": "MCP 服务器",
|
"mcpServer": "MCP 服务器",
|
||||||
"mcpDescription": "在 Claude Desktop、VS Code 和 Cursor 中使用"
|
"mcpDescription": "在 Claude Desktop、VS Code 和 Cursor 中使用"
|
||||||
},
|
},
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
"language": "语言",
|
"language": "语言",
|
||||||
"languageDescription": "选择界面语言。",
|
"languageDescription": "选择界面语言。",
|
||||||
"theme": "主题",
|
"theme": "主题",
|
||||||
"themeDescription": "界面用浅色还是深色。图纸始终是白底,和导出后的样子一致。",
|
"themeDescription": "界面用浅色还是深色,画布跟随同一主题。",
|
||||||
"diagramStyle": "图形风格",
|
"diagramStyle": "图形风格",
|
||||||
"diagramStyleDescription": "AI 画新图时的风格:精致会用颜色和图标,简约只用朴素的图形。",
|
"diagramStyleDescription": "AI 画新图时的风格:精致会用颜色和图标,简约只用朴素的图形。",
|
||||||
"sendShortcut": "发送快捷键",
|
"sendShortcut": "发送快捷键",
|
||||||
@@ -400,7 +400,7 @@
|
|||||||
"searchModels": "搜索模型...",
|
"searchModels": "搜索模型...",
|
||||||
"noModelsFound": "未找到模型。",
|
"noModelsFound": "未找到模型。",
|
||||||
"default": "默认",
|
"default": "默认",
|
||||||
"serverDefault": "服务器默认",
|
"serverDefault": "服务器模型",
|
||||||
"serverModels": "服务器模型",
|
"serverModels": "服务器模型",
|
||||||
"userModels": "用户模型",
|
"userModels": "用户模型",
|
||||||
"configureModels": "配置模型...",
|
"configureModels": "配置模型...",
|
||||||
@@ -695,7 +695,7 @@
|
|||||||
},
|
},
|
||||||
"lobby": {
|
"lobby": {
|
||||||
"title": "说说你要画什么",
|
"title": "说说你要画什么",
|
||||||
"lede": "描述一张图,或者上传截图、PDF。画出来的是标准的 draw.io 图表,可以接着改,也可以导出。",
|
"lede": "描述一张图,或上传截图、PDF。得到的是可以继续编辑和导出的 draw.io 图。",
|
||||||
"placeholder": "比如:用户登录的时序图,包含短信验证码和失败重试",
|
"placeholder": "比如:用户登录的时序图,包含短信验证码和失败重试",
|
||||||
"recent": "最近的图纸",
|
"recent": "最近的图纸",
|
||||||
"drawYourself": "从空白画布开始",
|
"drawYourself": "从空白画布开始",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 67 KiB |
Reference in New Issue
Block a user