mirror of
https://github.com/overleaf/overleaf.git
synced 2024-10-24 21:12:38 -04:00
[web] Migrate PDF Logs to BS5 (#21062)
* [web] Migrate Logs components JSX to Bootstrap 5 * [web] Migrate logs.less to logs.scss * [web] Remove unused class names * [storybook] Define default Bootstrap version in Storybook This prevents some warning in the console * [storybook] Update pdf-preview.stories.jsx * [storybook] Add pdf-log-entry.stories.tsx * [storybook] Force re-renders when switching BS version * [web] Keep files dropdown menu in bounds * [web] Make files dropdown items not bold in BS5 * [web] Revert unrelated change * [web] Fixup PreviewLogsPaneMaxEntries * [web] Add style for log-entry-content-link * [web] Replace log-entry by OLNotification in `PdfCodeCheckFailedNotice` * [web] Use `BootstrapVersionSwitcher` instead of `isBootstrap5` * [web] Rename `DropdownBS3` to `BS3Dropdown` * [web] Reuse variables for `toolbar-height` and `toolbar-small-height` * [web] Set `id` on `DropdownToggle` not `Dropdown` * [web] Set `log-entry-btn-expand-collapse` in BS3 only * [web] Remove `block: true` from StartFreeTrialButton in BS3 * [web] Remove unnecessary CSS in `.log-entry-header-link` * [web] Use semantic color names * Migrate the downloadable pdf file list to Bootstrap 5 * Remove nested BootstrapVersionSwitcher, fix "key" prop * Update roles to: `<li role="menuitem">` `<a role="link">` * Update `log-entry-header-link`: variant ghost and fix colors --------- Co-authored-by: Rebeka <o.dekany@gmail.com> GitOrigin-RevId: 89848970ab5d8a8c135335386caf24363f69a34c
This commit is contained in:
parent
d77ca18b0b
commit
30860ae9f9
22 changed files with 578 additions and 239 deletions
|
@ -177,7 +177,11 @@ const preview: Preview = {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{activeStyle && <style>{activeStyle.default}</style>}
|
{activeStyle && <style>{activeStyle.default}</style>}
|
||||||
<Story {...context} />
|
<Story
|
||||||
|
{...context}
|
||||||
|
// force re-renders when switching between Bootstrap versions
|
||||||
|
key={bootstrapVersion}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,6 +9,9 @@ export const bsVersionDecorator: Meta = {
|
||||||
description: 'Bootstrap version for components',
|
description: 'Bootstrap version for components',
|
||||||
control: { type: 'inline-radio' },
|
control: { type: 'inline-radio' },
|
||||||
options: ['3', '5'],
|
options: ['3', '5'],
|
||||||
|
table: {
|
||||||
|
defaultValue: { summary: '3' },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import { Button } from 'react-bootstrap'
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
||||||
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||||
|
|
||||||
function PdfClearCacheButton() {
|
function PdfClearCacheButton() {
|
||||||
const { compiling, clearCache, clearingCache } = useCompileContext()
|
const { compiling, clearCache, clearingCache } = useCompileContext()
|
||||||
|
@ -10,17 +11,29 @@ function PdfClearCacheButton() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<OLButton
|
||||||
bsSize="small"
|
size="sm"
|
||||||
bsStyle="danger"
|
variant="danger"
|
||||||
className="logs-pane-actions-clear-cache"
|
className="logs-pane-actions-clear-cache"
|
||||||
onClick={() => clearCache()}
|
onClick={() => clearCache()}
|
||||||
|
isLoading={clearingCache}
|
||||||
disabled={clearingCache || compiling}
|
disabled={clearingCache || compiling}
|
||||||
|
leadingIcon="delete"
|
||||||
>
|
>
|
||||||
{clearingCache ? <Icon type="refresh" spin /> : <Icon type="trash-o" />}
|
<BootstrapVersionSwitcher
|
||||||
|
bs3={
|
||||||
|
<>
|
||||||
|
{clearingCache ? (
|
||||||
|
<Icon type="refresh" spin />
|
||||||
|
) : (
|
||||||
|
<Icon type="trash-o" />
|
||||||
|
)}
|
||||||
|
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<span>{t('clear_cached_files')}</span>
|
<span>{t('clear_cached_files')}</span>
|
||||||
</Button>
|
</OLButton>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,29 @@
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
|
import OLNotification from '@/features/ui/components/ol/ol-notification'
|
||||||
|
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||||
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||||
|
|
||||||
function PdfCodeCheckFailedNotice() {
|
function PdfCodeCheckFailedNotice() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="log-entry">
|
<OLNotification
|
||||||
<div className="log-entry-header log-entry-header-error">
|
type="error"
|
||||||
<div className="log-entry-header-icon-container">
|
content={
|
||||||
<Icon type="exclamation-triangle" fw />
|
<>
|
||||||
</div>
|
<BootstrapVersionSwitcher
|
||||||
<h3 className="log-entry-header-title">
|
bs3={
|
||||||
|
<>
|
||||||
|
<Icon type="exclamation-triangle" fw />{' '}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
{t('code_check_failed_explanation')}
|
{t('code_check_failed_explanation')}
|
||||||
</h3>
|
</>
|
||||||
</div>
|
}
|
||||||
</div>
|
className={bsVersion({ bs5: 'm-0', bs3: 'mb-2' })}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
import { Dropdown } from 'react-bootstrap'
|
import { Dropdown as BS3Dropdown } from 'react-bootstrap'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dropdown,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownToggle,
|
||||||
|
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||||
import PdfFileList from './pdf-file-list'
|
import PdfFileList from './pdf-file-list'
|
||||||
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
||||||
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||||
|
|
||||||
function PdfDownloadFilesButton() {
|
function PdfDownloadFilesButton() {
|
||||||
const { compiling, fileList } = useCompileContext()
|
const { compiling, fileList } = useCompileContext()
|
||||||
|
@ -11,22 +18,42 @@ function PdfDownloadFilesButton() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ControlledDropdown
|
<BootstrapVersionSwitcher
|
||||||
id="dropdown-files-logs-pane"
|
bs3={
|
||||||
dropup
|
<ControlledDropdown
|
||||||
pullRight
|
id="dropdown-files-logs-pane"
|
||||||
disabled={compiling || !fileList}
|
dropup
|
||||||
>
|
pullRight
|
||||||
<Dropdown.Toggle
|
disabled={compiling || !fileList}
|
||||||
className="dropdown-toggle btn-secondary-info btn-secondary"
|
>
|
||||||
title={t('other_logs_and_files')}
|
<BS3Dropdown.Toggle
|
||||||
bsSize="small"
|
className="dropdown-toggle btn-secondary-info btn-secondary"
|
||||||
bsStyle={null}
|
title={t('other_logs_and_files')}
|
||||||
/>
|
bsSize="small"
|
||||||
<Dropdown.Menu id="dropdown-files-logs-pane-list">
|
bsStyle={null}
|
||||||
<PdfFileList fileList={fileList} />
|
/>
|
||||||
</Dropdown.Menu>
|
<BS3Dropdown.Menu id="dropdown-files-logs-pane-list">
|
||||||
</ControlledDropdown>
|
<PdfFileList fileList={fileList} />
|
||||||
|
</BS3Dropdown.Menu>
|
||||||
|
</ControlledDropdown>
|
||||||
|
}
|
||||||
|
bs5={
|
||||||
|
<Dropdown drop="up">
|
||||||
|
<DropdownToggle
|
||||||
|
id="dropdown-files-logs-pane"
|
||||||
|
variant="secondary"
|
||||||
|
title={t('other_logs_and_files')}
|
||||||
|
size="sm"
|
||||||
|
disabled={compiling || !fileList}
|
||||||
|
>
|
||||||
|
{t('other_logs_and_files')}
|
||||||
|
</DropdownToggle>
|
||||||
|
<DropdownMenu id="dropdown-files-logs-pane-list">
|
||||||
|
<PdfFileList fileList={fileList} />
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
import { MenuItem } from 'react-bootstrap'
|
import { MenuItem as BS3MenuItem } from 'react-bootstrap'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||||
|
import {
|
||||||
|
DropdownDivider,
|
||||||
|
DropdownHeader,
|
||||||
|
DropdownItem,
|
||||||
|
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||||
|
|
||||||
function PdfFileList({ fileList }) {
|
function PdfFileList({ fileList }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
@ -15,37 +21,93 @@ function PdfFileList({ fileList }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<BootstrapVersionSwitcher
|
||||||
<MenuItem header>{t('other_output_files')}</MenuItem>
|
bs3={
|
||||||
|
<>
|
||||||
|
<BS3MenuItem header>{t('other_output_files')}</BS3MenuItem>
|
||||||
|
|
||||||
{fileList.top.map(file => (
|
{fileList.top.map(file => (
|
||||||
<MenuItem download={basename(file)} href={file.url} key={file.path}>
|
<BS3MenuItem
|
||||||
<b>{file.path}</b>
|
download={basename(file)}
|
||||||
</MenuItem>
|
href={file.url}
|
||||||
))}
|
key={file.path}
|
||||||
|
>
|
||||||
|
<b>{file.path}</b>
|
||||||
|
</BS3MenuItem>
|
||||||
|
))}
|
||||||
|
|
||||||
{fileList.other.length > 0 && fileList.top.length > 0 && (
|
{fileList.other.length > 0 && fileList.top.length > 0 && (
|
||||||
<MenuItem divider />
|
<BS3MenuItem divider />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{fileList.other.map(file => (
|
{fileList.other.map(file => (
|
||||||
<MenuItem download={basename(file)} href={file.url} key={file.path}>
|
<BS3MenuItem
|
||||||
<b>{file.path}</b>
|
download={basename(file)}
|
||||||
</MenuItem>
|
href={file.url}
|
||||||
))}
|
key={file.path}
|
||||||
|
>
|
||||||
|
<b>{file.path}</b>
|
||||||
|
</BS3MenuItem>
|
||||||
|
))}
|
||||||
|
|
||||||
{fileList.archive?.fileCount > 0 && (
|
{fileList.archive?.fileCount && fileList.archive?.fileCount > 0 && (
|
||||||
<MenuItem
|
<BS3MenuItem
|
||||||
download={basename(fileList.archive)}
|
download={basename(fileList.archive)}
|
||||||
href={fileList.archive.url}
|
href={fileList.archive.url}
|
||||||
key={fileList.archive.path}
|
>
|
||||||
>
|
<b>
|
||||||
<b>
|
{t('download_all')} ({fileList.archive.fileCount})
|
||||||
{t('download_all')} ({fileList.archive.fileCount})
|
</b>
|
||||||
</b>
|
</BS3MenuItem>
|
||||||
</MenuItem>
|
)}
|
||||||
)}
|
</>
|
||||||
</>
|
}
|
||||||
|
bs5={
|
||||||
|
<>
|
||||||
|
<DropdownHeader>{t('other_output_files')}</DropdownHeader>
|
||||||
|
|
||||||
|
{fileList.top.map(file => (
|
||||||
|
<li key={file.path} role="menuitem">
|
||||||
|
<DropdownItem
|
||||||
|
role="link"
|
||||||
|
download={basename(file)}
|
||||||
|
href={file.url}
|
||||||
|
>
|
||||||
|
{file.path}
|
||||||
|
</DropdownItem>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{fileList.other.length > 0 && fileList.top.length > 0 && (
|
||||||
|
<DropdownDivider divider />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{fileList.other.map(file => (
|
||||||
|
<li key={file.path} role="menuitem">
|
||||||
|
<DropdownItem
|
||||||
|
role="link"
|
||||||
|
download={basename(file)}
|
||||||
|
href={file.url}
|
||||||
|
>
|
||||||
|
{file.path}
|
||||||
|
</DropdownItem>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{fileList.archive?.fileCount > 0 && (
|
||||||
|
<li role="menuitem">
|
||||||
|
<DropdownItem
|
||||||
|
role="link"
|
||||||
|
download={basename(fileList.archive)}
|
||||||
|
href={fileList.archive.url}
|
||||||
|
>
|
||||||
|
{t('download_all')} ({fileList.archive.fileCount})
|
||||||
|
</DropdownItem>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useCallback, useState } from 'react'
|
||||||
import { useResizeObserver } from '../../../shared/hooks/use-resize-observer'
|
import { useResizeObserver } from '../../../shared/hooks/use-resize-observer'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { Button } from 'react-bootstrap'
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
@ -44,10 +44,13 @@ export default function PdfLogEntryRawContent({
|
||||||
'log-entry-content-button-container-collapsed': !expanded,
|
'log-entry-content-button-container-collapsed': !expanded,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Button
|
<OLButton
|
||||||
bsSize="xs"
|
variant="secondary"
|
||||||
bsStyle={null}
|
size="sm"
|
||||||
className="log-entry-btn-expand-collapse btn-secondary"
|
bs3Props={{
|
||||||
|
bsSize: 'xsmall',
|
||||||
|
className: 'log-entry-btn-expand-collapse',
|
||||||
|
}}
|
||||||
onClick={() => setExpanded(value => !value)}
|
onClick={() => setExpanded(value => !value)}
|
||||||
>
|
>
|
||||||
{expanded ? (
|
{expanded ? (
|
||||||
|
@ -59,7 +62,7 @@ export default function PdfLogEntryRawContent({
|
||||||
<Icon type="angle-down" /> {t('expand')}
|
<Icon type="angle-down" /> {t('expand')}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</OLButton>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { useTranslation, Trans } from 'react-i18next'
|
import { useTranslation, Trans } from 'react-i18next'
|
||||||
import { memo, useCallback } from 'react'
|
import { memo, useCallback } from 'react'
|
||||||
import { Button } from 'react-bootstrap'
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||||
import PdfLogEntry from './pdf-log-entry'
|
import PdfLogEntry from './pdf-log-entry'
|
||||||
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
||||||
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
||||||
|
@ -23,9 +23,10 @@ function PdfPreviewError({ error }) {
|
||||||
i18nKey="something_went_wrong_rendering_pdf_expected"
|
i18nKey="something_went_wrong_rendering_pdf_expected"
|
||||||
components={[
|
components={[
|
||||||
// eslint-disable-next-line react/jsx-key
|
// eslint-disable-next-line react/jsx-key
|
||||||
<Button
|
<OLButton
|
||||||
bsSize="xs"
|
variant="info"
|
||||||
bsStyle="info"
|
size="sm"
|
||||||
|
bs3Props={{ bsSize: 'xsmall' }}
|
||||||
onClick={() => startCompile()}
|
onClick={() => startCompile()}
|
||||||
/>,
|
/>,
|
||||||
]}
|
]}
|
||||||
|
@ -157,7 +158,7 @@ function PdfPreviewError({ error }) {
|
||||||
<ErrorLogEntry title={t('no_pdf_error_title')}>
|
<ErrorLogEntry title={t('no_pdf_error_title')}>
|
||||||
{t('no_pdf_error_explanation')}
|
{t('no_pdf_error_explanation')}
|
||||||
|
|
||||||
<ul className="log-entry-formatted-content-list">
|
<ul className="my-1 ps-3">
|
||||||
<li>{t('no_pdf_error_reason_unrecoverable_error')}</li>
|
<li>{t('no_pdf_error_reason_unrecoverable_error')}</li>
|
||||||
<li>
|
<li>
|
||||||
<Trans
|
<Trans
|
||||||
|
@ -282,9 +283,10 @@ function TimedOutLogEntry() {
|
||||||
i18nKey="project_timed_out_enable_stop_on_first_error"
|
i18nKey="project_timed_out_enable_stop_on_first_error"
|
||||||
components={[
|
components={[
|
||||||
// eslint-disable-next-line react/jsx-key
|
// eslint-disable-next-line react/jsx-key
|
||||||
<Button
|
<OLButton
|
||||||
bsSize="xs"
|
variant="info"
|
||||||
bsStyle="info"
|
size="sm"
|
||||||
|
bs3Props={{ bsSize: 'xsmall' }}
|
||||||
onClick={handleEnableStopOnFirstErrorClick}
|
onClick={handleEnableStopOnFirstErrorClick}
|
||||||
/>,
|
/>,
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useTranslation, Trans } from 'react-i18next'
|
import { useTranslation, Trans } from 'react-i18next'
|
||||||
import { Button } from 'react-bootstrap'
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||||
import PdfLogEntry from './pdf-log-entry'
|
import PdfLogEntry from './pdf-log-entry'
|
||||||
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
||||||
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
||||||
|
@ -28,9 +28,14 @@ export default function StopOnFirstErrorPrompt() {
|
||||||
// eslint-disable-next-line react/jsx-key
|
// eslint-disable-next-line react/jsx-key
|
||||||
components={[<strong />]}
|
components={[<strong />]}
|
||||||
/>{' '}
|
/>{' '}
|
||||||
<Button bsSize="xs" bsStyle="info" onClick={handleDisableButtonClick}>
|
<OLButton
|
||||||
|
variant="info"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleDisableButtonClick}
|
||||||
|
bs3Props={{ bsSize: 'xsmall' }}
|
||||||
|
>
|
||||||
{t('disable_stop_on_first_error')}
|
{t('disable_stop_on_first_error')}
|
||||||
</Button>
|
</OLButton>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
level="info"
|
level="info"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import StartFreeTrialButton from '../../../shared/components/start-free-trial-bu
|
||||||
import { memo, useCallback } from 'react'
|
import { memo, useCallback } from 'react'
|
||||||
import PdfLogEntry from './pdf-log-entry'
|
import PdfLogEntry from './pdf-log-entry'
|
||||||
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
||||||
import { Button } from 'react-bootstrap'
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||||
import * as eventTracking from '../../../infrastructure/event-tracking'
|
import * as eventTracking from '../../../infrastructure/event-tracking'
|
||||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||||
import getMeta from '@/utils/meta'
|
import getMeta from '@/utils/meta'
|
||||||
|
@ -84,13 +84,7 @@ const CompileTimeout = memo(function CompileTimeout({
|
||||||
<p className="text-center">
|
<p className="text-center">
|
||||||
<StartFreeTrialButton
|
<StartFreeTrialButton
|
||||||
source="compile-timeout"
|
source="compile-timeout"
|
||||||
buttonProps={{
|
buttonProps={{ variant: 'primary', className: 'w-100' }}
|
||||||
variant: 'primary',
|
|
||||||
className: 'row-spaced-small',
|
|
||||||
bs3Props: {
|
|
||||||
block: true,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{hasNewPaywallCta
|
{hasNewPaywallCta
|
||||||
? t('get_more_compile_time')
|
? t('get_more_compile_time')
|
||||||
|
@ -178,10 +172,12 @@ const PreventTimeoutHelpMessage = memo(function PreventTimeoutHelpMessage({
|
||||||
i18nKey="enable_stop_on_first_error_under_recompile_dropdown_menu"
|
i18nKey="enable_stop_on_first_error_under_recompile_dropdown_menu"
|
||||||
components={[
|
components={[
|
||||||
// eslint-disable-next-line react/jsx-key
|
// eslint-disable-next-line react/jsx-key
|
||||||
<Button
|
<OLButton
|
||||||
bsSize="xs"
|
variant="link"
|
||||||
bsStyle="info-ghost-inline"
|
className="btn-inline-link fw-bold"
|
||||||
|
size="sm"
|
||||||
onClick={handleEnableStopOnFirstErrorClick}
|
onClick={handleEnableStopOnFirstErrorClick}
|
||||||
|
bs3Props={{ bsSize: 'xsmall' }}
|
||||||
/>,
|
/>,
|
||||||
// eslint-disable-next-line react/jsx-key
|
// eslint-disable-next-line react/jsx-key
|
||||||
<strong />,
|
<strong />,
|
||||||
|
|
|
@ -3,8 +3,11 @@ import classNames from 'classnames'
|
||||||
import { useState, useRef } from 'react'
|
import { useState, useRef } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import useResizeObserver from '../hooks/use-resize-observer'
|
import useResizeObserver from '../hooks/use-resize-observer'
|
||||||
import Tooltip from '../../../shared/components/tooltip'
|
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||||
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||||
|
import MaterialIcon from '@/shared/components/material-icon'
|
||||||
|
|
||||||
function PreviewLogEntryHeader({
|
function PreviewLogEntryHeader({
|
||||||
sourceLocation,
|
sourceLocation,
|
||||||
|
@ -72,18 +75,25 @@ function PreviewLogEntryHeader({
|
||||||
// This essentially tells the browser that, althought the text is laid out from right-to-left,
|
// This essentially tells the browser that, althought the text is laid out from right-to-left,
|
||||||
// the wrapped portion of text should follow left-to-right writing rules.
|
// the wrapped portion of text should follow left-to-right writing rules.
|
||||||
const locationLink = locationLinkText ? (
|
const locationLink = locationLinkText ? (
|
||||||
<button
|
<OLButton
|
||||||
|
variant="ghost"
|
||||||
className={logEntryLocationBtnClasses}
|
className={logEntryLocationBtnClasses}
|
||||||
type="button"
|
|
||||||
aria-label={headerLogLocationTitle}
|
aria-label={headerLogLocationTitle}
|
||||||
onClick={onSourceLocationClick}
|
onClick={onSourceLocationClick}
|
||||||
>
|
>
|
||||||
<Icon type="chain" />
|
<BootstrapVersionSwitcher
|
||||||
|
bs3={
|
||||||
|
<>
|
||||||
|
<Icon type="chain" />
|
||||||
|
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
bs5={<MaterialIcon type="link" />}
|
||||||
|
/>
|
||||||
<span ref={logLocationSpanRef} className="log-entry-header-link-location">
|
<span ref={logLocationSpanRef} className="log-entry-header-link-location">
|
||||||
{`\u202A${locationLinkText}\u202C`}
|
{`\u202A${locationLinkText}\u202C`}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</OLButton>
|
||||||
) : null
|
) : null
|
||||||
|
|
||||||
const headerTitleText = logType ? `${logType} ${headerTitle}` : headerTitle
|
const headerTitleText = logType ? `${logType} ${headerTitle}` : headerTitle
|
||||||
|
@ -95,26 +105,26 @@ function PreviewLogEntryHeader({
|
||||||
) : null}
|
) : null}
|
||||||
<h3 className="log-entry-header-title">{headerTitleText}</h3>
|
<h3 className="log-entry-header-title">{headerTitleText}</h3>
|
||||||
{locationSpanOverflown && locationLinkText ? (
|
{locationSpanOverflown && locationLinkText ? (
|
||||||
<Tooltip
|
<OLTooltip
|
||||||
id={locationLinkText}
|
id={locationLinkText}
|
||||||
description={locationLinkText}
|
description={locationLinkText}
|
||||||
overlayProps={{ placement: 'left' }}
|
overlayProps={{ placement: 'left' }}
|
||||||
tooltipProps={{ className: 'log-location-tooltip' }}
|
tooltipProps={{ className: 'log-location-tooltip' }}
|
||||||
>
|
>
|
||||||
{locationLink}
|
{locationLink}
|
||||||
</Tooltip>
|
</OLTooltip>
|
||||||
) : (
|
) : (
|
||||||
locationLink
|
locationLink
|
||||||
)}
|
)}
|
||||||
{showCloseButton ? (
|
{showCloseButton ? (
|
||||||
<button
|
<OLButton
|
||||||
|
variant="link"
|
||||||
className="btn-inline-link log-entry-header-link"
|
className="btn-inline-link log-entry-header-link"
|
||||||
type="button"
|
|
||||||
aria-label={t('dismiss_error_popup')}
|
aria-label={t('dismiss_error_popup')}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</OLButton>
|
||||||
) : null}
|
) : null}
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
import { Button } from 'react-bootstrap'
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||||
import PreviewLogEntryHeader from './preview-log-entry-header'
|
import PreviewLogEntryHeader from './preview-log-entry-header'
|
||||||
import Icon from '../../../shared/components/icon'
|
import Icon from '../../../shared/components/icon'
|
||||||
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
||||||
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
||||||
|
import MaterialIcon from '@/shared/components/material-icon'
|
||||||
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||||
|
|
||||||
function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) {
|
function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
@ -34,16 +36,22 @@ function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) {
|
||||||
{hasErrors && !stoppedOnFirstError ? (
|
{hasErrors && !stoppedOnFirstError ? (
|
||||||
<>
|
<>
|
||||||
<p>
|
<p>
|
||||||
<Icon type="lightbulb-o" />
|
<BootstrapVersionSwitcher
|
||||||
|
bs3={<Icon type="lightbulb-o" />}
|
||||||
|
bs5={
|
||||||
|
<MaterialIcon type="lightbulb" className="align-middle" />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<strong>{t('tip')}: </strong>
|
<strong>{t('tip')}: </strong>
|
||||||
<Trans
|
<Trans
|
||||||
i18nKey="log_entry_maximum_entries_enable_stop_on_first_error"
|
i18nKey="log_entry_maximum_entries_enable_stop_on_first_error"
|
||||||
components={[
|
components={[
|
||||||
<Button
|
<OLButton
|
||||||
|
variant="info"
|
||||||
|
size="sm"
|
||||||
key="enable-stop-on-first-error"
|
key="enable-stop-on-first-error"
|
||||||
bsSize="xs"
|
bs3Props={{ bsSize: 'xsmall' }}
|
||||||
bsStyle="info"
|
|
||||||
onClick={handleEnableStopOnFirstErrorClick}
|
onClick={handleEnableStopOnFirstErrorClick}
|
||||||
/>,
|
/>,
|
||||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||||
|
@ -58,7 +66,10 @@ function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) {
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p>
|
<p>
|
||||||
<Icon type="lightbulb-o" />
|
<BootstrapVersionSwitcher
|
||||||
|
bs3={<Icon type="lightbulb-o" />}
|
||||||
|
bs5={<MaterialIcon type="lightbulb" className="align-middle" />}
|
||||||
|
/>
|
||||||
|
|
||||||
<strong>{t('tip')}: </strong>
|
<strong>{t('tip')}: </strong>
|
||||||
{t('log_entry_maximum_entries_see_full_logs')}
|
{t('log_entry_maximum_entries_see_full_logs')}
|
||||||
|
|
|
@ -508,6 +508,8 @@ const hints = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ruleIds = Object.keys(hints)
|
||||||
|
|
||||||
if (!getMeta('ol-wikiEnabled')) {
|
if (!getMeta('ol-wikiEnabled')) {
|
||||||
Object.keys(hints).forEach(ruleId => {
|
Object.keys(hints).forEach(ruleId => {
|
||||||
hints[ruleId].extraInfoURL = null
|
hints[ruleId].extraInfoURL = null
|
||||||
|
|
75
services/web/frontend/stories/pdf-log-entry.stories.tsx
Normal file
75
services/web/frontend/stories/pdf-log-entry.stories.tsx
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import PdfLogEntry from '@/features/pdf-preview/components/pdf-log-entry'
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react'
|
||||||
|
import { bsVersionDecorator } from '../../.storybook/utils/with-bootstrap-switcher'
|
||||||
|
import { ruleIds } from '@/ide/human-readable-logs/HumanReadableLogsHints'
|
||||||
|
import { ScopeDecorator } from './decorators/scope'
|
||||||
|
import { useMeta } from './hooks/use-meta'
|
||||||
|
import { FC, ReactNode } from 'react'
|
||||||
|
import { useScope } from './hooks/use-scope'
|
||||||
|
import { EditorView } from '@codemirror/view'
|
||||||
|
|
||||||
|
const fakeSourceLocation = {
|
||||||
|
file: 'file.tex',
|
||||||
|
line: 12,
|
||||||
|
column: 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeLogEntry = {
|
||||||
|
key: 'fake',
|
||||||
|
ruleId: 'hint_misplaced_alignment_tab_character',
|
||||||
|
message: 'Fake message',
|
||||||
|
messageComponent: 'Fake message component',
|
||||||
|
content: 'Fake content',
|
||||||
|
type: 'Error: ',
|
||||||
|
level: 'error',
|
||||||
|
contentDetails: ['Fake detail 1', 'Fake detail 2'],
|
||||||
|
file: 'fake.tex',
|
||||||
|
line: 12,
|
||||||
|
column: 5,
|
||||||
|
raw: 'Fake raw',
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeArgs = {
|
||||||
|
headerTitle: 'PDF Preview',
|
||||||
|
formattedContent: 'This is a log entry',
|
||||||
|
level: 'error' as const,
|
||||||
|
extraInfoURL: 'https://example.com',
|
||||||
|
showCloseButton: true,
|
||||||
|
showSourceLocationLink: true,
|
||||||
|
rawContent: 'This is a raw log entry',
|
||||||
|
contentDetails: ['detail 1', 'detail 2'],
|
||||||
|
ruleId: 'hint_misplaced_alignment_tab_character' as const,
|
||||||
|
sourceLocation: fakeSourceLocation,
|
||||||
|
logEntry: fakeLogEntry,
|
||||||
|
logType: 'Fake type',
|
||||||
|
}
|
||||||
|
|
||||||
|
const meta: Meta<typeof PdfLogEntry> = {
|
||||||
|
title: 'Editor / PDF Preview / Logs',
|
||||||
|
component: PdfLogEntry,
|
||||||
|
// @ts-ignore
|
||||||
|
decorators: [ScopeDecorator],
|
||||||
|
argTypes: {
|
||||||
|
ruleId: { control: 'select', options: [...ruleIds, 'other'] },
|
||||||
|
...bsVersionDecorator.argTypes,
|
||||||
|
},
|
||||||
|
args: fakeArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof PdfLogEntry>
|
||||||
|
|
||||||
|
const Provider: FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
useMeta({ 'ol-showAiErrorAssistant': true })
|
||||||
|
useScope({ 'editor.view': new EditorView({ doc: '\\begin{document' }) })
|
||||||
|
return <div className="logs-pane p-2">{children}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PdfLogEntryWithControls: Story = {
|
||||||
|
render: args => (
|
||||||
|
<Provider>
|
||||||
|
<PdfLogEntry {...args} />
|
||||||
|
</Provider>
|
||||||
|
),
|
||||||
|
}
|
|
@ -23,6 +23,11 @@ import { cloneDeep } from 'lodash'
|
||||||
import { ScopeDecorator } from './decorators/scope'
|
import { ScopeDecorator } from './decorators/scope'
|
||||||
import { PdfPreviewProvider } from '@/features/pdf-preview/components/pdf-preview-provider'
|
import { PdfPreviewProvider } from '@/features/pdf-preview/components/pdf-preview-provider'
|
||||||
import { bsVersionDecorator } from '../../.storybook/utils/with-bootstrap-switcher'
|
import { bsVersionDecorator } from '../../.storybook/utils/with-bootstrap-switcher'
|
||||||
|
import {
|
||||||
|
Dropdown,
|
||||||
|
DropdownMenu,
|
||||||
|
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||||
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Editor / PDF Preview',
|
title: 'Editor / PDF Preview',
|
||||||
|
@ -286,7 +291,7 @@ export const DisplayError = () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="logs-pane">
|
||||||
{compileErrors.map(error => (
|
{compileErrors.map(error => (
|
||||||
<div
|
<div
|
||||||
key={error}
|
key={error}
|
||||||
|
@ -296,7 +301,7 @@ export const DisplayError = () => {
|
||||||
<PdfPreviewError error={error} />
|
<PdfPreviewError error={error} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,11 +325,22 @@ export const FileList = () => {
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dropdown open">
|
<BootstrapVersionSwitcher
|
||||||
<div className="dropdown-menu">
|
bs3={
|
||||||
<PdfFileList fileList={fileList} />
|
<div className="dropdown open">
|
||||||
</div>
|
<div className="dropdown-menu">
|
||||||
</div>
|
<PdfFileList fileList={fileList} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
bs5={
|
||||||
|
<Dropdown>
|
||||||
|
<DropdownMenu id="dropdown-files-logs-pane-list" show>
|
||||||
|
<PdfFileList fileList={fileList} />
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,11 +32,6 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-entry-first-error-popup {
|
|
||||||
border-radius: 0;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-entry-header {
|
.log-entry-header {
|
||||||
padding: 3px @padding-sm;
|
padding: 3px @padding-sm;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -182,40 +177,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-entry-formatted-content-list {
|
|
||||||
margin: @margin-xs 0;
|
|
||||||
padding-left: @padding-md;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first-error-popup {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
top: @toolbar-small-height + 2px;
|
|
||||||
right: @padding-xs;
|
|
||||||
width: 90%;
|
|
||||||
max-width: 450px;
|
|
||||||
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
|
|
||||||
animation: fade-in 0.15s linear 0s 1 none;
|
|
||||||
background-color: #fff;
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
.triangle(top, @padding-sm, @padding-xs, @ol-red);
|
|
||||||
top: -@padding-xs;
|
|
||||||
right: @padding-xl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.first-error-popup-actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 @padding-sm @padding-sm @padding-sm;
|
|
||||||
border-radius: 0 0 @border-radius-base @border-radius-base;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first-error-btn {
|
|
||||||
.no-outline-ring-on-click;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-location-tooltip {
|
.log-location-tooltip {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
&.tooltip.in {
|
&.tooltip.in {
|
||||||
|
|
|
@ -113,14 +113,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.pdf-viewer,
|
.pdf-viewer,
|
||||||
.pdf-logs,
|
|
||||||
.pdf-errors,
|
.pdf-errors,
|
||||||
.pdf-uncompiled {
|
.pdf-uncompiled {
|
||||||
.full-size;
|
.full-size;
|
||||||
top: @pdf-top-offset;
|
top: @pdf-top-offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pdf-logs,
|
|
||||||
.pdf-errors,
|
.pdf-errors,
|
||||||
.pdf-uncompiled,
|
.pdf-uncompiled,
|
||||||
.pdf-validation-problems {
|
.pdf-validation-problems {
|
||||||
|
@ -327,72 +325,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pdf-logs {
|
|
||||||
overflow: auto;
|
|
||||||
.alert {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
margin-bottom: @line-height-computed / 2;
|
|
||||||
cursor: pointer;
|
|
||||||
.line-no {
|
|
||||||
float: right;
|
|
||||||
color: @log-line-no-color;
|
|
||||||
font-weight: 700;
|
|
||||||
|
|
||||||
.fa {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.entry-message {
|
|
||||||
font-weight: 700;
|
|
||||||
//font-family: @font-family-monospace;
|
|
||||||
}
|
|
||||||
.entry-content {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
//font-family: @font-family-monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .line-no {
|
|
||||||
color: inherit;
|
|
||||||
.fa {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.alert-danger {
|
|
||||||
background-color: tint(@alert-danger-bg, 15%);
|
|
||||||
&:hover {
|
|
||||||
background-color: @alert-danger-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.alert-warning {
|
|
||||||
background-color: tint(@alert-warning-bg, 15%);
|
|
||||||
&:hover {
|
|
||||||
background-color: @alert-warning-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.alert-info {
|
|
||||||
background-color: tint(@alert-info-bg, 15%);
|
|
||||||
&:hover {
|
|
||||||
background-color: @alert-info-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
font-size: 12px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
.dropdown {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.force-recompile {
|
|
||||||
margin-top: 10px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.synctex-controls {
|
.synctex-controls {
|
||||||
margin-right: -8px;
|
margin-right: -8px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -443,9 +375,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-dark {
|
.editor-dark {
|
||||||
.pdf-logs {
|
|
||||||
background-color: lighten(@editor-dark-background-color, 10%);
|
|
||||||
}
|
|
||||||
.pdfjs-viewer {
|
.pdfjs-viewer {
|
||||||
background-color: lighten(@editor-dark-background-color, 10%);
|
background-color: lighten(@editor-dark-background-color, 10%);
|
||||||
}
|
}
|
||||||
|
@ -604,21 +533,13 @@
|
||||||
|
|
||||||
@editor-and-logs-pane-toolbars-height: @toolbar-small-height + @toolbar-height;
|
@editor-and-logs-pane-toolbars-height: @toolbar-small-height + @toolbar-height;
|
||||||
@btn-small-height: (@padding-small-vertical * 2)+ (@font-size-small *
|
@btn-small-height: (@padding-small-vertical * 2)+ (@font-size-small *
|
||||||
@line-height-small);
|
@line-height-small); // 5px * 2 + 14px * 1.5 = 31px
|
||||||
|
|
||||||
#download-dropdown-list,
|
|
||||||
#dropdown-files-logs-pane-list {
|
#dropdown-files-logs-pane-list {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
.dropdown-header {
|
.dropdown-header {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#download-dropdown-list {
|
|
||||||
max-height: calc(
|
|
||||||
~'100vh - ' @editor-and-logs-pane-toolbars-height ~' - ' @margin-md
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#dropdown-files-logs-pane-list {
|
|
||||||
max-height: calc(
|
max-height: calc(
|
||||||
~'100vh - ' @editor-and-logs-pane-toolbars-height ~' - ' @btn-small-height ~' - '
|
~'100vh - ' @editor-and-logs-pane-toolbars-height ~' - ' @btn-small-height ~' - '
|
||||||
@margin-md
|
@margin-md
|
||||||
|
|
|
@ -3,6 +3,8 @@ $footer-height: 50px;
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
$header-height: 68px;
|
$header-height: 68px;
|
||||||
|
$toolbar-height: 40px;
|
||||||
|
$toolbar-small-height: 32px;
|
||||||
|
|
||||||
// Forms
|
// Forms
|
||||||
$form-group-margin-bottom: $spacing-06;
|
$form-group-margin-bottom: $spacing-06;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
@import 'editor/hotkeys';
|
@import 'editor/hotkeys';
|
||||||
@import 'editor/left-menu';
|
@import 'editor/left-menu';
|
||||||
@import 'editor/loading-screen';
|
@import 'editor/loading-screen';
|
||||||
|
@import 'editor/logs';
|
||||||
@import 'editor/outline';
|
@import 'editor/outline';
|
||||||
@import 'editor/file-tree';
|
@import 'editor/file-tree';
|
||||||
@import 'editor/file-view';
|
@import 'editor/file-view';
|
||||||
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
@import '../../foundations/colors';
|
||||||
|
|
||||||
|
.logs-pane {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: var(--bg-dark-secondary);
|
||||||
|
z-index: 11; // above the PDF viewer + controls
|
||||||
|
top: var(--toolbar-small-height);
|
||||||
|
|
||||||
|
.logs-pane-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px;
|
||||||
|
gap: 10px;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-pane-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
place-content: flex-end flex-end;
|
||||||
|
padding: var(--spacing-03) 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: var(--spacing-04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry {
|
||||||
|
border-radius: var(--border-radius-base);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header {
|
||||||
|
padding: var(--spacing-02) var(--spacing-04);
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--spacing-04);
|
||||||
|
border-radius: var(--border-radius-base) var(--border-radius-base) 0 0;
|
||||||
|
color: var(--content-primary-dark);
|
||||||
|
|
||||||
|
.material-symbols {
|
||||||
|
@include body-base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-error {
|
||||||
|
background-color: var(--content-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-link-error {
|
||||||
|
@include ol-button-variant(
|
||||||
|
$color: var(--content-primary-dark),
|
||||||
|
$background: var(--bg-danger-02),
|
||||||
|
$hover-background: var(--red-70)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-warning {
|
||||||
|
background-color: var(--content-warning-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-link-warning {
|
||||||
|
@include ol-button-variant(
|
||||||
|
$color: var(--content-primary-dark),
|
||||||
|
$background: var(--bg-warning-01),
|
||||||
|
$hover-background: var(--bg-warning-02)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-typesetting {
|
||||||
|
background-color: var(--blue-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-link-typesetting {
|
||||||
|
@include ol-button-variant(
|
||||||
|
$color: var(--content-primary-dark),
|
||||||
|
$background: var(--blue-60),
|
||||||
|
$hover-background: var(--blue-70)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-raw,
|
||||||
|
.log-entry-header-info {
|
||||||
|
background-color: var(--bg-dark-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-link-raw,
|
||||||
|
.log-entry-header-link-info {
|
||||||
|
@include ol-button-variant(
|
||||||
|
$color: var(--content-primary-dark),
|
||||||
|
$background: var(--bg-dark-secondary),
|
||||||
|
$hover-background: var(--bg-dark-primary)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-success {
|
||||||
|
background-color: var(--green-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-link-success {
|
||||||
|
@include ol-button-variant(
|
||||||
|
$color: var(--content-primary-dark),
|
||||||
|
$background: var(--green-60),
|
||||||
|
$hover-background: var(--green-70)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-title {
|
||||||
|
@include body-base;
|
||||||
|
|
||||||
|
flex-grow: 1;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--content-primary-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-link {
|
||||||
|
color: var(--content-primary-dark);
|
||||||
|
border-width: 0;
|
||||||
|
max-width: 33%;
|
||||||
|
text-decoration: none; // needed for the "close button"
|
||||||
|
padding: 0 var(--spacing-03);
|
||||||
|
|
||||||
|
.button-content {
|
||||||
|
min-width: 0; // needed to display the ellipsis on overflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-header-link-location {
|
||||||
|
white-space: nowrap;
|
||||||
|
direction: rtl;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-content {
|
||||||
|
background-color: var(--bg-light-primary);
|
||||||
|
padding: var(--spacing-04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-content-raw {
|
||||||
|
font-size: var(--font-size-01);
|
||||||
|
color: var(--content-secondary);
|
||||||
|
padding: var(--spacing-03);
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-content-button-container {
|
||||||
|
position: relative;
|
||||||
|
height: 40px;
|
||||||
|
margin-top: 0;
|
||||||
|
transition:
|
||||||
|
margin 0.15s ease-in-out,
|
||||||
|
opacity 0.15s ease-in-out;
|
||||||
|
padding-bottom: var(--spacing-04);
|
||||||
|
text-align: center;
|
||||||
|
background-image: linear-gradient(
|
||||||
|
0deg,
|
||||||
|
var(--bg-light-tertiary) 0%,
|
||||||
|
transparent 100%
|
||||||
|
);
|
||||||
|
border-radius: 0 0 var(--border-radius-base) var(--border-radius-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-content-button-container-collapsed {
|
||||||
|
margin-top: -40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-content-raw-container {
|
||||||
|
background-color: var(--bg-light-tertiary);
|
||||||
|
border-radius: var(--border-radius-base);
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: var(--spacing-03);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary-compile-timeout-override {
|
||||||
|
color: var(--content-primary);
|
||||||
|
background-color: var(--bg-light-primary);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry-formatted-content,
|
||||||
|
.log-entry-content-link {
|
||||||
|
font-size: var(--font-size-02);
|
||||||
|
margin-top: var(--spacing-02);
|
||||||
|
|
||||||
|
&:first-of-type {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-location-tooltip {
|
||||||
|
word-break: break-all;
|
||||||
|
|
||||||
|
& > .tooltip-inner {
|
||||||
|
max-width: 450px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
|
@ -136,7 +136,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.pdf-viewer,
|
.pdf-viewer,
|
||||||
.pdf-logs,
|
|
||||||
.pdf-errors,
|
.pdf-errors,
|
||||||
.pdf-uncompiled {
|
.pdf-uncompiled {
|
||||||
@extend .full-size;
|
@extend .full-size;
|
||||||
|
@ -378,3 +377,17 @@
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#dropdown-files-logs-pane-list {
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.dropdown-header {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This keeps the dropdown menu inside the Logs div.
|
||||||
|
// "spacing-11" is there to compensate the button height and the margin around the logs.
|
||||||
|
max-height: calc(
|
||||||
|
100vh - #{$toolbar-small-height + $toolbar-height + $spacing-11}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@import '../../abstracts/variables';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--toolbar-border-color: var(--neutral-80);
|
--toolbar-border-color: var(--neutral-80);
|
||||||
--toolbar-header-bg-color: var(--neutral-90);
|
--toolbar-header-bg-color: var(--neutral-90);
|
||||||
|
@ -7,7 +9,8 @@
|
||||||
--toolbar-btn-hover-color: var(--white);
|
--toolbar-btn-hover-color: var(--white);
|
||||||
--toolbar-btn-active-color: var(--white);
|
--toolbar-btn-active-color: var(--white);
|
||||||
--toolbar-btn-active-bg-color: var(--green-50);
|
--toolbar-btn-active-bg-color: var(--green-50);
|
||||||
--toolbar-small-height: 32px;
|
--toolbar-height: #{$toolbar-height};
|
||||||
|
--toolbar-small-height: #{$toolbar-small-height};
|
||||||
--toolbar-alt-bg-color: var(--neutral-80);
|
--toolbar-alt-bg-color: var(--neutral-80);
|
||||||
--formatting-btn-color: var(--white);
|
--formatting-btn-color: var(--white);
|
||||||
--formatting-btn-bg: var(--neutral-80);
|
--formatting-btn-bg: var(--neutral-80);
|
||||||
|
@ -208,8 +211,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.toolbar-header {
|
&.toolbar-header {
|
||||||
--toolbar-height: 40px;
|
|
||||||
|
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
background-color: var(--toolbar-header-bg-color);
|
background-color: var(--toolbar-header-bg-color);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -315,7 +316,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-editor {
|
.toolbar-editor {
|
||||||
height: 32px;
|
height: var(--toolbar-small-height);
|
||||||
background-color: var(--editor-toolbar-bg);
|
background-color: var(--editor-toolbar-bg);
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
Loading…
Reference in a new issue