diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 33ee8ee4a2..dd263ff7fe 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -246,8 +246,10 @@ "loading_recent_github_commits": "", "log_entry_description": "", "log_entry_maximum_entries": "", + "log_entry_maximum_entries_enable_stop_on_first_error": "", "log_entry_maximum_entries_message": "", "log_entry_maximum_entries_message_no_errors": "", + "log_entry_maximum_entries_see_full_logs": "", "log_entry_maximum_entries_title": "", "log_hint_extra_info": "", "log_viewer_error": "", @@ -351,6 +353,11 @@ "project_ownership_transfer_confirmation_2": "", "project_synced_with_git_repo_at": "", "project_synchronisation": "", + "project_timed_out_enable_stop_on_first_error": "", + "project_timed_out_fatal_error": "", + "project_timed_out_intro": "", + "project_timed_out_learn_more": "", + "project_timed_out_optimize_images": "", "project_too_large": "", "project_too_large_please_reduce": "", "project_too_much_editable_text": "", @@ -459,6 +466,7 @@ "this_project_is_public_read_only": "", "this_project_will_appear_in_your_dropbox_folder_at": "", "timedout": "", + "tip": "", "to_add_email_accounts_need_to_be_linked_2": "", "to_add_more_collaborators": "", "to_change_access_permissions": "", diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-logs-viewer.js b/services/web/frontend/js/features/pdf-preview/components/pdf-logs-viewer.js index 8084b1f28d..3354439787 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-logs-viewer.js +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-logs-viewer.js @@ -22,7 +22,6 @@ function PdfLogsViewer() { rawLog, validationIssues, showLogs, - stopOnFirstError, stoppedOnFirstError, } = useCompileContext() @@ -33,7 +32,7 @@ function PdfLogsViewer() {
{codeCheckFailed && } - {stopOnFirstError && stoppedOnFirstError && } + {stoppedOnFirstError && } {error && } diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js b/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js index 0a156b2a5e..78d119ea26 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js @@ -1,10 +1,15 @@ import PropTypes from 'prop-types' import { useTranslation, Trans } from 'react-i18next' import { memo } from 'react' +import { Button } from 'react-bootstrap' import PdfLogEntry from './pdf-log-entry' +import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' +import getMeta from '../../../utils/meta' function PdfPreviewError({ error }) { const { t } = useTranslation() + const { lastCompileOptions, setStopOnFirstError, startCompile } = + useCompileContext() switch (error) { case 'rendering-error': @@ -70,22 +75,82 @@ function PdfPreviewError({ error }) { ) - case 'timedout': - return ( - - {t('proj_timed_out_reason')} + case 'timedout': { + const showStopOnFirstError = getMeta('ol-showStopOnFirstError') + if (showStopOnFirstError) { + return ( + +

{t('project_timed_out_intro')}

+
    +
  • + , + ]} + /> +
  • +
  • + , + ]} + /> + {!lastCompileOptions.stopOnFirstError && ( + <> + {' '} + { + startCompile({ stopOnFirstError: true }) + setStopOnFirstError(true) + }} + />, + ]} + /> + + )} +
  • +
+

+ + ), + }} + /> +

+
+ ) + } else { + return ( + + {t('proj_timed_out_reason')} - - - ) + +
+ ) + } + } case 'failure': return ( diff --git a/services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.js b/services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.js index 1bf6976da8..386c62728b 100644 --- a/services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.js +++ b/services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.js @@ -1,10 +1,16 @@ import PropTypes from 'prop-types' import { Trans, useTranslation } from 'react-i18next' +import { Button } from 'react-bootstrap' import PreviewLogEntryHeader from './preview-log-entry-header' import Icon from '../../../shared/components/icon' +import getMeta from '../../../utils/meta' +import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) { const { t } = useTranslation() + const showStopOnFirstError = getMeta('ol-showStopOnFirstError') + const { startCompile, setStopOnFirstError, stoppedOnFirstError } = + useCompileContext() const title = t('log_entry_maximum_entries_title', { total: totalEntries, @@ -15,18 +21,59 @@ function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) {
- -   - {hasErrors ? ( - ,

]} // eslint-disable-line react/jsx-key - /> + {showStopOnFirstError ? ( + hasErrors && !stoppedOnFirstError ? ( + <> +

+ +   + {t('tip')}: + { + startCompile({ stopOnFirstError: true }) + setStopOnFirstError(true) + }} + /> + ), + 'learn-more-link': ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + + ), + }} + /> +

+

{t('log_entry_maximum_entries_see_full_logs')}

+ + ) : ( +

+ +   + {t('tip')}: + {t('log_entry_maximum_entries_see_full_logs')} +

+ ) ) : ( - ]} - /> + <> + +   + {hasErrors ? ( + ,

]} // eslint-disable-line react/jsx-key + /> + ) : ( + ]} // eslint-disable-line react/jsx-key + /> + )} + )}

