import { getAssetUrl } from "@/lib/base-path" import type { Locale } from "@/lib/i18n/config" /** External draw.io configured at build time (cross-origin, limited features) */ export const EXTERNAL_DRAWIO_URL = process.env.NEXT_PUBLIC_DRAWIO_BASE_URL || "" /** * URL of the draw.io editor iframe. By default this is the copy bundled in * public/drawio, served from our own origin. */ export function getDrawioSrc({ lang, isElectron, }: { lang: Locale isElectron: boolean }): string { // react-drawio used `new URL(baseUrl)`, so keep an absolute URL, and // include index.html because Next.js does not serve directory indexes const base = EXTERNAL_DRAWIO_URL || `${window.location.origin}${getAssetUrl("/drawio/index.html")}` const url = new URL(base) const params: Record = { embed: "1", proto: "json", configure: "1", ui: "simple", spin: "0", libraries: "0", noSaveBtn: "1", noExitBtn: "1", saveAndExit: "0", // The diagram always renders on a light sheet; dark mode only // recolors the table around it dark: "0", // draw.io names Traditional Chinese "zh-tw" lang: lang === "zh-Hant" ? "zh-tw" : lang, } // No calls to external services from the desktop app if (isElectron && !EXTERNAL_DRAWIO_URL) params.offline = "1" for (const [key, value] of Object.entries(params)) { url.searchParams.set(key, value) } return url.toString() } /** * Show the page (a white sheet with a grid) unless the diagram says * otherwise. AI output usually has a bare , and draw.io's * simple UI would then show neither page nor grid. */ export function withPageDefaults(xml: string): string { return xml.replace( /]*?)(\/?)>/g, (_match, attrs, slash) => { let next = attrs as string if (!/\spage\s*=/.test(next)) next += ' page="1"' if (!/\sgrid\s*=/.test(next)) next += ' grid="1"' if (!/\sgridSize\s*=/.test(next)) next += ' gridSize="10"' return `` }, ) } /** Empty document loaded when draw.io starts */ export const EMPTY_DRAWIO_DOCUMENT = withPageDefaults( '', ) /** * Editor defaults sent with draw.io's configure message. The simple UI turns * page view off for new diagrams; keep the white page on the canvas, as in * the classic UI. */ export const DRAWIO_CONFIG = { defaultPageVisible: true, defaultGridEnabled: true, } /** * CSS injected into draw.io. `customChrome` hides draw.io's own toolbar and * page tabs because the app draws its own over the canvas. */ export function getDrawioCss({ dark, customChrome, }: { dark: boolean customChrome: boolean }): string { const workspace = dark ? "#0f2440" : "#f4f5f7" // draw.io itself stays light, so its focus color does too const accent = "#14181d" let css = ` :root { --workspace-color: ${workspace}; --focus-color: ${accent}; } .geDiagramContainer { background-color: ${workspace}; } .geBackgroundPage { box-shadow: 0 1px 2px rgba(20,24,29,.08), 0 12px 32px -12px rgba(20,24,29,${dark ? ".5" : ".18"}) !important; } ` if (customChrome) { css += ` .geSimpleMainMenu, .geTabContainer, .geFooterContainer, .geStatus, .geToolbarContainer:not(.geFormatContainer *), .geHsplit, .geSidebarContainer:not(.geFormatContainer) { display: none !important; } .geDiagramContainer { top: 0 !important; bottom: 0 !important; left: 0 !important; border: 0 !important; } .geFormatContainer { top: 64px !important; bottom: 64px !important; border-radius: 14px 0 0 14px; box-shadow: 0 10px 30px -8px rgba(20,24,29,.2); border: 0 !important; } body.nai-shapes-open .geSidebarContainer:not(.geFormatContainer) { display: block !important; top: 64px !important; bottom: 64px !important; left: 12px !important; width: 248px !important; transform: none !important; border-radius: 14px; box-shadow: 0 10px 30px -8px rgba(20,24,29,.2); border: 0 !important; z-index: 3; } ` } else { css += ` .geSimpleMainMenu { border-bottom: 1px solid #e4e7eb !important; box-shadow: none !important; } ` } return css }