Files
overleaf-cep/services/web/frontend/js/features/pdf-preview/components/error-logs.tsx
T
Jimmy Domagala-TangandTim Gibbon baa9cf78d9 Adding 2025 and 2026 incremental compiles options (#35850)
* feat: adding 2025 and 2026 incremental compiles options

* feat: remove dmtcp from compiler list and autoselect checkpointing version with labs inclusion

* feat: updating seccomp profile for checkpointing

* feat: disable non-premium dmtcp compiles, and align checkpoint version detection between clsi and web

* feat: adding in reminder of checkpoint experiment, and using session based split test retrieval

* Increase Packer build.sh timeout from 1h15m to 1h30m as build takes approx 1h

* fix: moving checkpointing check from tag name based to hasCheckpointing flag set as part of allowed_images, and removing uneeded seccomp rule

* feat: move variant/ image selection to CLSI, and have web control only what options to set for a compile. removing uneeded extra variants from webs list of allowed images

* feat: adding compile timer as a query string as well

---------

Co-authored-by: Tim Gibbon <tim.gibbon@overleaf.com>
GitOrigin-RevId: cdbdf76a62dcd5a585925f18d64b6cb9cd6ed373
2026-09-17 12:00:46 +00:00

171 lines
5.6 KiB
TypeScript

import { useTranslation } from 'react-i18next'
import { memo, useCallback, useMemo, useState } from 'react'
import { usePdfPreviewContext } from '@/features/pdf-preview/components/pdf-preview-provider'
import StopOnFirstErrorPrompt from '@/features/pdf-preview/components/stop-on-first-error-prompt'
import PdfPreviewError from '@/features/pdf-preview/components/pdf-preview-error'
import PdfValidationIssue from '@/features/pdf-preview/components/pdf-validation-issue'
import PdfLogsEntries from '@/features/pdf-preview/components/pdf-logs-entries'
import PdfPreviewErrorBoundaryFallback from '@/features/pdf-preview/components/pdf-preview-error-boundary-fallback'
import withErrorBoundary from '@/infrastructure/error-boundary'
import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context'
import { Nav, NavLink, TabContainer, TabContent } from 'react-bootstrap'
import { LogEntry as LogEntryData } from '@/features/pdf-preview/util/types'
import LogEntry from './log-entry'
import TimeoutUpgradePromptNew from '@/features/pdf-preview/components/timeout-upgrade-prompt-new'
import getMeta from '@/utils/meta'
import PdfClearCacheButton from '@/features/pdf-preview/components/pdf-clear-cache-button'
import PdfDownloadFilesButton from '@/features/pdf-preview/components/pdf-download-files-button'
import RollingBuildSelectedReminder from './rolling-build-selected-reminder'
import CheckpointCompilesEnabledReminder from './checkpoint-compiles-enabled-reminder'
import ErrorAssistantAiPaywallNotification from './error-assistant-ai-paywall-notification'
type ErrorLogTab = {
key: string
label: string
entries: LogEntryData[] | undefined
}
function ErrorLogs({
includeActionButtons,
}: {
includeActionButtons?: boolean
}) {
const { error, logEntries, rawLog, validationIssues, stoppedOnFirstError } =
useCompileContext()
const { compileTimeout } = getMeta('ol-compileSettings')
const { t } = useTranslation()
const tabs = useMemo(() => {
return [
{
key: 'all',
label: t('all_logs'),
entries: logEntries?.all,
},
{ key: 'errors', label: t('errors'), entries: logEntries?.errors },
{ key: 'warnings', label: t('warnings'), entries: logEntries?.warnings },
{ key: 'info', label: t('info'), entries: logEntries?.typesetting },
]
}, [logEntries, t])
const { loadingError } = usePdfPreviewContext()
const [activeTab, setActiveTab] = useState<string | null>('all')
const changeTab = useCallback(
(key: string | null) => {
if (tabs.some(tab => tab.key === key)) {
setActiveTab(key)
}
},
[tabs]
)
const entries = useMemo(() => {
return tabs.find(tab => tab.key === activeTab)?.entries || []
}, [activeTab, tabs])
const includeErrors = activeTab === 'all' || activeTab === 'errors'
const includeWarnings = activeTab === 'all' || activeTab === 'warnings'
return (
<TabContainer onSelect={changeTab} defaultActiveKey={activeTab ?? 'all'}>
<Nav defaultActiveKey="all" className="error-logs-tabs">
{tabs.map(tab => (
<TabHeader key={tab.key} tab={tab} active={activeTab === tab.key} />
))}
</Nav>
<ErrorAssistantAiPaywallNotification />
<TabContent className="error-logs new-error-logs">
<div className="logs-pane-content">
<RollingBuildSelectedReminder />
<CheckpointCompilesEnabledReminder />
{stoppedOnFirstError && includeErrors && <StopOnFirstErrorPrompt />}
{loadingError && (
<PdfPreviewError
error="pdf-viewer-loading-error"
includeErrors={includeErrors}
includeWarnings={includeWarnings}
/>
)}
{compileTimeout < 60 && error === 'timedout' ? (
<TimeoutUpgradePromptNew />
) : (
<>{error && <PdfPreviewError error={error} />}</>
)}
{includeErrors &&
validationIssues &&
Object.entries(validationIssues).map(([name, issue]) => (
<PdfValidationIssue key={name} name={name} issue={issue} />
))}
{entries && (
<PdfLogsEntries
entries={entries}
hasErrors={
includeErrors &&
logEntries?.errors &&
logEntries?.errors.length > 0
}
/>
)}
{rawLog && activeTab === 'all' && (
<LogEntry
headerTitle={t('raw_logs')}
rawContent={rawLog}
entryAriaLabel={t('raw_logs_description')}
level="raw"
alwaysExpandRawContent
showSourceLocationLink={false}
/>
)}
{includeActionButtons && (
<div className="logs-pane-actions">
<PdfClearCacheButton />
<PdfDownloadFilesButton />
</div>
)}
</div>
</TabContent>
</TabContainer>
)
}
function formatErrorNumber(num: number | undefined) {
if (num === undefined) {
return undefined
}
if (num > 99) {
return '99+'
}
return Math.floor(num).toString()
}
const TabHeader = ({ tab, active }: { tab: ErrorLogTab; active: boolean }) => {
return (
<NavLink
eventKey={tab.key}
className="error-logs-tab-header"
active={active}
as="button"
>
{tab.label}
<div className="error-logs-tab-count">
{/* TODO: it would be nice if this number included custom errors */}
{formatErrorNumber(tab.entries?.length)}
</div>
</NavLink>
)
}
export default withErrorBoundary(memo(ErrorLogs), () => (
<PdfPreviewErrorBoundaryFallback type="logs" />
))