200bff4ecb
Build and Deploy Verso / deploy (push) Successful in 12m51s
Adds a dual-mode Typst preview: a new "Live (browser)" mode compiles and
renders Typst documents entirely in-browser using typst.ts WASM (28 MB
compiler + 1 MB renderer). The existing server-side PDF mode is preserved
and selectable via a new "Preview mode" section in the recompile dropdown,
visible only for Typst projects.
Architecture:
- Web Worker (typst-preview-worker.ts) runs the WASM compiler; queues
compile requests so only the latest compile runs after each keypress
- TypstWasmPreview component initialises the renderer on the main thread,
listens to changedAt from the compile context, debounces at 400 ms, and
renders SVG into a container div via renderToSvg
- typstPreviewMode ('wasm'|'pdf') is persisted per-project in localStorage
- isTypstProject, changedAt, typstPreviewMode, setTypstPreviewMode are
exposed through both LocalCompileContext and DetachCompileContext
- Fonts loaded from jsDelivr CDN (text subset only) on first use
- Phase 1: single-file Typst only (no #include, no images)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
import { ElementType, lazy, memo, Suspense } from 'react'
|
|
import classNames from 'classnames'
|
|
import PdfViewer from './pdf-viewer'
|
|
import { FullSizeLoadingSpinner } from '../../../shared/components/loading-spinner'
|
|
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
|
import { PdfPreviewMessages } from './pdf-preview-messages'
|
|
import CompileTimeWarningUpgradePrompt from './compile-time-warning-upgrade-prompt'
|
|
import { PdfPreviewProvider } from './pdf-preview-provider'
|
|
import PdfPreviewHybridToolbar from '@/features/pdf-preview/components/pdf-preview-hybrid-toolbar'
|
|
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
|
import PdfCodeCheckFailedBanner from '@/features/pdf-preview/components/pdf-code-check-failed-banner'
|
|
import getMeta from '@/utils/meta'
|
|
import PdfLogsViewer from '@/features/pdf-preview/components/pdf-logs-viewer'
|
|
|
|
const TypstWasmPreview = lazy(
|
|
() =>
|
|
import(
|
|
/* webpackChunkName: "typst-wasm-preview" */
|
|
'@/features/typst-preview/components/typst-wasm-preview'
|
|
)
|
|
)
|
|
|
|
function PdfPreviewPane() {
|
|
const {
|
|
pdfUrl,
|
|
pdfViewer,
|
|
darkModePdf: darkModeSetting,
|
|
activeOverallTheme,
|
|
isTypstProject,
|
|
typstPreviewMode,
|
|
} = useCompileContext()
|
|
const { compileTimeout } = getMeta('ol-compileSettings')
|
|
const isHtmlOutput = pdfUrl?.includes('output.html')
|
|
const darkModePdf =
|
|
(pdfViewer === 'pdfjs' || isHtmlOutput) &&
|
|
activeOverallTheme === 'dark' &&
|
|
darkModeSetting
|
|
|
|
const isWasmMode = isTypstProject && typstPreviewMode === 'wasm'
|
|
const classes = classNames('pdf', 'full-size', {
|
|
'pdf-empty': !pdfUrl && !isWasmMode,
|
|
'pdf-dark-mode': darkModePdf,
|
|
})
|
|
|
|
const pdfPromotions = importOverleafModules('pdfPreviewPromotions') as {
|
|
import: { default: ElementType }
|
|
path: string
|
|
}[]
|
|
|
|
return (
|
|
<div className={classes}>
|
|
<PdfPreviewProvider>
|
|
<PdfPreviewHybridToolbar />
|
|
<PdfCodeCheckFailedBanner />
|
|
<PdfPreviewMessages>
|
|
{compileTimeout < 60 && <CompileTimeWarningUpgradePrompt />}
|
|
</PdfPreviewMessages>
|
|
<Suspense fallback={<FullSizeLoadingSpinner delay={500} />}>
|
|
<div className="pdf-viewer" data-testid="pdf-viewer">
|
|
{isWasmMode ? (
|
|
<TypstWasmPreview />
|
|
) : (
|
|
<PdfViewer />
|
|
)}
|
|
</div>
|
|
</Suspense>
|
|
<PdfLogsViewer />
|
|
{pdfPromotions.map(({ import: { default: Component }, path }) => (
|
|
<Component key={path} />
|
|
))}
|
|
</PdfPreviewProvider>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default memo(PdfPreviewPane)
|