mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-09 19:19:50 +08:00
feat(ui): canvas-first redesign with a self-hosted draw.io (#957)
The canvas now fills the window and the chat floats on its right. draw.io is served from our own origin (public/drawio, downloaded at build time and checked against the release's SHA-256), so the app can drive the editor directly: - every AI change becomes a version card with a thumbnail; compare, restore or undo it, and Ctrl+Z on the canvas takes an AI change back in one step - shapes the AI just changed are highlighted on the canvas - selected shapes appear as a chip and are sent to the model - draw.io's own simple UI: its toolbar, menus (auto layout, Mermaid, CSV import) and page tabs; the shape library starts closed - new start screen, session rename and switcher, open .drawio files - settings split into Models & keys, Appearance, Advanced and About - Bedrock in the model settings also takes an API key instead of access keys - phones switch between canvas and chat - dark mode uses draw.io's own dark mode, with neutral greys around it With an external draw.io (NEXT_PUBLIC_DRAWIO_BASE_URL) the features above that need same origin are off. The chat logic moved from the 1500-line chat panel into a chat engine plus small UI components; zustand holds canvas, versions, settings and UI state.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import type { ChangeSummary } from "@/lib/diagram-diff"
|
||||
import { formatMessage } from "@/lib/i18n/utils"
|
||||
|
||||
type VersionDict = Record<string, string>
|
||||
|
||||
const PARTS: [keyof ChangeSummary, string][] = [
|
||||
["shapesAdded", "shapesAdded"],
|
||||
["edgesAdded", "edgesAdded"],
|
||||
["shapesChanged", "shapesChanged"],
|
||||
["edgesChanged", "edgesChanged"],
|
||||
["shapesRemoved", "shapesRemoved"],
|
||||
["edgesRemoved", "edgesRemoved"],
|
||||
]
|
||||
|
||||
/**
|
||||
* "Added 2 shapes, changed 1 connector". Each dictionary entry has a
|
||||
* singular (...One) and plural (...Other) form; languages without plurals
|
||||
* use the same text for both.
|
||||
*/
|
||||
export function describeChanges(
|
||||
summary: ChangeSummary,
|
||||
t: VersionDict,
|
||||
): string {
|
||||
const pieces = PARTS.filter(([field]) => summary[field] > 0).map(
|
||||
([field, key]) => {
|
||||
const count = summary[field]
|
||||
const template = count === 1 ? t[`${key}One`] : t[`${key}Other`]
|
||||
return formatMessage(template ?? "", { count })
|
||||
},
|
||||
)
|
||||
if (pieces.length === 0) return t.noChanges ?? ""
|
||||
const text = pieces.join(t.listSeparator ?? ", ")
|
||||
// Sentence case for scripts with letter case
|
||||
return text.charAt(0).toUpperCase() + text.slice(1)
|
||||
}
|
||||
|
||||
/** "3 shapes, 1 connector": what a diagram drawn from scratch holds */
|
||||
export function describeTotals(summary: ChangeSummary, t: VersionDict): string {
|
||||
const count = (n: number, key: string) =>
|
||||
formatMessage((n === 1 ? t[`${key}One`] : t[`${key}Other`]) ?? "", {
|
||||
count: n,
|
||||
})
|
||||
return formatMessage(t.totals ?? "", {
|
||||
shapes: count(summary.shapesAdded, "shapesTotal"),
|
||||
edges: count(summary.edgesAdded, "edgesTotal"),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user