diff --git a/services/web/frontend/js/shared/context/detach-compile-context.js b/services/web/frontend/js/shared/context/detach-compile-context.js index 524fa5eb10..162dde9c89 100644 --- a/services/web/frontend/js/shared/context/detach-compile-context.js +++ b/services/web/frontend/js/shared/context/detach-compile-context.js @@ -30,6 +30,7 @@ export function DetachCompileProvider({ children }) { fileList: _fileList, hasChanges: _hasChanges, highlights: _highlights, + lastCompileOptions: _lastCompileOptions, logEntries: _logEntries, logEntryAnnotations: _logEntryAnnotations, pdfDownloadUrl: _pdfDownloadUrl, @@ -113,6 +114,12 @@ export function DetachCompileProvider({ children }) { 'detacher', 'detached' ) + const [lastCompileOptions] = useDetachStateWatcher( + 'lastCompileOptions', + _lastCompileOptions, + 'detacher', + 'detached' + ) const [logEntries] = useDetachStateWatcher( 'logEntries', _logEntries, @@ -314,6 +321,7 @@ export function DetachCompileProvider({ children }) { fileList, hasChanges, highlights, + lastCompileOptions, logEntryAnnotations, logEntries, pdfDownloadUrl, @@ -357,6 +365,7 @@ export function DetachCompileProvider({ children }) { fileList, hasChanges, highlights, + lastCompileOptions, logEntryAnnotations, logEntries, pdfDownloadUrl, diff --git a/services/web/frontend/js/shared/context/local-compile-context.js b/services/web/frontend/js/shared/context/local-compile-context.js index f83d22ce27..69a12d41e9 100644 --- a/services/web/frontend/js/shared/context/local-compile-context.js +++ b/services/web/frontend/js/shared/context/local-compile-context.js @@ -458,6 +458,10 @@ export function LocalCompileProvider({ children }) { }) }, [clearCache, compiler]) + // After a compile, the compiler sets `data.options` to the options that were + // used for that compile. + const lastCompileOptions = useMemo(() => data?.options || {}, [data]) + const value = useMemo( () => ({ autoCompile, @@ -471,6 +475,7 @@ export function LocalCompileProvider({ children }) { fileList, hasChanges, highlights, + lastCompileOptions, logEntryAnnotations, logEntries, pdfDownloadUrl, @@ -514,6 +519,7 @@ export function LocalCompileProvider({ children }) { fileList, hasChanges, highlights, + lastCompileOptions, logEntries, logEntryAnnotations, position, diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 5bd4bf3264..d4f3fe7751 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -34,6 +34,9 @@ "log_entry_maximum_entries_title": "__total__ log messages total. Showing the first __displayed__", "log_entry_maximum_entries_message": "<0>Tip: Try to fix the first error and recompile. Often one error causes many later error messages.<1>If you need to see the full logs, you can still download them or view the raw logs below.", "log_entry_maximum_entries_message_no_errors": "<0>Tip: If you need to see the full logs, you can still download them or view the raw logs below.", + "log_entry_maximum_entries_enable_stop_on_first_error": "Try to fix the first error and recompile. Often one error causes many later error messages. You can to focus on fixing errors. We recommend fixing errors as soon as possible; letting them accumulate may lead to hard-to-debug and fatal errors. Learn more", + "log_entry_maximum_entries_see_full_logs": "If you need to see the full logs, you can still download them or view the raw logs below.", + "tip": "Tip", "navigate_log_source": "Navigate to log position in source code: __location__", "other_output_files": "Download other output files", "refresh": "Refresh", @@ -1082,6 +1085,11 @@ "something_went_wrong_loading_pdf_viewer": "Something went wrong loading the PDF viewer. This might be caused by issues like <0>temporary network problems or an <0>outdated web browser. Please follow the <1>troubleshooting steps for access, loading and display problems. If the issue persists, please <2>let us know.", "timedout": "Timed out", "proj_timed_out_reason": "Sorry, your compile took too long to run and timed out. This may be due to a LaTeX error, or a large number of high-res images or complicated diagrams.", + "project_timed_out_intro": "Sorry, your compile took too long to run and timed out. The most common causes of timeouts are:", + "project_timed_out_optimize_images": "Large or high-resolution images are taking too long to process. You may be able to <0>optimize them.", + "project_timed_out_fatal_error": "A <0>fatal compile error may be completely blocking compilation.", + "project_timed_out_enable_stop_on_first_error": "<0>Enable “Stop on first error” to help you find and fix errors right away.", + "project_timed_out_learn_more": "<0>Learn more about other causes of compile timeouts and how to fix them.", "no_errors_good_job": "No errors, good job!", "compile_error": "Compile Error", "generic_failed_compile_message": "Sorry, your LaTeX code couldn’t compile for some reason. Please check the errors below for details, or view the raw log",