import { useCallback , useEffect , useState } from "react" ; import { exportToSvg , restoreElements , loadLibraryFromBlob , } from "@excalidraw/excalidraw" ; import type { BinaryFiles } from "@excalidraw/excalidraw/types/types" ; import type { ExcalidrawElement , NonDeletedExcalidrawElement , } from "@excalidraw/excalidraw/types/element/types" ; import { loadScene } from "./vendor/loadScene" ; import { animateSvg } from "./animate" ; export const getNonDeletedElements = ( elements ) : NonDeletedExcalidrawElement [ ] => elements . filter ( ( element ) : element is NonDeletedExcalidrawElement => ! element . isDeleted ) ; const importLibraryFromUrl = async ( url ) => { try { const request = await fetch ( url ) ; const blob = await request . blob ( ) ; const libraryFile = await loadLibraryFromBlob ( blob ) ; if ( ! libraryFile || ! libraryFile . libraryItems ) { throw new Error ( ) ; } return libraryFile . libraryItems . map ( ( libraryItem ) => getNonDeletedElements ( restoreElements ( libraryItem . elements , null ) ) ) ; } catch ( error ) { window . alert ( "Unable to load library" ) ; return [ ] ; } } ; export const useLoadSvg = ( ) => { const [ loading , setLoading ] = useState ( true ) ; const [ loadedSvgList , setLoadedSvgList ] = useState < { svg : SVGSVGElement ; finishedMs : number ; } [ ] > ( [ ] ) ; const loadDataList = useCallback ( async ( dataList , inSequence ? ) => { const hash = window . location . hash . slice ( 1 ) ; const searchParams = new URLSearchParams ( hash ) ; const options = { startMs : undefined as number | undefined , pointerImg : searchParams . get ( "pointerImg" ) || undefined , pointerWidth : searchParams . get ( "pointerWidth" ) || undefined , pointerHeight : searchParams . get ( "pointerHeight" ) || undefined , } ; const svgList = await Promise . all ( dataList . map ( async ( data ) => { const elements = getNonDeletedElements ( data . elements ) ; const svg = await exportToSvg ( { elements , files : data . files , appState : { exportBackground : true , viewBackgroundColor : "white" , } , exportPadding : 30 , } ) ; const result = animateSvg ( svg , elements , options ) ; console . log ( svg ) ; if ( inSequence ) { options . startMs = result . finishedMs ; } return { svg , finishedMs : result . finishedMs } ; } ) ) ; setLoadedSvgList ( svgList ) ; return svgList ; } , [ ] ) ; useEffect ( ( ) => { ( async ( ) => { const hash = window . location . hash . slice ( 1 ) ; const searchParams = new URLSearchParams ( hash ) ; const matchIdKey = /([0-9]+),?([a-zA-Z0-9_-]*)/ . exec ( searchParams . get ( "json" ) || "" ) ; if ( matchIdKey ) { const [ , id , key ] = matchIdKey ; const data = await loadScene ( id , key , null ) ; const [ { svg , finishedMs } ] = await loadDataList ( [ data ] ) ; if ( searchParams . get ( "autoplay" ) === "no" ) { svg . setCurrentTime ( finishedMs ) ; } } const matchLibrary = /(.*\.excalidrawlib)/ . exec ( searchParams . get ( "library" ) || "" ) ; if ( matchLibrary ) { const [ , url ] = matchLibrary ; const dataList = await importLibraryFromUrl ( url ) ; const svgList = await loadDataList ( dataList . map ( ( elements ) => ( { elements , files : { } } ) ) , searchParams . has ( "sequence" ) ) ; if ( searchParams . get ( "autoplay" ) === "no" ) { svgList . forEach ( ( { svg , finishedMs } ) => { svg . setCurrentTime ( finishedMs ) ; } ) ; } } setLoading ( false ) ; } ) ( ) ; } , [ loadDataList ] ) ; return { loading , loadedSvgList , loadDataList } ; } ;