Build and Deploy Verso / deploy (push) Successful in 9m50s
- Add "Convert to Typst (.typ)" in the file tree context menu for .tex docs, and "Convert to LaTeX (.tex)" for .typ docs. Clicking runs pandoc on the file content and creates the converted file in the same folder. - New backend endpoint POST /project/:id/doc/:id/convert/:type that reads the doc from document-updater, runs pandoc directly, and creates the result via ProjectEntityUpdateHandler (file tree updates via socket). - Rewrite the export success toast for typst and latex conversions: no more link to /contact, replaced with a plain warning that errors are expected (pandoc does not support all constructs). - Add i18n keys: convert_to_typst, convert_to_latex, typst_export_feedback_message, latex_export_feedback_message (EN + FR) and all four to extracted-translations.json. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
import { FetchError, getJSON } from '@/infrastructure/fetch-json'
|
|
import { useLocation } from '@/shared/hooks/use-location'
|
|
import { debugConsole } from '@/utils/debugging'
|
|
import { useProjectContext } from '@/shared/context/project-context'
|
|
import { useCallback } from 'react'
|
|
import {
|
|
hideExportDocumentError,
|
|
hidePreparingExportToast,
|
|
showExportDocumentError,
|
|
showExportDocumentSuccess,
|
|
showPreparingExportToast,
|
|
} from '../components/toolbar/export-document-toasts'
|
|
import { RootDocInfo } from '@/shared/hooks/use-root-doc'
|
|
import { OpenDocuments } from '../editor/open-documents'
|
|
|
|
const SLOW_CONVERSION_THRESHOLD = 2000
|
|
|
|
export default function useConvertProject(
|
|
type: 'docx' | 'markdown' | 'html' | 'typst' | 'latex',
|
|
openDocs: OpenDocuments,
|
|
getRootDocInfo: () => RootDocInfo
|
|
) {
|
|
const { projectId } = useProjectContext()
|
|
const location = useLocation()
|
|
return useCallback(async () => {
|
|
let handle: string | undefined
|
|
const toastTimer = setTimeout(() => {
|
|
handle = showPreparingExportToast()
|
|
}, SLOW_CONVERSION_THRESHOLD)
|
|
const hidePreparingToast = () => {
|
|
clearTimeout(toastTimer)
|
|
if (handle) hidePreparingExportToast(handle)
|
|
}
|
|
const url = new URL(window.location.origin)
|
|
url.pathname = `/project/${projectId}/download/conversion/${type}`
|
|
url.searchParams.set('responseFormat', 'json')
|
|
const { rootResourcePath } = getRootDocInfo()
|
|
url.searchParams.set('rootResourcePath', rootResourcePath)
|
|
hideExportDocumentError()
|
|
try {
|
|
await openDocs.awaitBufferedOps(AbortSignal.timeout(10_000))
|
|
const response = await getJSON(url.href)
|
|
hidePreparingToast()
|
|
const { downloadUrl } = response
|
|
if (downloadUrl) {
|
|
const url = new URL(downloadUrl, window.location.origin)
|
|
location.assign(url.toString())
|
|
showExportDocumentSuccess(type as 'docx' | 'markdown' | 'html' | 'typst' | 'latex')
|
|
} else {
|
|
showExportDocumentError()
|
|
}
|
|
} catch (error) {
|
|
hidePreparingToast()
|
|
let errorMessage
|
|
if (
|
|
error instanceof FetchError &&
|
|
error.response?.status === 422 &&
|
|
error.data?.error
|
|
) {
|
|
errorMessage = error.data.error
|
|
}
|
|
showExportDocumentError(errorMessage)
|
|
debugConsole.error(error)
|
|
}
|
|
}, [projectId, type, getRootDocInfo, openDocs, location])
|
|
}
|