fix(typst-preview): fix recursive Rust aliasing error and stale renders
Build and Deploy Verso / deploy (push) Successful in 12m44s
Build and Deploy Verso / deploy (push) Successful in 12m44s
Two bugs fixed:
1. 'recursive use of an object' Rust error: inside runWithSession(), calling
renderer.renderToSvg({ renderSession: session }) passes the session to the
renderer while runWithSession already holds it — double-aliasing the same
Rust object. Fixed by using session.renderToSvg({ container }) directly.
2. Stale preview after edits: concurrent doRender calls (compile finishes
while previous render is still in progress) would both enter runWithSession
simultaneously, causing the Rust error and leaving the view frozen. Fixed
with a render guard (isRenderingRef) that queues the latest vectorData and
flushes it once the current render completes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,8 +39,12 @@ const TypstWasmPreview: FC = () => {
|
|||||||
const rendererRef = useRef<TypstRenderer | null>(null)
|
const rendererRef = useRef<TypstRenderer | null>(null)
|
||||||
const isReadyRef = useRef(false)
|
const isReadyRef = useRef(false)
|
||||||
const compileTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
const compileTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||||
// If 'compiled' arrives before renderer is ready, buffer the vector data
|
// If 'compiled' arrives before renderer is ready, buffer the latest vector data
|
||||||
const pendingVectorRef = useRef<Uint8Array | null>(null)
|
const pendingVectorRef = useRef<Uint8Array | null>(null)
|
||||||
|
// Prevent concurrent runWithSession calls which cause Rust aliasing errors
|
||||||
|
const isRenderingRef = useRef(false)
|
||||||
|
// Latest pending render while one is in progress
|
||||||
|
const renderQueueRef = useRef<Uint8Array | null>(null)
|
||||||
|
|
||||||
const [status, setStatus] = useState<Status>('initializing')
|
const [status, setStatus] = useState<Status>('initializing')
|
||||||
const [errorMsg, setErrorMsg] = useState('')
|
const [errorMsg, setErrorMsg] = useState('')
|
||||||
@@ -52,17 +56,42 @@ const TypstWasmPreview: FC = () => {
|
|||||||
pendingVectorRef.current = vectorData
|
pendingVectorRef.current = vectorData
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
await renderer.runWithSession(async session => {
|
// If a render is in progress, queue this data and return —
|
||||||
session.manipulateData({ action: 'reset', data: vectorData })
|
// the in-progress render will pick it up when it finishes
|
||||||
await renderer.renderToSvg({ renderSession: session, container })
|
if (isRenderingRef.current) {
|
||||||
})
|
renderQueueRef.current = vectorData
|
||||||
pendingVectorRef.current = null
|
return
|
||||||
setStatus('ready')
|
}
|
||||||
setErrorMsg('')
|
|
||||||
} catch (e) {
|
let dataToRender: Uint8Array | null = vectorData
|
||||||
setStatus('error')
|
while (dataToRender) {
|
||||||
setErrorMsg(`Render failed: ${e}`)
|
isRenderingRef.current = true
|
||||||
|
const data = dataToRender
|
||||||
|
dataToRender = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
await renderer.runWithSession(async session => {
|
||||||
|
session.manipulateData({ action: 'reset', data })
|
||||||
|
// Use session.renderToSvg (NOT renderer.renderToSvg with renderSession)
|
||||||
|
// to avoid double-aliasing the same Rust object inside runWithSession
|
||||||
|
await session.renderToSvg({ container })
|
||||||
|
})
|
||||||
|
pendingVectorRef.current = null
|
||||||
|
setStatus('ready')
|
||||||
|
setErrorMsg('')
|
||||||
|
} catch (e) {
|
||||||
|
setStatus('error')
|
||||||
|
setErrorMsg(`Render failed: ${e}`)
|
||||||
|
} finally {
|
||||||
|
isRenderingRef.current = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick up any compile result that arrived while we were rendering
|
||||||
|
if (renderQueueRef.current) {
|
||||||
|
dataToRender = renderQueueRef.current
|
||||||
|
renderQueueRef.current = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user