Files
overleaf-cep/services/web/frontend/js/shared/context/detach-compile-context.tsx
T
Jakob AckermannandBrian Gough 69ccb3c316 [clsi] integrate png2pdf (#34796)
* Add png to pdf converter for optimising image loading in pdftex

* refactor

* move fixtures

* refactor and add unit tests

* Remove gamma correction from PNG processing and update related tests

* Add crc32fast dependency and enhance PNG processing tests

* Add fixture test cases for PNG processing

* fix alpha channel to match pdftex

pdftex always uses an 8 bit alpha channel in the output even for 16 bit images

* Refactor PNG processing to use Option for alpha stream and update related logic

* Enhance PDF writing by adding DecodeParms for alpha stream and optimize PNG encoding process

* Add pixel dimensions support in PNG processing and PDF writing

* [copybara] move clsi excludes

* [clsi] move gitignore rules

* [png2pdf] move into dockerfiles folder

* [png2pdf] use atomic writes

* [clsi] integrate png2pdf

* [saas-e2e] add coverage for png2pdf

* [png2pdf] make compatible with seccomp profile in clsi

* [copybara] remove exclude following move of png2pdf

* [copybara] remove spurious excludes

* fix broken detection of malformed chunk headers

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* [project-history] add build script flag for depending on doc-updater types

* [png2pdf] run tests as part of clsi pipeline

* [png2pdf] fix calculation of PDF page size

* [web] put png2pdf behind feature flag and make it opt-in

* [web] fix ClsiManager.promises.sendRequest invocation after merge

* extend is_copy_safe function to follow pdftex

see the conditions in pdftex's writepng.c
https://svn.tug.org:8369/texlive/trunk/Build/source/texk/web2c/pdftexdir/writepng.c?revision=78081&view=markup#l575

the criteria are not an exact match because the png crate does not support all the
png chunk types
https://github.com/image-rs/image-png/issues/116

if the criteria result in a png being converted or skipped the final result should be the same.

in one case the conversion will be done by pdftex (at runtime)
in the other case the conversion will be done by png2pdf -

both conversions should give the same result.

* include tests in png2pdf Dockerfile

* [clsi] explicitly disable png2pdf conversion for project conversion

* [clsi] png2pdf: count converted files

* [png2pdf] bump date in docker image tag

* [web] make draft-setting option wider when png2pdf is available

---------

Co-authored-by: Brian Gough <brian.gough@overleaf.com>
GitOrigin-RevId: d0d952ee009bf93c72bba2ffe763ae0160d396e4
2026-09-17 11:17:28 +00:00

553 lines
13 KiB
TypeScript

import { createContext, FC, useContext, useMemo } from 'react'
import { CompileContext, useLocalCompileContext } from './local-compile-context'
import useDetachStateWatcher from '../hooks/use-detach-state-watcher'
import useDetachAction from '../hooks/use-detach-action'
import useCompileTriggers from '../../features/pdf-preview/hooks/use-compile-triggers'
import { useLogEvents } from '@/features/pdf-preview/hooks/use-log-events'
export const DetachCompileContext = createContext<CompileContext | undefined>(
undefined
)
export const DetachCompileProvider: FC<React.PropsWithChildren> = ({
children,
}) => {
const localCompileContext = useLocalCompileContext()
if (!localCompileContext) {
throw new Error(
'DetachCompileProvider is only available inside LocalCompileProvider'
)
}
const {
animateCompileDropdownArrow: _animateCompileDropdownArrow,
autoCompile: _autoCompile,
clearingCache: _clearingCache,
clsiServerId: _clsiServerId,
codeCheckFailed: _codeCheckFailed,
compiling: _compiling,
deliveryLatencies: _deliveryLatencies,
draft: _draft,
png2pdf: _png2pdf,
editedSinceCompileStarted: _editedSinceCompileStarted,
error: _error,
fileList: _fileList,
hasChanges: _hasChanges,
hasShortCompileTimeout: _hasShortCompileTimeout,
highlights: _highlights,
isProjectOwner: _isProjectOwner,
lastCompileOptions: _lastCompileOptions,
logEntries: _logEntries,
logEntryAnnotations: _logEntryAnnotations,
pdfFile: _pdfFile,
pdfViewer: _pdfViewer,
position: _position,
rawLog: _rawLog,
setAnimateCompileDropdownArrow: _setAnimateCompileDropdownArrow,
setAutoCompile: _setAutoCompile,
setDraft: _setDraft,
setPng2pdf: _setPng2pdf,
setError: _setError,
setHasLintingError: _setHasLintingError,
setHighlights: _setHighlights,
setPosition: _setPosition,
setShowCompileTimeWarning: _setShowCompileTimeWarning,
setShowLogs: _setShowLogs,
toggleLogs: _toggleLogs,
setStopOnFirstError: _setStopOnFirstError,
setStopOnValidationError: _setStopOnValidationError,
showLogs: _showLogs,
showCompileTimeWarning: _showCompileTimeWarning,
stopOnFirstError: _stopOnFirstError,
stopOnValidationError: _stopOnValidationError,
stoppedOnFirstError: _stoppedOnFirstError,
uncompiled: _uncompiled,
validationIssues: _validationIssues,
firstRenderDone: _firstRenderDone,
cleanupCompileResult: _cleanupCompileResult,
recompileFromScratch: _recompileFromScratch,
setCompiling: _setCompiling,
startCompile: _startCompile,
stopCompile: _stopCompile,
setChangedAt: _setChangedAt,
clearCache: _clearCache,
syncToEntry: _syncToEntry,
recordAction: _recordAction,
darkModePdf: _darkModePdf,
setDarkModePdf: _setDarkModePdf,
activeOverallTheme: _activeOverallTheme,
} = localCompileContext
const [animateCompileDropdownArrow] = useDetachStateWatcher(
'animateCompileDropdownArrow',
_animateCompileDropdownArrow,
'detacher',
'detached'
)
const [autoCompile] = useDetachStateWatcher(
'autoCompile',
_autoCompile,
'detacher',
'detached'
)
const [clearingCache] = useDetachStateWatcher(
'clearingCache',
_clearingCache,
'detacher',
'detached'
)
const [clsiServerId] = useDetachStateWatcher(
'clsiServerId',
_clsiServerId,
'detacher',
'detached'
)
const [codeCheckFailed] = useDetachStateWatcher(
'codeCheckFailed',
_codeCheckFailed,
'detacher',
'detached'
)
const [compiling] = useDetachStateWatcher(
'compiling',
_compiling,
'detacher',
'detached'
)
const [deliveryLatencies] = useDetachStateWatcher(
'deliveryLatencies',
_deliveryLatencies,
'detacher',
'detached'
)
const [draft] = useDetachStateWatcher('draft', _draft, 'detacher', 'detached')
const [png2pdf] = useDetachStateWatcher(
'png2pdf',
_png2pdf,
'detacher',
'detached'
)
const [error] = useDetachStateWatcher('error', _error, 'detacher', 'detached')
const [fileList] = useDetachStateWatcher(
'fileList',
_fileList,
'detacher',
'detached'
)
const [hasChanges] = useDetachStateWatcher(
'hasChanges',
_hasChanges,
'detacher',
'detached'
)
const [hasShortCompileTimeout] = useDetachStateWatcher(
'hasShortCompileTimeout',
_hasShortCompileTimeout,
'detacher',
'detached'
)
const [highlights] = useDetachStateWatcher(
'highlights',
_highlights,
'detacher',
'detached'
)
const [isProjectOwner] = useDetachStateWatcher(
'isProjectOwner',
_isProjectOwner,
'detacher',
'detached'
)
const [lastCompileOptions] = useDetachStateWatcher(
'lastCompileOptions',
_lastCompileOptions,
'detacher',
'detached'
)
const [logEntries] = useDetachStateWatcher(
'logEntries',
_logEntries,
'detacher',
'detached'
)
const [logEntryAnnotations] = useDetachStateWatcher(
'logEntryAnnotations',
_logEntryAnnotations,
'detacher',
'detached'
)
const [pdfFile] = useDetachStateWatcher(
'pdfFile',
_pdfFile,
'detacher',
'detached'
)
const [pdfViewer] = useDetachStateWatcher(
'pdfViewer',
_pdfViewer,
'detacher',
'detached'
)
const [position] = useDetachStateWatcher(
'position',
_position,
'detacher',
'detached'
)
const [rawLog] = useDetachStateWatcher(
'rawLog',
_rawLog,
'detacher',
'detached'
)
const [showCompileTimeWarning] = useDetachStateWatcher(
'showCompileTimeWarning',
_showCompileTimeWarning,
'detacher',
'detached'
)
const [showLogs] = useDetachStateWatcher(
'showLogs',
_showLogs,
'detacher',
'detached'
)
const [stopOnFirstError] = useDetachStateWatcher(
'stopOnFirstError',
_stopOnFirstError,
'detacher',
'detached'
)
const [stopOnValidationError] = useDetachStateWatcher(
'stopOnValidationError',
_stopOnValidationError,
'detacher',
'detached'
)
const [stoppedOnFirstError] = useDetachStateWatcher(
'stoppedOnFirstError',
_stoppedOnFirstError,
'detacher',
'detached'
)
const [uncompiled] = useDetachStateWatcher(
'uncompiled',
_uncompiled,
'detacher',
'detached'
)
const [editedSinceCompileStarted] = useDetachStateWatcher(
'editedSinceCompileStarted',
_editedSinceCompileStarted,
'detacher',
'detached'
)
const [validationIssues] = useDetachStateWatcher(
'validationIssues',
_validationIssues,
'detacher',
'detached'
)
const setAnimateCompileDropdownArrow = useDetachAction(
'setAnimateCompileDropdownArrow',
_setAnimateCompileDropdownArrow,
'detached',
'detacher'
)
const setAutoCompile = useDetachAction(
'setAutoCompile',
_setAutoCompile,
'detached',
'detacher'
)
const setDraft = useDetachAction(
'setDraft',
_setDraft,
'detached',
'detacher'
)
const setPng2pdf = useDetachAction(
'setPng2pdf',
_setPng2pdf,
'detached',
'detacher'
)
const setError = useDetachAction(
'setError',
_setError,
'detacher',
'detached'
)
const setPosition = useDetachAction(
'setPosition',
_setPosition,
'detached',
'detacher'
)
const firstRenderDone = useDetachAction(
'firstRenderDone',
_firstRenderDone,
'detached',
'detacher'
)
const setHasLintingError = useDetachAction(
'setHasLintingError',
_setHasLintingError,
'detacher',
'detached'
)
const setHighlights = useDetachAction(
'setHighlights',
_setHighlights,
'detacher',
'detached'
)
const setShowCompileTimeWarning = useDetachAction(
'setShowCompileTimeWarning',
_setShowCompileTimeWarning,
'detached',
'detacher'
)
const setShowLogs = useDetachAction(
'setShowLogs',
_setShowLogs,
'detached',
'detacher'
)
const toggleLogs = useDetachAction(
'toggleLogs',
_toggleLogs,
'detached',
'detacher'
)
const setStopOnFirstError = useDetachAction(
'setStopOnFirstError',
_setStopOnFirstError,
'detached',
'detacher'
)
const setStopOnValidationError = useDetachAction(
'setStopOnValidationError',
_setStopOnValidationError,
'detached',
'detacher'
)
const cleanupCompileResult = useDetachAction(
'cleanupCompileResult',
_cleanupCompileResult,
'detached',
'detacher'
)
const recompileFromScratch = useDetachAction(
'recompileFromScratch',
_recompileFromScratch,
'detached',
'detacher'
)
const setCompiling = useDetachAction(
'setCompiling',
_setCompiling,
'detacher',
'detached'
)
const startCompile = useDetachAction(
'startCompile',
_startCompile,
'detached',
'detacher'
)
const stopCompile = useDetachAction(
'stopCompile',
_stopCompile,
'detached',
'detacher'
)
const setChangedAt = useDetachAction(
'setChangedAt',
_setChangedAt,
'detached',
'detacher'
)
const clearCache = useDetachAction(
'clearCache',
_clearCache,
'detached',
'detacher'
)
const syncToEntry = useDetachAction(
'sync-to-entry',
_syncToEntry,
'detached',
'detacher'
)
const recordAction = useDetachAction(
'record-action',
_recordAction,
'detached',
'detacher'
)
const [darkModePdf] = useDetachStateWatcher(
'darkModePdf',
_darkModePdf,
'detacher',
'detached'
)
const setDarkModePdf = useDetachAction(
'setDarkModePdf',
_setDarkModePdf,
'detached',
'detacher'
)
const [activeOverallTheme] = useDetachStateWatcher(
'activeOverallTheme',
_activeOverallTheme,
'detacher',
'detached'
)
useCompileTriggers(startCompile, setChangedAt)
useLogEvents(setShowLogs)
const value = useMemo(
() => ({
animateCompileDropdownArrow,
autoCompile,
clearCache,
clearingCache,
clsiServerId,
codeCheckFailed,
compiling,
deliveryLatencies,
draft,
png2pdf,
editedSinceCompileStarted,
error,
fileList,
hasChanges,
hasShortCompileTimeout,
highlights,
isProjectOwner,
lastCompileOptions,
logEntryAnnotations,
logEntries,
pdfDownloadUrl: pdfFile?.pdfDownloadUrl,
pdfFile,
pdfUrl: pdfFile?.pdfUrl,
pdfViewer,
position,
rawLog,
recompileFromScratch,
setAnimateCompileDropdownArrow,
setAutoCompile,
setCompiling,
setDraft,
setPng2pdf,
setError,
setHasLintingError,
setHighlights,
setPosition,
setShowCompileTimeWarning,
setShowLogs,
toggleLogs,
setStopOnFirstError,
setStopOnValidationError,
showLogs,
showCompileTimeWarning,
startCompile,
stopCompile,
stopOnFirstError,
stopOnValidationError,
stoppedOnFirstError,
uncompiled,
validationIssues,
firstRenderDone,
setChangedAt,
cleanupCompileResult,
syncToEntry,
recordAction,
darkModePdf,
setDarkModePdf,
activeOverallTheme,
}),
[
animateCompileDropdownArrow,
autoCompile,
clearCache,
clearingCache,
clsiServerId,
codeCheckFailed,
compiling,
deliveryLatencies,
draft,
png2pdf,
editedSinceCompileStarted,
error,
fileList,
hasChanges,
hasShortCompileTimeout,
highlights,
isProjectOwner,
lastCompileOptions,
logEntryAnnotations,
logEntries,
pdfFile,
pdfViewer,
position,
rawLog,
recompileFromScratch,
setAnimateCompileDropdownArrow,
setAutoCompile,
setCompiling,
setDraft,
setPng2pdf,
setError,
setHasLintingError,
setHighlights,
setPosition,
setShowCompileTimeWarning,
setShowLogs,
toggleLogs,
setStopOnFirstError,
setStopOnValidationError,
showCompileTimeWarning,
showLogs,
startCompile,
stopCompile,
stopOnFirstError,
stopOnValidationError,
stoppedOnFirstError,
uncompiled,
validationIssues,
firstRenderDone,
setChangedAt,
cleanupCompileResult,
syncToEntry,
recordAction,
darkModePdf,
setDarkModePdf,
activeOverallTheme,
]
)
return (
<DetachCompileContext.Provider value={value}>
{children}
</DetachCompileContext.Provider>
)
}
export function useDetachCompileContext() {
const context = useContext(DetachCompileContext)
if (!context) {
throw new Error(
'useDetachCompileContext is ony available inside DetachCompileProvider'
)
}
return context
}