diff --git a/app/[lang]/layout.tsx b/app/[lang]/layout.tsx index bc2fd112..2e09e6b9 100644 --- a/app/[lang]/layout.tsx +++ b/app/[lang]/layout.tsx @@ -3,6 +3,7 @@ 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" @@ -184,7 +185,9 @@ export default async function RootLayout({ className={`${instrumentSans.variable} ${jetbrainsMono.variable} antialiased`} > - {children} + + {children} + diff --git a/components/canvas/canvas-stage.tsx b/components/canvas/canvas-stage.tsx index 2cb1617e..c7f52fd1 100644 --- a/components/canvas/canvas-stage.tsx +++ b/components/canvas/canvas-stage.tsx @@ -1,8 +1,6 @@ -import { useParams } from "next/navigation" import { useEffect, useRef, useState } from "react" import { DrawioFrame } from "@/components/canvas/drawio-frame" -import { SelectionAsk } from "@/components/canvas/selection-ask" -import { useChatEngine } from "@/components/chat/chat-engine" +import { useLocale } from "@/components/canvas/locale-context" import { useDiagram } from "@/contexts/diagram-context" import { useDictionary } from "@/hooks/use-dictionary" import { DRAWIO_CSS, getDrawioSrc } from "@/lib/drawio/drawio-config" @@ -13,28 +11,38 @@ import { cn } from "@/lib/utils" import { useCanvasStore } from "@/stores/canvas-store" import { useSettingsStore } from "@/stores/settings-store" +/** Size of the canvas area, for overlays that stay inside it */ +export interface CanvasSize { + width: number + height: number +} + /** * The draw.io canvas, with draw.io's own toolbar, menus and page tabs, and - * the "ask AI" button laid over it (only when the page can drive draw.io - * directly, not with an external cross-origin draw.io). + * an overlay laid over it (the web app's "ask AI" button; only when the + * page can drive draw.io directly, not with an external cross-origin + * draw.io). */ export function CanvasStage({ className, style, hideOverlays = false, + overlay, + waitForSavedLocale = false, }: { className?: string style?: React.CSSProperties - /** Hide the "ask AI" button (e.g. while the start screen covers the canvas) */ + /** Hide the overlay (e.g. while the start screen covers the canvas) */ hideOverlays?: boolean + /** Rendered over the canvas once draw.io is ready */ + overlay?: (size: CanvasSize) => React.ReactNode + /** Web app: the page is about to switch to the language picked last + * time (useSavedLocale in the workspace); do not load draw.io in the + * URL's language meanwhile */ + waitForSavedLocale?: boolean }) { const dict = useDictionary() - const params = useParams<{ lang: string }>() - const lang = ( - i18n.locales.includes(params.lang as Locale) - ? params.lang - : i18n.defaultLocale - ) as Locale + const lang = useLocale() const { drawioRef, handleDiagramAutoSave, @@ -43,7 +51,6 @@ export function CanvasStage({ resetDrawioReady, isDrawioReady, } = useDiagram() - const engine = useChatEngine() const isDark = useSettingsStore((s) => s.isDark) const hasEditor = useCanvasStore((s) => s.hasEditor) // Canvas width when the chat panel starts sliding in or out @@ -53,14 +60,19 @@ export function CanvasStage({ // null until draw.io tells us whether we can drive it directly const [editorAccess, setEditorAccess] = useState(null) const stageRef = useRef(null) - const [size, setSize] = useState({ width: 0, height: 0 }) + const [size, setSize] = useState({ width: 0, height: 0 }) useEffect(() => { - // The page is about to switch to the language picked last time - // (useSavedLocale): load draw.io once, in that language - const saved = localStorage.getItem(STORAGE_KEYS.locale) - if (saved && saved !== lang && i18n.locales.includes(saved as Locale)) { - return + // Load draw.io once, in the language the page will have + if (waitForSavedLocale) { + const saved = localStorage.getItem(STORAGE_KEYS.locale) + if ( + saved && + saved !== lang && + i18n.locales.includes(saved as Locale) + ) { + return + } } const isElectron = !!(window as { electronAPI?: unknown }).electronAPI // The theme class is set before the page renders; the settings store @@ -159,13 +171,7 @@ export function CanvasStage({ )} - {showOverlays && ( -