mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 14:22:28 +08:00
fix: Prevent DrawIO remount and data loss when resizing window across 768px breakpoint (#306)
* fix: Prevent DrawIO remount and data loss when resizing window across 768px breakpoint * fix: prevent DrawIO remount and data loss when resizing window - Move key from ResizablePanelGroup to chat-panel only - Save diagram to localStorage before breakpoint change - Restore defaultSize on drawio-panel to prevent layout flash - Keep save button functionality from main * fix: reset draw.io ready state on breakpoint change to restore diagram * fix: skip initial render save and remove console logs - Add isInitialRenderRef to skip unnecessary save/reset on first render - Remove console.log statements for production cleanliness - Add eslint-disable comment explaining loadDiagram dependency --------- Co-authored-by: dayuan.jiang <jdy.toh@gmail.com>
This commit is contained in:
@@ -35,6 +35,9 @@ const STORAGE_XML_SNAPSHOTS_KEY = "next-ai-draw-io-xml-snapshots"
|
||||
const STORAGE_SESSION_ID_KEY = "next-ai-draw-io-session-id"
|
||||
export const STORAGE_DIAGRAM_XML_KEY = "next-ai-draw-io-diagram-xml"
|
||||
|
||||
// sessionStorage keys
|
||||
const SESSION_STORAGE_INPUT_KEY = "next-ai-draw-io-input"
|
||||
|
||||
// Type for message parts (tool calls and their states)
|
||||
interface MessagePart {
|
||||
type: string
|
||||
@@ -106,7 +109,6 @@ export default function ChatPanel({
|
||||
resolverRef,
|
||||
chartXML,
|
||||
clearDiagram,
|
||||
isDrawioReady,
|
||||
} = useDiagram()
|
||||
|
||||
const onFetchChart = (saveToHistory = true) => {
|
||||
@@ -148,6 +150,14 @@ export default function ChatPanel({
|
||||
const [showNewChatDialog, setShowNewChatDialog] = useState(false)
|
||||
const [minimalStyle, setMinimalStyle] = useState(false)
|
||||
|
||||
// Restore input from sessionStorage on mount (when ChatPanel remounts due to key change)
|
||||
useEffect(() => {
|
||||
const savedInput = sessionStorage.getItem(SESSION_STORAGE_INPUT_KEY)
|
||||
if (savedInput) {
|
||||
setInput(savedInput)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Check config on mount
|
||||
useEffect(() => {
|
||||
fetch("/api/config")
|
||||
@@ -210,9 +220,6 @@ export default function ChatPanel({
|
||||
const localStorageDebounceRef = useRef<ReturnType<
|
||||
typeof setTimeout
|
||||
> | null>(null)
|
||||
const xmlStorageDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(
|
||||
null,
|
||||
)
|
||||
const LOCAL_STORAGE_DEBOUNCE_MS = 1000 // Save at most once per second
|
||||
|
||||
const {
|
||||
@@ -725,47 +732,6 @@ Continue from EXACTLY where you stopped.`,
|
||||
}
|
||||
}, [setMessages])
|
||||
|
||||
// Restore diagram XML when DrawIO becomes ready
|
||||
const hasDiagramRestoredRef = useRef(false)
|
||||
const [canSaveDiagram, setCanSaveDiagram] = useState(false)
|
||||
useEffect(() => {
|
||||
// Reset restore flag when DrawIO is not ready (e.g., theme/UI change remounts it)
|
||||
if (!isDrawioReady) {
|
||||
hasDiagramRestoredRef.current = false
|
||||
setCanSaveDiagram(false)
|
||||
return
|
||||
}
|
||||
if (hasDiagramRestoredRef.current) return
|
||||
hasDiagramRestoredRef.current = true
|
||||
|
||||
try {
|
||||
const savedDiagramXml = localStorage.getItem(
|
||||
STORAGE_DIAGRAM_XML_KEY,
|
||||
)
|
||||
console.log(
|
||||
"[ChatPanel] Restoring diagram, has saved XML:",
|
||||
!!savedDiagramXml,
|
||||
)
|
||||
if (savedDiagramXml) {
|
||||
console.log(
|
||||
"[ChatPanel] Loading saved diagram XML, length:",
|
||||
savedDiagramXml.length,
|
||||
)
|
||||
// Skip validation for trusted saved diagrams
|
||||
onDisplayChart(savedDiagramXml, true)
|
||||
chartXMLRef.current = savedDiagramXml
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to restore diagram from localStorage:", error)
|
||||
}
|
||||
|
||||
// Allow saving after restore is complete
|
||||
setTimeout(() => {
|
||||
console.log("[ChatPanel] Enabling diagram save")
|
||||
setCanSaveDiagram(true)
|
||||
}, 500)
|
||||
}, [isDrawioReady, onDisplayChart])
|
||||
|
||||
// Save messages to localStorage whenever they change (debounced to prevent blocking during streaming)
|
||||
useEffect(() => {
|
||||
if (!hasRestoredRef.current) return
|
||||
@@ -795,28 +761,6 @@ Continue from EXACTLY where you stopped.`,
|
||||
}
|
||||
}, [messages])
|
||||
|
||||
// Save diagram XML to localStorage whenever it changes (debounced)
|
||||
useEffect(() => {
|
||||
if (!canSaveDiagram) return
|
||||
if (!chartXML || chartXML.length <= 300) return
|
||||
|
||||
// Clear any pending save
|
||||
if (xmlStorageDebounceRef.current) {
|
||||
clearTimeout(xmlStorageDebounceRef.current)
|
||||
}
|
||||
|
||||
// Debounce: save after 1 second of no changes
|
||||
xmlStorageDebounceRef.current = setTimeout(() => {
|
||||
localStorage.setItem(STORAGE_DIAGRAM_XML_KEY, chartXML)
|
||||
}, LOCAL_STORAGE_DEBOUNCE_MS)
|
||||
|
||||
return () => {
|
||||
if (xmlStorageDebounceRef.current) {
|
||||
clearTimeout(xmlStorageDebounceRef.current)
|
||||
}
|
||||
}
|
||||
}, [chartXML, canSaveDiagram])
|
||||
|
||||
// Save XML snapshots to localStorage whenever they change
|
||||
const saveXmlSnapshots = useCallback(() => {
|
||||
try {
|
||||
@@ -916,6 +860,7 @@ Continue from EXACTLY where you stopped.`,
|
||||
},
|
||||
] as any)
|
||||
setInput("")
|
||||
sessionStorage.removeItem(SESSION_STORAGE_INPUT_KEY)
|
||||
setFiles([])
|
||||
return
|
||||
}
|
||||
@@ -963,6 +908,7 @@ Continue from EXACTLY where you stopped.`,
|
||||
|
||||
// Token count is tracked in onFinish with actual server usage
|
||||
setInput("")
|
||||
sessionStorage.removeItem(SESSION_STORAGE_INPUT_KEY)
|
||||
setFiles([])
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error)
|
||||
@@ -985,6 +931,7 @@ Continue from EXACTLY where you stopped.`,
|
||||
localStorage.removeItem(STORAGE_XML_SNAPSHOTS_KEY)
|
||||
localStorage.removeItem(STORAGE_DIAGRAM_XML_KEY)
|
||||
localStorage.setItem(STORAGE_SESSION_ID_KEY, newSessionId)
|
||||
sessionStorage.removeItem(SESSION_STORAGE_INPUT_KEY)
|
||||
toast.success("Started a fresh chat")
|
||||
} catch (error) {
|
||||
console.error("Failed to clear localStorage:", error)
|
||||
@@ -999,9 +946,14 @@ Continue from EXACTLY where you stopped.`,
|
||||
const handleInputChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
saveInputToSessionStorage(e.target.value)
|
||||
setInput(e.target.value)
|
||||
}
|
||||
|
||||
const saveInputToSessionStorage = (input: string) => {
|
||||
sessionStorage.setItem(SESSION_STORAGE_INPUT_KEY, input)
|
||||
}
|
||||
|
||||
// Helper functions for message actions (regenerate/edit)
|
||||
// Extract previous XML snapshot before a given message index
|
||||
const getPreviousXml = (beforeIndex: number): string => {
|
||||
|
||||
Reference in New Issue
Block a user