{"record":{"id":"090b26363b02dad8","repo":"AykutSarac/jsoncrack.com","slug":"canvas-not-found","errorCode":null,"errorMessage":"Canvas not found.","messagePattern":"Canvas not found\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/www/src/features/modals/DownloadModal/index.tsx","lineNumber":83,"sourceCode":"const getExportElement = () =>\n  (document.querySelector(\".jsoncrack-canvas\") as HTMLElement | null) ??\n  (document.querySelector(\"svg[id*='ref']\") as HTMLElement | null);\n\nexport const DownloadModal = ({ opened, onClose }: ModalProps) => {\n  const [extension, setExtension] = React.useState(Extensions.PNG);\n  const [fileDetails, setFileDetails] = React.useState({\n    filename: \"jsoncrack.com\",\n    backgroundColor: \"#FFFFFF\",\n    quality: 1,\n  });\n\n  const clipboardImage = async () => {\n    try {\n      toast.loading(\"Copying to clipboard...\", { id: \"toastClipboard\" });\n\n      const imageElement = getExportElement();\n      if (!imageElement) {\n        toast.error(\"Canvas not found.\");\n        return;\n      }\n      const imageOptions = {\n        quality: fileDetails.quality,\n        backgroundColor: fileDetails.backgroundColor,\n        skipFonts: true,\n      };\n\n      const blob = await toBlob(imageElement, imageOptions);\n\n      if (!blob) return;\n\n      await navigator.clipboard?.write([\n        new ClipboardItem({\n          [blob.type]: blob,\n        }),\n      ]);\n","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/AykutSarac/jsoncrack.com/blob/3c9af69e23c635356293b6b28cf4cd0af10d1059/apps/www/src/features/modals/DownloadModal/index.tsx#L65-L101","documentation":"Toast from clipboardImage() when getExportElement() returns null. getExportElement queries the DOM for `.jsoncrack-canvas` first, falling back to `svg[id*='ref']`. If neither exists (the graph canvas was not rendered, was unmounted, or uses a different selector in an integration), the clipboard copy aborts.","triggerScenarios":"Opening the Download modal before the graph has rendered; the canvas unmounted due to an error/empty input; embedding JSONCrack with a custom className that replaced `.jsoncrack-canvas`; an integration that does not render the reaflow Canvas.","commonSituations":"Clicking 'Clipboard' immediately on modal open before layout completes; calling export on an empty/above-limit graph (no canvas rendered); DOM stripped by an error boundary.","solutions":["Ensure the graph has rendered before enabling the Clipboard button (gate on a non-empty canvas).","Confirm the canvas wrapper retains the `jsoncrack-canvas` class.","Wait for layout/onLayoutChange before allowing export.","If integrating, render the component so that `.jsoncrack-canvas` or an `svg[id*='ref']` exists in the DOM."],"exampleFix":"// before\nconst imageElement = getExportElement();\nif (!imageElement) {\n  toast.error(\"Canvas not found.\");\n  return;\n}\n\n// after — retry once after a frame to tolerate not-yet-painted canvas\nconst waitFrame = () => new Promise(r => requestAnimationFrame(() => r(null)));\nlet imageElement = getExportElement();\nif (!imageElement) { await waitFrame(); imageElement = getExportElement(); }\nif (!imageElement) {\n  toast.error(\"Canvas not found. Wait for the graph to render first.\");\n  return;\n}","handlingStrategy":"type-guard","validationCode":"// Ensure the export target exists in the DOM before enabling Clipboard\nexport function canvasReady(): boolean {\n  return !!(document.querySelector(\".jsoncrack-canvas\") || document.querySelector(\"svg[id*='ref']\"));\n}","typeGuard":"// Treat getExportElement's result as a present HTMLElement\nfunction isExportElement(el: Element | null): el is HTMLElement {\n  return el instanceof HTMLElement;\n}","tryCatchPattern":"// Guard, then act; offer a user-actionable hint\nconst el = getExportElement();\nif (!el) {\n  toast.error(\"Canvas not found. Wait for the graph to render.\");\n  return;\n}","preventionTips":["Gate the Clipboard button on layout readiness.","Keep the `.jsoncrack-canvas` class on the canvas wrapper.","Wait for onLayoutChange before allowing export."],"tags":["dom","clipboard","export","html-to-image","modal"],"backgroundTag":null,"analyzedSha":"3c9af69e23c635356293b6b28cf4cd0af10d1059","analyzedAt":"2026-08-12T19:00:27.891Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}