mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-10 06:39:01 +02:00
Merge pull request #3175 from overleaf/pr-spike-new-logs-errors-ui
Spike new logs errors ui GitOrigin-RevId: b4c598bc03d8aa8e5a438ac5633e18dadd82dc74
This commit is contained in:
committed by
Copybot
parent
ec0240395f
commit
315987dc5a
@@ -841,7 +841,8 @@ const ProjectController = {
|
||||
showSupport: Features.hasFeature('support'),
|
||||
gaOptimize: enableOptimize,
|
||||
customOptimizeEvent: true,
|
||||
experimentId: Settings.experimentId
|
||||
experimentId: Settings.experimentId,
|
||||
showNewLogsUI: req.query && req.query.new_logs_ui === 'true'
|
||||
})
|
||||
timer.done()
|
||||
}
|
||||
|
||||
@@ -1,224 +1,242 @@
|
||||
div.full-size.pdf(ng-controller="PdfController")
|
||||
.toolbar.toolbar-pdf(ng-class="{ 'changes-to-autocompile': changesToAutoCompile && !autoCompileLintingError }")
|
||||
.btn-group.btn-recompile-group#recompile(
|
||||
dropdown,
|
||||
tooltip-html="'"+translate('recompile_pdf')+" <span class=\"keyboard-shortcut\">({{modifierKey}} + Enter)</span>'"
|
||||
tooltip-class="keyboard-tooltip"
|
||||
tooltip-popup-delay="500"
|
||||
tooltip-append-to-body="true"
|
||||
tooltip-placement="bottom"
|
||||
if showNewLogsUI
|
||||
preview-pane(
|
||||
compiler-state=`{
|
||||
isAutoCompileOn: autocompile_enabled,
|
||||
isCompiling: pdf.compiling,
|
||||
isDraftModeOn: draft,
|
||||
isSyntaxCheckOn: stop_on_validation_error
|
||||
}`
|
||||
log-entries="pdf.logEntries ? pdf.logEntries.all : []"
|
||||
on-recompile="recompile"
|
||||
on-run-syntax-check-now="runSyntaxCheckNow"
|
||||
on-set-auto-compile="setAutoCompile"
|
||||
on-set-draft-mode="setDraftMode"
|
||||
on-set-syntax-check="setSyntaxCheck"
|
||||
on-toggle-logs="toggleLogs"
|
||||
should-show-logs="shouldShowLogs"
|
||||
)
|
||||
a.btn.btn-recompile(
|
||||
href,
|
||||
ng-disabled="pdf.compiling",
|
||||
ng-click="recompile()"
|
||||
)
|
||||
i.fa.fa-refresh(
|
||||
ng-class="{'fa-spin': pdf.compiling }"
|
||||
)
|
||||
|
|
||||
span(ng-show="!pdf.compiling") #{translate("recompile")}
|
||||
span(ng-show="pdf.compiling") #{translate("compiling")}…
|
||||
a.btn.btn-recompile.dropdown-toggle(
|
||||
href,
|
||||
ng-disabled="pdf.compiling",
|
||||
dropdown-toggle
|
||||
)
|
||||
span.caret
|
||||
ul.dropdown-menu.dropdown-menu-left
|
||||
li.dropdown-header #{translate("auto_compile")}
|
||||
li
|
||||
a(href, ng-click="autocompile_enabled = true")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': autocompile_enabled}")
|
||||
| #{translate('on')}
|
||||
li
|
||||
a(href, ng-click="autocompile_enabled = false")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': !autocompile_enabled}")
|
||||
| #{translate('off')}
|
||||
li.dropdown-header #{translate("compile_mode")}
|
||||
li
|
||||
a(href, ng-click="draft = false")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': !draft}")
|
||||
| #{translate("normal")}
|
||||
li
|
||||
a(href, ng-click="draft = true")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': draft}")
|
||||
| #{translate("fast")}
|
||||
span.subdued [draft]
|
||||
li.dropdown-header #{translate("compile_time_checks")}
|
||||
li
|
||||
a(href, ng-click="stop_on_validation_error = true")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': stop_on_validation_error}")
|
||||
| #{translate("stop_on_validation_error")}
|
||||
li
|
||||
a(href, ng-click="stop_on_validation_error = false")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': !stop_on_validation_error}")
|
||||
| #{translate("ignore_validation_errors")}
|
||||
li
|
||||
a(href, ng-click="recompile({check:true})")
|
||||
i.fa.fa-fw()
|
||||
| #{translate("run_syntax_check_now")}
|
||||
a(
|
||||
href
|
||||
ng-click="stop()"
|
||||
ng-show="pdf.compiling",
|
||||
tooltip=translate('stop_compile')
|
||||
tooltip-placement="bottom"
|
||||
)
|
||||
i.fa.fa-fw.fa-stop()
|
||||
a.log-btn(
|
||||
href
|
||||
ng-click="toggleLogs()"
|
||||
ng-class="{ 'active': shouldShowLogs == true }"
|
||||
tooltip=translate('logs_and_output_files')
|
||||
tooltip-placement="bottom"
|
||||
)
|
||||
i.fa.fa-fw.fa-file-text-o
|
||||
span.label(
|
||||
ng-show="pdf.logEntries.warnings.length + pdf.logEntries.errors.length > 0"
|
||||
ng-class="{\
|
||||
'label-warning': pdf.logEntries.errors.length == 0,\
|
||||
'label-danger': pdf.logEntries.errors.length > 0\
|
||||
}"
|
||||
) {{ pdf.logEntries.errors.length + pdf.logEntries.warnings.length }}
|
||||
|
||||
a(
|
||||
ng-href="{{pdf.downloadUrl || pdf.url}}"
|
||||
target="_blank"
|
||||
ng-if="pdf.url"
|
||||
tooltip=translate('download_pdf')
|
||||
tooltip-placement="bottom"
|
||||
)
|
||||
i.fa.fa-fw.fa-download
|
||||
|
||||
.toolbar-right
|
||||
span.auto-compile-status.small(
|
||||
ng-show="changesToAutoCompile && !compiling && !autoCompileLintingError"
|
||||
) #{translate('uncompiled_changes')}
|
||||
span.auto-compile-status.auto-compile-error.small(
|
||||
ng-show="autoCompileLintingError"
|
||||
tooltip-placement="bottom"
|
||||
tooltip=translate("code_check_failed_explanation")
|
||||
else
|
||||
.toolbar.toolbar-pdf(ng-class="{ 'changes-to-autocompile': changesToAutoCompile && !autoCompileLintingError }")
|
||||
.btn-group.btn-recompile-group#recompile(
|
||||
dropdown,
|
||||
tooltip-html="'"+translate('recompile_pdf')+" <span class=\"keyboard-shortcut\">({{modifierKey}} + Enter)</span>'"
|
||||
tooltip-class="keyboard-tooltip"
|
||||
tooltip-popup-delay="500"
|
||||
tooltip-append-to-body="true"
|
||||
)
|
||||
i.fa.fa-fw.fa-exclamation-triangle
|
||||
|
|
||||
| #{translate("code_check_failed")}
|
||||
a(
|
||||
href,
|
||||
ng-click="switchToFlatLayout('pdf')"
|
||||
ng-show="ui.pdfLayout == 'sideBySide'"
|
||||
tooltip=translate('full_screen')
|
||||
tooltip-placement="bottom"
|
||||
tooltip-append-to-body="true"
|
||||
)
|
||||
i.fa.fa-expand
|
||||
a(
|
||||
href,
|
||||
ng-click="switchToSideBySideLayout('editor')"
|
||||
ng-show="ui.pdfLayout == 'flat'"
|
||||
tooltip=translate('split_screen')
|
||||
tooltip-placement="bottom"
|
||||
tooltip-append-to-body="true"
|
||||
)
|
||||
i.fa.fa-compress
|
||||
// end of toolbar
|
||||
|
||||
// logs view
|
||||
.pdf-logs(ng-show="shouldShowLogs")
|
||||
.alert.alert-success(ng-show="pdf.logEntries.all.length == 0 && !pdf.failure")
|
||||
| #{translate("no_errors_good_job")}
|
||||
|
||||
.alert.alert-danger(ng-show="pdf.failure")
|
||||
strong #{translate("compile_error")}.
|
||||
span #{translate("generic_failed_compile_message")}.
|
||||
|
||||
.alert.alert-danger(ng-show="pdf.failedCheck")
|
||||
strong #{translate("failed_compile_check")}.
|
||||
p
|
||||
p.text-center(ng-show="!check")
|
||||
a.text-info(
|
||||
a.btn.btn-recompile(
|
||||
href,
|
||||
ng-disabled="pdf.compiling",
|
||||
ng-click="recompile({try:true})"
|
||||
) #{translate("failed_compile_check_try")}
|
||||
|  #{translate("failed_compile_option_or")} 
|
||||
a.text-info(
|
||||
href,
|
||||
ng-disabled="pdf.compiling",
|
||||
ng-click="recompile({force:true})"
|
||||
) #{translate("failed_compile_check_ignore")}
|
||||
| .
|
||||
|
||||
div(ng-repeat="entry in pdf.logEntries.all", ng-controller="PdfLogEntryController")
|
||||
.alert(
|
||||
ng-class="{\
|
||||
'alert-danger': entry.level == 'error',\
|
||||
'alert-warning': entry.level == 'warning',\
|
||||
'alert-info': entry.level == 'typesetting'\
|
||||
}"
|
||||
ng-click="openInEditor(entry)"
|
||||
)
|
||||
span.line-no
|
||||
i.fa.fa-link(aria-hidden="true")
|
||||
|
|
||||
span(ng-show="entry.file") {{ entry.file }}
|
||||
span(ng-show="entry.line") , line {{ entry.line }}
|
||||
p.entry-message(ng-show="entry.message")
|
||||
| {{ entry.type }} {{ entry.message }}
|
||||
.card.card-hint(
|
||||
ng-if="entry.humanReadableHint"
|
||||
stop-propagation="click"
|
||||
ng-click="recompile()"
|
||||
)
|
||||
figure.card-hint-icon-container
|
||||
i.fa.fa-lightbulb-o(aria-hidden="true")
|
||||
p.card-hint-text(
|
||||
ng-show="entry.humanReadableHint",
|
||||
ng-bind-html="wikiEnabled ? entry.humanReadableHint : stripHTMLFromString(entry.humanReadableHint)")
|
||||
.card-hint-footer.clearfix
|
||||
.card-hint-ext-link(ng-if="wikiEnabled && entry.extraInfoURL")
|
||||
a(
|
||||
ng-href="{{ entry.extraInfoURL }}",
|
||||
ng-click="trackLogHintsLearnMore()"
|
||||
target="_blank"
|
||||
)
|
||||
i.fa.fa-external-link
|
||||
| #{translate("log_hint_extra_info")}
|
||||
|
||||
p.entry-content(ng-show="entry.content") {{ entry.content.trim() }}
|
||||
|
||||
div
|
||||
.files-dropdown-container
|
||||
a.btn.btn-default.btn-sm(
|
||||
href,
|
||||
tooltip=translate('clear_cached_files'),
|
||||
tooltip-placement="top",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="openClearCacheModal()"
|
||||
)
|
||||
i.fa.fa-trash-o
|
||||
|
|
||||
div.files-dropdown(
|
||||
ng-class="shouldDropUp ? 'dropup' : 'dropdown'"
|
||||
dropdown
|
||||
)
|
||||
a.btn.btn-default.btn-sm(
|
||||
href
|
||||
dropdown-toggle
|
||||
i.fa.fa-refresh(
|
||||
ng-class="{'fa-spin': pdf.compiling }"
|
||||
)
|
||||
| !{translate("other_logs_and_files")}
|
||||
span.caret
|
||||
ul.dropdown-menu.dropdown-menu-right
|
||||
li(ng-repeat="file in pdf.outputFiles")
|
||||
a(
|
||||
href="{{file.url}}"
|
||||
target="_blank"
|
||||
) {{ file.name }}
|
||||
a.btn.btn-info.btn-sm(href, ng-click="toggleRawLog()")
|
||||
span(ng-show="!pdf.showRawLog") #{translate("view_raw_logs")}
|
||||
span(ng-show="pdf.showRawLog") #{translate("hide_raw_logs")}
|
||||
span.btn-recompile-label(ng-show="!pdf.compiling") #{translate("recompile")}
|
||||
span.btn-recompile-label(ng-show="pdf.compiling") #{translate("compiling")}…
|
||||
|
||||
a.btn.btn-recompile.dropdown-toggle(
|
||||
href,
|
||||
ng-disabled="pdf.compiling",
|
||||
dropdown-toggle
|
||||
)
|
||||
span.caret
|
||||
ul.dropdown-menu.dropdown-menu-left
|
||||
li.dropdown-header #{translate("auto_compile")}
|
||||
li
|
||||
a(href, ng-click="autocompile_enabled = true")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': autocompile_enabled}")
|
||||
| #{translate('on')}
|
||||
li
|
||||
a(href, ng-click="autocompile_enabled = false")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': !autocompile_enabled}")
|
||||
| #{translate('off')}
|
||||
li.dropdown-header #{translate("compile_mode")}
|
||||
li
|
||||
a(href, ng-click="draft = false")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': !draft}")
|
||||
| #{translate("normal")}
|
||||
li
|
||||
a(href, ng-click="draft = true")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': draft}")
|
||||
| #{translate("fast")}
|
||||
span.subdued [draft]
|
||||
li.dropdown-header #{translate("compile_time_checks")}
|
||||
li
|
||||
a(href, ng-click="stop_on_validation_error = true")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': stop_on_validation_error}")
|
||||
| #{translate("stop_on_validation_error")}
|
||||
li
|
||||
a(href, ng-click="stop_on_validation_error = false")
|
||||
i.fa.fa-fw(ng-class="{'fa-check': !stop_on_validation_error}")
|
||||
| #{translate("ignore_validation_errors")}
|
||||
li
|
||||
a(href, ng-click="recompile({check:true})")
|
||||
i.fa.fa-fw()
|
||||
| #{translate("run_syntax_check_now")}
|
||||
a(
|
||||
href
|
||||
ng-click="stop()"
|
||||
ng-show="pdf.compiling",
|
||||
tooltip=translate('stop_compile')
|
||||
tooltip-placement="bottom"
|
||||
)
|
||||
i.fa.fa-fw.fa-stop()
|
||||
a.log-btn(
|
||||
href
|
||||
ng-click="toggleLogs()"
|
||||
ng-class="{ 'active': shouldShowLogs == true }"
|
||||
tooltip=translate('logs_and_output_files')
|
||||
tooltip-placement="bottom"
|
||||
)
|
||||
i.fa.fa-fw.fa-file-text-o
|
||||
span.label(
|
||||
ng-show="pdf.logEntries.warnings.length + pdf.logEntries.errors.length > 0"
|
||||
ng-class="{\
|
||||
'label-warning': pdf.logEntries.errors.length == 0,\
|
||||
'label-danger': pdf.logEntries.errors.length > 0\
|
||||
}"
|
||||
) {{ pdf.logEntries.errors.length + pdf.logEntries.warnings.length }}
|
||||
|
||||
a(
|
||||
ng-href="{{pdf.downloadUrl || pdf.url}}"
|
||||
target="_blank"
|
||||
ng-if="pdf.url"
|
||||
tooltip=translate('download_pdf')
|
||||
tooltip-placement="bottom"
|
||||
)
|
||||
i.fa.fa-fw.fa-download
|
||||
|
||||
.toolbar-right
|
||||
span.auto-compile-status.small(
|
||||
ng-show="changesToAutoCompile && !compiling && !autoCompileLintingError"
|
||||
) #{translate('uncompiled_changes')}
|
||||
span.auto-compile-status.auto-compile-error.small(
|
||||
ng-show="autoCompileLintingError"
|
||||
tooltip-placement="bottom"
|
||||
tooltip=translate("code_check_failed_explanation")
|
||||
tooltip-append-to-body="true"
|
||||
)
|
||||
i.fa.fa-fw.fa-exclamation-triangle
|
||||
|
|
||||
| #{translate("code_check_failed")}
|
||||
a(
|
||||
href,
|
||||
ng-click="switchToFlatLayout('pdf')"
|
||||
ng-show="ui.pdfLayout == 'sideBySide'"
|
||||
tooltip=translate('full_screen')
|
||||
tooltip-placement="bottom"
|
||||
tooltip-append-to-body="true"
|
||||
)
|
||||
i.fa.fa-expand
|
||||
a(
|
||||
href,
|
||||
ng-click="switchToSideBySideLayout('editor')"
|
||||
ng-show="ui.pdfLayout == 'flat'"
|
||||
tooltip=translate('split_screen')
|
||||
tooltip-placement="bottom"
|
||||
tooltip-append-to-body="true"
|
||||
)
|
||||
i.fa.fa-compress
|
||||
// end of toolbar
|
||||
|
||||
pre(ng-bind="pdf.rawLog", ng-show="pdf.showRawLog")
|
||||
// logs view
|
||||
.pdf-logs(ng-show="shouldShowLogs")
|
||||
.alert.alert-success(ng-show="pdf.logEntries.all.length == 0 && !pdf.failure")
|
||||
| #{translate("no_errors_good_job")}
|
||||
|
||||
.alert.alert-danger(ng-show="pdf.failure")
|
||||
strong #{translate("compile_error")}.
|
||||
span #{translate("generic_failed_compile_message")}.
|
||||
|
||||
.alert.alert-danger(ng-show="pdf.failedCheck")
|
||||
strong #{translate("failed_compile_check")}.
|
||||
p
|
||||
p.text-center(ng-show="!check")
|
||||
a.text-info(
|
||||
href,
|
||||
ng-disabled="pdf.compiling",
|
||||
ng-click="recompile({try:true})"
|
||||
) #{translate("failed_compile_check_try")}
|
||||
|  #{translate("failed_compile_option_or")} 
|
||||
a.text-info(
|
||||
href,
|
||||
ng-disabled="pdf.compiling",
|
||||
ng-click="recompile({force:true})"
|
||||
) #{translate("failed_compile_check_ignore")}
|
||||
| .
|
||||
|
||||
div(ng-repeat="entry in pdf.logEntries.all", ng-controller="PdfLogEntryController")
|
||||
.alert(
|
||||
ng-class="{\
|
||||
'alert-danger': entry.level == 'error',\
|
||||
'alert-warning': entry.level == 'warning',\
|
||||
'alert-info': entry.level == 'typesetting'\
|
||||
}"
|
||||
ng-click="openInEditor(entry)"
|
||||
)
|
||||
span.line-no
|
||||
i.fa.fa-link(aria-hidden="true")
|
||||
|
|
||||
span(ng-show="entry.file") {{ entry.file }}
|
||||
span(ng-show="entry.line") , line {{ entry.line }}
|
||||
p.entry-message(ng-show="entry.message")
|
||||
| {{ entry.type }} {{ entry.message }}
|
||||
.card.card-hint(
|
||||
ng-if="entry.humanReadableHint"
|
||||
stop-propagation="click"
|
||||
)
|
||||
figure.card-hint-icon-container
|
||||
i.fa.fa-lightbulb-o(aria-hidden="true")
|
||||
p.card-hint-text(
|
||||
ng-show="entry.humanReadableHint",
|
||||
ng-bind-html="wikiEnabled ? entry.humanReadableHint : stripHTMLFromString(entry.humanReadableHint)")
|
||||
.card-hint-footer.clearfix
|
||||
.card-hint-ext-link(ng-if="wikiEnabled && entry.extraInfoURL")
|
||||
a(
|
||||
ng-href="{{ entry.extraInfoURL }}",
|
||||
ng-click="trackLogHintsLearnMore()"
|
||||
target="_blank"
|
||||
)
|
||||
i.fa.fa-external-link
|
||||
| #{translate("log_hint_extra_info")}
|
||||
|
||||
p.entry-content(ng-show="entry.content") {{ entry.content.trim() }}
|
||||
|
||||
div
|
||||
.files-dropdown-container
|
||||
a.btn.btn-default.btn-sm(
|
||||
href,
|
||||
tooltip=translate('clear_cached_files'),
|
||||
tooltip-placement="top",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="openClearCacheModal()"
|
||||
)
|
||||
i.fa.fa-trash-o
|
||||
|
|
||||
div.files-dropdown(
|
||||
ng-class="shouldDropUp ? 'dropup' : 'dropdown'"
|
||||
dropdown
|
||||
)
|
||||
a.btn.btn-default.btn-sm(
|
||||
href
|
||||
dropdown-toggle
|
||||
)
|
||||
| !{translate("other_logs_and_files")}
|
||||
span.caret
|
||||
ul.dropdown-menu.dropdown-menu-right
|
||||
li(ng-repeat="file in pdf.outputFiles")
|
||||
a(
|
||||
href="{{file.url}}"
|
||||
target="_blank"
|
||||
) {{ file.name }}
|
||||
a.btn.btn-info.btn-sm(href, ng-click="toggleRawLog()")
|
||||
span(ng-show="!pdf.showRawLog") #{translate("view_raw_logs")}
|
||||
span(ng-show="pdf.showRawLog") #{translate("hide_raw_logs")}
|
||||
|
||||
pre(ng-bind="pdf.rawLog", ng-show="pdf.showRawLog")
|
||||
|
||||
|
||||
// non-log views (pdf and errors)
|
||||
|
||||
@@ -6,5 +6,16 @@
|
||||
"expand",
|
||||
"collapse",
|
||||
"show_outline",
|
||||
"hide_outline"
|
||||
"hide_outline",
|
||||
"compiling",
|
||||
"recompile",
|
||||
"auto_compile",
|
||||
"on",
|
||||
"off",
|
||||
"compile_mode",
|
||||
"normal",
|
||||
"fast",
|
||||
"stop_on_validation_error",
|
||||
"ignore_validation_errors",
|
||||
"run_syntax_check_now"
|
||||
]
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classNames from 'classnames'
|
||||
|
||||
function PreviewLogEntry({ file, line, message, content, raw, level }) {
|
||||
const logEntryClasses = classNames('alert', {
|
||||
'alert-danger': level === 'error',
|
||||
'alert-warning': level === 'warning',
|
||||
'alert-info': level === 'typesetting'
|
||||
})
|
||||
return (
|
||||
<div className={logEntryClasses}>
|
||||
<span className="line-no">
|
||||
<i className="fa fa-link" aria-hidden="true" />
|
||||
{file ? <span>{file}</span> : null}
|
||||
{line ? <span>, {line}</span> : null}
|
||||
</span>
|
||||
{message ? (
|
||||
<p className="entry-message">
|
||||
{level} {message}
|
||||
</p>
|
||||
) : null}
|
||||
{content ? <p className="entry-content">{content.trim()}</p> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PreviewLogEntry.propTypes = {
|
||||
file: PropTypes.string,
|
||||
// `line should be either a number or null (i.e. not required), but currently sometimes we get
|
||||
// an empty string (from BibTeX errors), which is why we're using `any` here. We should revert
|
||||
// to PropTypes.number (not required) once we fix that.
|
||||
line: PropTypes.any,
|
||||
message: PropTypes.string,
|
||||
content: PropTypes.string,
|
||||
raw: PropTypes.string,
|
||||
level: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default PreviewLogEntry
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import PreviewLogEntry from './preview-log-entry'
|
||||
|
||||
function PreviewLogsPane({ logEntries }) {
|
||||
return (
|
||||
<div className="pdf-logs">
|
||||
{logEntries && logEntries.length > 0 ? (
|
||||
logEntries.map((logEntry, idx) => (
|
||||
<PreviewLogEntry key={idx} {...logEntry} />
|
||||
))
|
||||
) : (
|
||||
<div>No logs</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PreviewLogsPane.propTypes = {
|
||||
logEntries: PropTypes.array
|
||||
}
|
||||
|
||||
export default PreviewLogsPane
|
||||
@@ -0,0 +1,50 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import PreviewToolbar from './preview-toolbar'
|
||||
import PreviewLogsPane from './preview-logs-pane'
|
||||
|
||||
function PreviewPane({
|
||||
compilerState,
|
||||
logEntries,
|
||||
onRecompile,
|
||||
onRunSyntaxCheckNow,
|
||||
onSetAutoCompile,
|
||||
onSetDraftMode,
|
||||
onSetSyntaxCheck,
|
||||
onToggleLogs,
|
||||
shouldShowLogs
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<PreviewToolbar
|
||||
compilerState={compilerState}
|
||||
onRecompile={onRecompile}
|
||||
onRunSyntaxCheckNow={onRunSyntaxCheckNow}
|
||||
onSetAutoCompile={onSetAutoCompile}
|
||||
onSetDraftMode={onSetDraftMode}
|
||||
onSetSyntaxCheck={onSetSyntaxCheck}
|
||||
onToggleLogs={onToggleLogs}
|
||||
/>
|
||||
{shouldShowLogs ? <PreviewLogsPane logEntries={logEntries} /> : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
PreviewPane.propTypes = {
|
||||
compilerState: PropTypes.shape({
|
||||
isAutoCompileOn: PropTypes.bool.isRequired,
|
||||
isCompiling: PropTypes.bool.isRequired,
|
||||
isDraftModeOn: PropTypes.bool.isRequired,
|
||||
isSyntaxCheckOn: PropTypes.bool.isRequired
|
||||
}),
|
||||
logEntries: PropTypes.array,
|
||||
onRecompile: PropTypes.func.isRequired,
|
||||
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
||||
onSetAutoCompile: PropTypes.func.isRequired,
|
||||
onSetDraftMode: PropTypes.func.isRequired,
|
||||
onSetSyntaxCheck: PropTypes.func.isRequired,
|
||||
onToggleLogs: PropTypes.func.isRequired,
|
||||
shouldShowLogs: PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
export default PreviewPane
|
||||
@@ -0,0 +1,125 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classNames from 'classnames'
|
||||
import { Dropdown, MenuItem } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
function PreviewRecompileButton({
|
||||
compilerState: {
|
||||
isAutoCompileOn,
|
||||
isCompiling,
|
||||
isDraftModeOn,
|
||||
isSyntaxCheckOn
|
||||
},
|
||||
onRecompile,
|
||||
onRunSyntaxCheckNow,
|
||||
onSetAutoCompile,
|
||||
onSetDraftMode,
|
||||
onSetSyntaxCheck
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const iconClasses = {
|
||||
recompile: classNames('fa', 'fa-refresh', {
|
||||
'fa-spin': isCompiling
|
||||
}),
|
||||
autoCompileOn: classNames('fa', 'fa-fw', { 'fa-check': isAutoCompileOn }),
|
||||
autoCompileOff: classNames('fa', 'fa-fw', { 'fa-check': !isAutoCompileOn }),
|
||||
compileModeNormal: classNames('fa', 'fa-fw', {
|
||||
'fa-check': !isDraftModeOn
|
||||
}),
|
||||
compileModeDraft: classNames('fa', 'fa-fw', { 'fa-check': isDraftModeOn }),
|
||||
syntaxCheckOn: classNames('fa', 'fa-fw', { 'fa-check': isSyntaxCheckOn }),
|
||||
syntaxCheckOff: classNames('fa', 'fa-fw', { 'fa-check': !isSyntaxCheckOn })
|
||||
}
|
||||
|
||||
function handleSelectAutoCompileOn() {
|
||||
onSetAutoCompile(true)
|
||||
}
|
||||
|
||||
function handleSelectAutoCompileOff() {
|
||||
onSetAutoCompile(false)
|
||||
}
|
||||
|
||||
function handleSelectDraftModeOn() {
|
||||
onSetDraftMode(true)
|
||||
}
|
||||
|
||||
function handleSelectDraftModeOff() {
|
||||
onSetDraftMode(false)
|
||||
}
|
||||
|
||||
function handleSelectSyntaxCheckOn() {
|
||||
onSetSyntaxCheck(true)
|
||||
}
|
||||
|
||||
function handleSelectSyntaxCheckOff() {
|
||||
onSetSyntaxCheck(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown id="pdf-recompile-dropdown" className="btn-recompile-group">
|
||||
<button className="btn btn-recompile" onClick={onRecompile}>
|
||||
<i className={iconClasses.recompile} aria-hidden="true" />
|
||||
{isCompiling ? (
|
||||
<span className="btn-recompile-label">
|
||||
{t('compiling')}
|
||||
…
|
||||
</span>
|
||||
) : (
|
||||
<span className="btn-recompile-label">{t('recompile')}</span>
|
||||
)}
|
||||
</button>
|
||||
<Dropdown.Toggle className="btn btn-recompile" />
|
||||
<Dropdown.Menu>
|
||||
<MenuItem header>{t('auto_compile')}</MenuItem>
|
||||
<MenuItem onSelect={handleSelectAutoCompileOn}>
|
||||
<i className={iconClasses.autoCompileOn} aria-hidden="true" />
|
||||
{t('on')}
|
||||
</MenuItem>
|
||||
<MenuItem onSelect={handleSelectAutoCompileOff}>
|
||||
<i className={iconClasses.autoCompileOff} aria-hidden="true" />
|
||||
{t('off')}
|
||||
</MenuItem>
|
||||
<MenuItem header>{t('compile_mode')}</MenuItem>
|
||||
<MenuItem onSelect={handleSelectDraftModeOff}>
|
||||
<i className={iconClasses.compileModeNormal} aria-hidden="true" />
|
||||
{t('normal')}
|
||||
</MenuItem>
|
||||
<MenuItem onSelect={handleSelectDraftModeOn}>
|
||||
<i className={iconClasses.compileModeDraft} aria-hidden="true" />
|
||||
{t('fast')} <span className="subdued">[draft]</span>
|
||||
</MenuItem>
|
||||
<MenuItem header>Syntax Checks</MenuItem>
|
||||
<MenuItem onSelect={handleSelectSyntaxCheckOn}>
|
||||
<i className={iconClasses.syntaxCheckOn} aria-hidden="true" />
|
||||
{t('stop_on_validation_error')}
|
||||
</MenuItem>
|
||||
<MenuItem onSelect={handleSelectSyntaxCheckOff}>
|
||||
<i className={iconClasses.syntaxCheckOff} aria-hidden="true" />
|
||||
{t('ignore_validation_errors')}
|
||||
</MenuItem>
|
||||
<MenuItem onSelect={onRunSyntaxCheckNow}>
|
||||
<i className="fa fa-fw" aria-hidden="true" />
|
||||
{t('run_syntax_check_now')}
|
||||
</MenuItem>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
|
||||
PreviewRecompileButton.propTypes = {
|
||||
compilerState: PropTypes.shape({
|
||||
isAutoCompileOn: PropTypes.bool.isRequired,
|
||||
isCompiling: PropTypes.bool.isRequired,
|
||||
isDraftModeOn: PropTypes.bool.isRequired,
|
||||
isSyntaxCheckOn: PropTypes.bool.isRequired
|
||||
}),
|
||||
onRecompile: PropTypes.func.isRequired,
|
||||
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
||||
onSetAutoCompile: PropTypes.func.isRequired,
|
||||
onSetDraftMode: PropTypes.func.isRequired,
|
||||
onSetSyntaxCheck: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default PreviewRecompileButton
|
||||
@@ -0,0 +1,46 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import PreviewRecompileButton from './preview-recompile-button'
|
||||
|
||||
function PreviewToolbar({
|
||||
compilerState,
|
||||
onRecompile,
|
||||
onRunSyntaxCheckNow,
|
||||
onSetAutoCompile,
|
||||
onSetDraftMode,
|
||||
onSetSyntaxCheck,
|
||||
onToggleLogs
|
||||
}) {
|
||||
return (
|
||||
<div className="toolbar toolbar-pdf">
|
||||
<PreviewRecompileButton
|
||||
compilerState={compilerState}
|
||||
onRecompile={onRecompile}
|
||||
onRunSyntaxCheckNow={onRunSyntaxCheckNow}
|
||||
onSetAutoCompile={onSetAutoCompile}
|
||||
onSetDraftMode={onSetDraftMode}
|
||||
onSetSyntaxCheck={onSetSyntaxCheck}
|
||||
/>
|
||||
<button className="btn btn-sm btn-secondary" onClick={onToggleLogs}>
|
||||
Toggle logs
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PreviewToolbar.propTypes = {
|
||||
compilerState: PropTypes.shape({
|
||||
isAutoCompileOn: PropTypes.bool.isRequired,
|
||||
isCompiling: PropTypes.bool.isRequired,
|
||||
isDraftModeOn: PropTypes.bool.isRequired,
|
||||
isSyntaxCheckOn: PropTypes.bool.isRequired
|
||||
}),
|
||||
onRecompile: PropTypes.func.isRequired,
|
||||
onRunSyntaxCheckNow: PropTypes.func.isRequired,
|
||||
onSetAutoCompile: PropTypes.func.isRequired,
|
||||
onSetDraftMode: PropTypes.func.isRequired,
|
||||
onSetSyntaxCheck: PropTypes.func.isRequired,
|
||||
onToggleLogs: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default PreviewToolbar
|
||||
@@ -11,6 +11,7 @@
|
||||
import './controllers/PdfController'
|
||||
import './controllers/PdfViewToggleController'
|
||||
import '../pdfng/directives/pdfJs'
|
||||
|
||||
let PdfManager
|
||||
|
||||
export default (PdfManager = class PdfManager {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import App from '../../../base'
|
||||
import HumanReadableLogs from '../../human-readable-logs/HumanReadableLogs'
|
||||
import BibLogParser from 'libs/bib-log-parser'
|
||||
import PreviewPane from '../../../features/preview/components/preview-pane'
|
||||
import { react2angular } from 'react2angular'
|
||||
import 'ace/ace'
|
||||
const AUTO_COMPILE_MAX_WAIT = 5000
|
||||
// We add a 1 second debounce to sending user changes to server if they aren't
|
||||
@@ -807,10 +809,12 @@ App.controller('PdfController', function(
|
||||
}
|
||||
|
||||
$scope.toggleLogs = function() {
|
||||
$scope.shouldShowLogs = !$scope.shouldShowLogs
|
||||
if ($scope.shouldShowLogs) {
|
||||
eventTracking.sendMBOnce('ide-open-logs-once')
|
||||
}
|
||||
$scope.$applyAsync(() => {
|
||||
$scope.shouldShowLogs = !$scope.shouldShowLogs
|
||||
if ($scope.shouldShowLogs) {
|
||||
eventTracking.sendMBOnce('ide-open-logs-once')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$scope.showPdf = function() {
|
||||
@@ -839,6 +843,27 @@ App.controller('PdfController', function(
|
||||
ide.editorManager.openDoc(doc, { gotoLine: line })
|
||||
})
|
||||
}
|
||||
|
||||
$scope.setAutoCompile = function(isOn) {
|
||||
$scope.$applyAsync(function() {
|
||||
$scope.autocompile_enabled = isOn
|
||||
})
|
||||
}
|
||||
$scope.setDraftMode = function(isOn) {
|
||||
$scope.$applyAsync(function() {
|
||||
$scope.draft = isOn
|
||||
})
|
||||
}
|
||||
$scope.setSyntaxCheck = function(isOn) {
|
||||
$scope.$applyAsync(function() {
|
||||
$scope.stop_on_validation_error = isOn
|
||||
})
|
||||
}
|
||||
$scope.runSyntaxCheckNow = function() {
|
||||
$scope.$applyAsync(function() {
|
||||
$scope.recompile({ check: true })
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
App.factory('synctex', function(ide, $http, $q) {
|
||||
@@ -1043,3 +1068,6 @@ App.controller('ClearCacheModalController', function($scope, $modalInstance) {
|
||||
|
||||
$scope.cancel = () => $modalInstance.dismiss('cancel')
|
||||
})
|
||||
|
||||
// Wrap React component as Angular component. Only needed for "top-level" component
|
||||
App.component('previewPane', react2angular(PreviewPane))
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.btn-recompile-label {
|
||||
margin-left: @line-height-computed / 4;
|
||||
}
|
||||
|
||||
.pdf-viewer {
|
||||
iframe {
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user