12 Commits

Author SHA1 Message Date
jmescuderowritefull
f0f7899de4 Fix duplicated sign in event (#28777)
GitOrigin-RevId: 92e81160b34207a215113a2af93206ca623adb52
2025-10-01 08:06:33 +00:00
Jakob Ackermann
5fe9d3d6e9 [monorepo] migrate monorepo checks to Jenkins (#28765)
GitOrigin-RevId: ebc3db95cebe99226f8de0f66c1830eb5d78e26c
2025-10-01 08:06:28 +00:00
Jakob Ackermann
7488c80b36 [web] bring back initial compile from cache (#28771)
GitOrigin-RevId: 0021eb3a3cb242195369e4f6abf67653e551e362
2025-10-01 08:06:12 +00:00
jmescuderowritefull
360c7c1d33 Clean extension checks and old feature flags in Writefull (#28713)
GitOrigin-RevId: c1949019f1f51add7fd036a1ad0e2946ccab5382
2025-10-01 08:06:07 +00:00
Borja
67f105edcf Change suggestion-click event listener to avoid duplications (#28756)
GitOrigin-RevId: 4feac33ee530f768397a9097e96400ff25d70e5c
2025-10-01 08:06:02 +00:00
Kristina
9e3aba0b6f [web] add translated error messages to Stripe custom checkout (#28730)
GitOrigin-RevId: 06a1e15decdb5f557e68f73cba2ed5ad37fecdac
2025-09-30 08:06:18 +00:00
Eric Mc Sween
414fc3cdb3 Merge pull request #28753 from overleaf/em-debug-revert-file
Log debug info when file not found while reverting

GitOrigin-RevId: 944db8d61a68612325a733151616ff37298dc699
2025-09-30 08:06:13 +00:00
Rebeka Dekany
56c1d38d47 Remove duplicated story (#28751)
GitOrigin-RevId: 85c210b8240c46c08eead1d1b357724e50bcc756
2025-09-30 08:05:58 +00:00
David
1d36f42159 Merge pull request #28684 from overleaf/dp-syntax-checks-casing
Fix casing of "Syntax checks" in compile menu

GitOrigin-RevId: d81f54ff34f9c8a95e679970c1cd16b3dfc355c0
2025-09-30 08:05:53 +00:00
David
1b5887d97f Merge pull request #28675 from overleaf/dp-pdf-preview-output-files-typescript-2
Convert output-files.js to typescript

GitOrigin-RevId: 32eb509f491cfd53de7f1b21b97861ba421566a5
2025-09-30 08:05:45 +00:00
Andrew Rumble
2bf48b3774 Allow fetching csv files by date from GCS
GitOrigin-RevId: 7d2cf4eb6b27a84c6fae86e6ec238432b283abc3
2025-09-30 08:05:32 +00:00
Rebeka Dekany
19b38340ac Consistent usage of the modal header close button (#28681)
* Convert OLModal to named exports only

* Make closeButton the default for OLModalHeader

* Set `closeButton={false}` for modal that is not dismissible

* Fix duplicated imports

* Remove another unnecessary `closeButton` prop

* Fix import

---------

Co-authored-by: Antoine Clausse <antoine.clausse@overleaf.com>
GitOrigin-RevId: ddd7be6e59a966ac634683d2494d6e9d2c3732e6
2025-09-30 08:05:24 +00:00
102 changed files with 319 additions and 412 deletions

1
package-lock.json generated
View File

@@ -52496,6 +52496,7 @@
"@contentful/rich-text-html-renderer": "^16.0.2",
"@contentful/rich-text-types": "^16.0.2",
"@google-cloud/bigquery": "^6.0.1",
"@google-cloud/storage": "^6.10.1",
"@node-oauth/oauth2-server": "^5.1.0",
"@node-saml/passport-saml": "^5.1.0",
"@overleaf/access-token-encryptor": "*",

View File

@@ -43,6 +43,8 @@
"scripts": {
"format": "prettier --list-different $PWD/'**/*.js'",
"format:fix": "prettier --write $PWD/'**/*.js'",
"format:jenkins": "prettier --list-different $PWD/'**/Jenkinsfile'",
"format:jenkins:fix": "prettier --write $PWD/'**/Jenkinsfile'",
"lint": "eslint --max-warnings 0 --format unix .",
"lint:fix": "eslint --fix .",
"postinstall": "patch-package"

View File

@@ -5,8 +5,8 @@
"scripts": {
"cypress:open": "cypress open --e2e --browser chrome",
"cypress:run": "cypress run --e2e --browser chrome",
"format": "prettier --list-different $PWD/'**/{*.{js,mjs,ts,tsx,json},Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.{js,mjs,ts,tsx,json},Jenkinsfile}'"
"format": "prettier --list-different $PWD/'**/*.{js,mjs,ts,tsx,json}'",
"format:fix": "prettier --write $PWD/'**/*.{js,mjs,ts,tsx,json}'"
},
"dependencies": {
"@isomorphic-git/lightning-fs": "^4.6.0",

View File

@@ -12,8 +12,8 @@
"test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js",
"test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js",
"lint": "eslint --max-warnings 0 --format unix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"lint:fix": "eslint --fix .",
"types:check": "tsc --noEmit"
},

View File

@@ -11,8 +11,8 @@
"test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP",
"nodemon": "node --watch app.js",
"lint": "eslint --max-warnings 0 --format unix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"lint:fix": "eslint --fix .",
"types:check": "tsc --noEmit"
},

View File

@@ -12,8 +12,8 @@
"test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP",
"nodemon": "node --watch app.js",
"lint": "eslint --max-warnings 0 --format unix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"lint:fix": "eslint --fix .",
"types:check": "tsc --noEmit"
},

View File

@@ -11,8 +11,8 @@
"test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP",
"nodemon": "node --watch app.js",
"lint": "eslint --max-warnings 0 --format unix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"lint:fix": "eslint --fix .",
"types:check": "tsc --noEmit"
},

View File

@@ -12,8 +12,8 @@
"nodemon": "node --watch app.js",
"benchmark:apply": "node benchmarks/apply",
"lint": "eslint --max-warnings 0 --format unix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"lint:fix": "eslint --fix .",
"types:check": "tsc --noEmit"
},

View File

@@ -11,8 +11,8 @@
"start": "node app.js",
"nodemon": "node --watch app.js",
"lint": "eslint --max-warnings 0 --format unix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js",
"test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js",
"lint:fix": "eslint --fix .",

View File

@@ -62,8 +62,8 @@
"start": "node app.js",
"lint": "eslint --max-warnings 0 --format unix .",
"lint:fix": "eslint --fix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP",
"test:acceptance": "npm run test:acceptance:_run -- --grep=$MOCHA_GREP",
"test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js",

View File

@@ -11,8 +11,8 @@
"test:unit:_run": "vitest --config ./vitest.config.unit.cjs",
"test:unit": "npm run test:unit:_run",
"lint": "eslint --max-warnings 0 --format unix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"lint:fix": "eslint --fix .",
"types:check": "tsc --noEmit",
"nodemon": "node --watch app.js"

View File

@@ -12,8 +12,8 @@
"test:acceptance:_run": "mocha --loader=esmock --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js",
"test:unit:_run": "mocha --loader=esmock --recursive --reporter spec --exit $@ test/unit/js",
"lint": "eslint --max-warnings 0 --format unix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"lint:fix": "eslint --fix .",
"types:check": "tsc --noEmit"
},

View File

@@ -11,8 +11,8 @@
"test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP",
"nodemon": "node --watch app.js",
"lint": "eslint --max-warnings 0 --format unix .",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts,Jenkinsfile}'",
"format": "prettier --list-different $PWD/'**/{*.*js,*.ts}'",
"format:fix": "prettier --write $PWD/'**/{*.*js,*.ts}'",
"lint:fix": "eslint --fix .",
"types:check": "tsc --noEmit"
},

View File

@@ -136,7 +136,11 @@ const RestoreManager = {
const snapshotFile = projectSnapshotAtVersion.getFile(pathname)
if (!snapshotFile) {
throw new OError('file not found in snapshot', { pathname })
throw new OError('file not found in snapshot', {
projectId,
version,
pathname,
})
}
let hadDeletedRootFile = false

View File

@@ -1,6 +1,6 @@
meta(name="ol-project_id" content=project_id)
meta(name="ol-projectName" content=projectName)
meta(name="ol-projectOwnerHasPremiumOnPageLoad" content=projectOwnerHasPremiumOnPageLoad)
meta(name="ol-projectOwnerHasPremiumOnPageLoad" data-type="boolean" content=projectOwnerHasPremiumOnPageLoad)
meta(name="ol-userSettings" data-type="json" content=userSettings)
meta(name="ol-user" data-type="json" content=user)
meta(name="ol-labsExperiments" data-type="json" content=labsExperiments)

View File

@@ -1239,7 +1239,9 @@
"payment_error_3ds_failed": "",
"payment_error_generic": "",
"payment_error_intermittent_error": "",
"payment_error_invalid_payment_method": "",
"payment_error_update_payment_method": "",
"payment_error_update_payment_method_checkout": "",
"payment_provider_unreachable_error": "",
"payment_summary": "",
"pdf": "",
@@ -1779,6 +1781,7 @@
"sync_with_a_github_repository": "",
"synctex_error_recompile_and_try_again": "",
"synctex_failed": "",
"syntax_checks": "",
"syntax_validation": "",
"tab_connecting": "",
"tab_no_longer_connected": "",

View File

@@ -89,7 +89,7 @@ export default function CloneProjectModalContent({
return (
<>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('copy_project')}</OLModalTitle>
</OLModalHeader>

View File

@@ -1,6 +1,6 @@
import React, { memo, useCallback, useState } from 'react'
import CloneProjectModalContent from './clone-project-modal-content'
import OLModal from '@/shared/components/ol/ol-modal'
import { OLModal } from '@/shared/components/ol/ol-modal'
import { ClonedProject } from '../../../../../types/project/dashboard/api'
import { Tag } from '../../../../../app/src/Features/Tags/types'

View File

@@ -52,7 +52,7 @@ export default function DictionaryModalContent({
return (
<>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('edit_dictionary')}</OLModalTitle>
</OLModalHeader>

View File

@@ -1,7 +1,7 @@
import React from 'react'
import DictionaryModalContent from './dictionary-modal-content'
import withErrorBoundary from '../../../infrastructure/error-boundary'
import OLModal from '@/shared/components/ol/ol-modal'
import { OLModal } from '@/shared/components/ol/ol-modal'
type DictionaryModalProps = {
show?: boolean

View File

@@ -3,7 +3,8 @@ import { useFileTreeActionable } from '../../contexts/file-tree-actionable'
import FileTreeCreateFormProvider from '../../contexts/file-tree-create-form'
import FileTreeModalCreateFileBody from '../file-tree-create/file-tree-modal-create-file-body'
import FileTreeModalCreateFileFooter from '../file-tree-create/file-tree-modal-create-file-footer'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -22,7 +23,7 @@ export default function FileTreeModalCreateFile() {
return (
<FileTreeCreateFormProvider>
<OLModal size="lg" onHide={cancel} show>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('add_files')}</OLModalTitle>
</OLModalHeader>

View File

@@ -4,7 +4,8 @@ import { useRefWithAutoFocus } from '../../../../shared/hooks/use-ref-with-auto-
import { useFileTreeActionable } from '../../contexts/file-tree-actionable'
import { DuplicateFilenameError } from '../../errors'
import { isCleanFilename } from '../../util/safe-path'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -1,7 +1,8 @@
import { useTranslation } from 'react-i18next'
import { useFileTreeActionable } from '../../contexts/file-tree-actionable'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -8,7 +8,8 @@ import {
DuplicateFilenameError,
DuplicateFilenameMoveError,
} from '../../errors'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -5,7 +5,8 @@ import { useTranslation } from 'react-i18next'
import { useLocation } from '@/shared/hooks/use-location'
import { FetchError, postJSON } from '@/infrastructure/fetch-json'
import { debugConsole } from '@/utils/debugging'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -5,7 +5,8 @@ import { useTranslation, Trans } from 'react-i18next'
import { useLocation } from '@/shared/hooks/use-location'
import { FetchError, postJSON } from '@/infrastructure/fetch-json'
import { debugConsole } from '@/utils/debugging'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -8,7 +8,8 @@ import NotificationScrolledTo from '@/shared/components/notification-scrolled-to
import { debugConsole } from '@/utils/debugging'
import { GroupUserAlert } from '../../utils/types'
import { useGroupMembersContext } from '../../context/group-members-context'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -3,7 +3,8 @@ import { useEffect, useState } from 'react'
import OLForm from '@/shared/components/ol/ol-form'
import OLFormGroup from '@/shared/components/ol/ol-form-group'
import ModalError from './modal-error'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -1,6 +1,7 @@
import { forwardRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -1,6 +1,7 @@
import { formatTime } from '@/features/utils/format-date'
import { useMemo } from 'react'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -28,7 +29,7 @@ export function RestoreFileConfirmModal({
return (
<OLModal show={show} onHide={onHide}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('restore_file_confirmation_title')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -1,4 +1,5 @@
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -16,7 +17,7 @@ export function RestoreFileErrorModal({
return (
<OLModal show onHide={resetErrorBoundary}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('restore_file_error_title')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>{t('restore_file_error_message')}</OLModalBody>

View File

@@ -1,4 +1,5 @@
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -16,7 +17,7 @@ export function RestoreProjectErrorModal({
return (
<OLModal show onHide={resetErrorBoundary}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>
{t('an_error_occured_while_restoring_project')}
</OLModalTitle>

View File

@@ -1,4 +1,5 @@
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -1,6 +1,7 @@
import { Trans, useTranslation } from 'react-i18next'
import HotkeysModalBottomText from './hotkeys-modal-bottom-text'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -29,7 +30,7 @@ export default function HotkeysModal({
return (
<OLModal size="lg" onHide={handleHide} show={show} animation={animation}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('hotkeys')}</OLModalTitle>
</OLModalHeader>

View File

@@ -1,7 +1,8 @@
import { useTranslation } from 'react-i18next'
import { memo, useEffect, useState } from 'react'
import { useConnectionContext } from '@/features/ide-react/context/connection-context'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalHeader,
OLModalTitle,
@@ -55,7 +56,7 @@ function ForceDisconnected() {
backdrop={false}
keyboard={false}
>
<OLModalHeader>
<OLModalHeader closeButton={false}>
<OLModalTitle>{t('please_wait')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -1,6 +1,7 @@
import { useTranslation } from 'react-i18next'
import { memo } from 'react'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -32,7 +33,7 @@ function GenericConfirmModal({
return (
<OLModal {...modalProps}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{title}</OLModalTitle>
</OLModalHeader>

View File

@@ -1,6 +1,7 @@
import { useTranslation } from 'react-i18next'
import { memo } from 'react'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -25,7 +26,7 @@ function GenericMessageModal({
return (
<OLModal {...modalProps}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{title}</OLModalTitle>
</OLModalHeader>

View File

@@ -2,7 +2,8 @@ import { Trans, useTranslation } from 'react-i18next'
import { memo, useState } from 'react'
import { useLocation } from '@/shared/hooks/use-location'
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -36,7 +37,7 @@ function OutOfSyncModal({ editorContent, show, onHide }: OutOfSyncModalProps) {
backdrop={false}
keyboard={false}
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('out_of_sync')}</OLModalTitle>
</OLModalHeader>
<OLModalBody className="modal-body-share">

View File

@@ -1,4 +1,5 @@
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalHeader,
OLModalTitle,
@@ -37,7 +38,7 @@ const SettingsModal = () => {
: undefined
}
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('settings')}</OLModalTitle>
</OLModalHeader>
<OLModalBody className="ide-settings-modal-body">

View File

@@ -1,6 +1,7 @@
import { useIdeRedesignSwitcherContext } from '@/features/ide-react/context/ide-redesign-switcher-context'
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -50,7 +51,7 @@ export const IdeRedesignIntroModal: FC = () => {
onHide={dismissTutorial}
className="ide-redesign-switcher-modal"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>
{t('the_new_overleaf_editor_try_now_in_beta')}
</OLModalTitle>
@@ -99,7 +100,7 @@ export const IdeRedesignSwitcherModal = () => {
onHide={onHide}
className="ide-redesign-switcher-modal"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>
{enabled
? t('beta_program_the_new_overleaf_editor')

View File

@@ -165,7 +165,7 @@ function PdfCompileButton() {
</DropdownItem>
</li>
<DropdownDivider />
<DropdownHeader>Syntax Checks</DropdownHeader>
<DropdownHeader>{t('syntax_checks')}</DropdownHeader>
<li role="none">
<DropdownItem
as="button"

View File

@@ -1,10 +1,17 @@
import HumanReadableLogs from '../../../ide/human-readable-logs/HumanReadableLogs'
import BibLogParser from '../../../ide/log-parser/bib-log-parser'
import BibLogParser, {
BibLogEntry,
} from '../../../ide/log-parser/bib-log-parser'
import { enablePdfCaching } from './pdf-caching-flags'
import { debugConsole } from '@/utils/debugging'
import { dirname, findEntityByPath } from '@/features/file-tree/util/path'
import '@/utils/readable-stream-async-iterator-polyfill'
import { EDITOR_SESSION_ID } from '@/features/pdf-preview/util/metrics'
import { LogEntry } from './types'
import { CompileResponseData, PDFFile } from '@ol-types/compile'
import { LatexLogEntry } from '@/ide/log-parser/latex-log-parser'
import { Annotation } from '@ol-types/annotation'
import { Folder } from '@ol-types/folder'
// Warnings that may disappear after a second LaTeX pass
const TRANSIENT_WARNING_REGEX = /^(Reference|Citation).+undefined on input line/
@@ -12,7 +19,11 @@ const TRANSIENT_WARNING_REGEX = /^(Reference|Citation).+undefined on input line/
const MAX_LOG_SIZE = 1024 * 1024 // 1MB
const MAX_BIB_LOG_SIZE_PER_FILE = MAX_LOG_SIZE
export function handleOutputFiles(outputFiles, projectId, data) {
export function handleOutputFiles(
outputFiles: Map<string, PDFFile>,
projectId: string,
data: CompileResponseData
): PDFFile | null {
const outputFile = outputFiles.get('output.pdf')
if (!outputFile) return null
@@ -34,10 +45,7 @@ export function handleOutputFiles(outputFiles, projectId, data) {
params.set('enable_pdf_caching', 'true')
}
outputFile.pdfUrl = `${buildURL(
outputFile,
data.pdfDownloadDomain
)}?${params}`
outputFile.pdfUrl = `${buildURL(outputFile, data.pdfDownloadDomain)}?${params}`
if (data.fromCache) {
outputFile.pdfDownloadUrl = outputFile.downloadURL
@@ -53,33 +61,58 @@ export function handleOutputFiles(outputFiles, projectId, data) {
let nextEntryId = 1
function generateEntryKey() {
function generateEntryKey(): string {
return 'compile-log-entry-' + nextEntryId++
}
export const handleLogFiles = async (outputFiles, data, signal) => {
const result = {
type LogResult = {
log: string | null
logEntries: {
errors: LogEntry[]
warnings: LogEntry[]
typesetting: LogEntry[]
all: LogEntry[]
}
}
export async function handleLogFiles(
outputFiles: Map<string, PDFFile>,
data: CompileResponseData,
signal: AbortSignal
): Promise<LogResult> {
const result: LogResult = {
log: null,
logEntries: {
all: [],
errors: [],
warnings: [],
typesetting: [],
},
}
function accumulateResults(newEntries, type) {
for (const key in result.logEntries) {
function accumulateResults(
newEntries: {
errors?: (LatexLogEntry | BibLogEntry)[]
warnings?: (LatexLogEntry | BibLogEntry)[]
typesetting?: (LatexLogEntry | BibLogEntry)[]
all?: (LatexLogEntry | BibLogEntry)[]
},
type?: string
) {
for (const key of Object.keys(result.logEntries) as Array<
keyof typeof result.logEntries
>) {
if (newEntries[key]) {
for (const entry of newEntries[key]) {
if (type) {
entry.type = newEntries.type
// Type casting as we are mutating LatexLogEntry | BibLogEntry into a LogEntry
;(entry as LogEntry).type = type
}
if (entry.file) {
entry.file = normalizeFilePath(entry.file)
}
entry.key = generateEntryKey()
;(entry as LogEntry).key = generateEntryKey()
}
result.logEntries[key].push(...newEntries[key])
result.logEntries[key].push(...(newEntries[key] as LogEntry[]))
}
}
}
@@ -111,7 +144,7 @@ export const handleLogFiles = async (outputFiles, data, signal) => {
}
}
const blgFiles = []
const blgFiles: PDFFile[] = []
for (const [filename, file] of outputFiles) {
if (filename.endsWith('.blg')) {
@@ -143,15 +176,15 @@ export const handleLogFiles = async (outputFiles, data, signal) => {
return result
}
/**
* @typedef {import('../../../../../types/annotation').Annotation} Annotation
* @returns {Record<string, Annotation[]>}
*/
export function buildLogEntryAnnotations(entries, fileTreeData, rootDocId) {
const rootDocDirname = dirname(fileTreeData, rootDocId)
export function buildLogEntryAnnotations(
entries: LogEntry[],
fileTreeData: Folder,
rootDocId?: string | null
): Record<string, Annotation[]> {
const rootDocDirname = rootDocId ? dirname(fileTreeData, rootDocId) : null
const logEntryAnnotations = {}
const seenLine = {}
const logEntryAnnotations: Record<string, Annotation[]> = {}
const seenLine: Record<number, boolean> = {}
for (const entry of entries) {
if (entry.file) {
@@ -164,12 +197,12 @@ export function buildLogEntryAnnotations(entries, fileTreeData, rootDocId) {
logEntryAnnotations[entity._id] = []
}
const annotation = {
const annotation: Annotation = {
id: entry.key,
entryIndex: logEntryAnnotations[entity._id].length, // used for maintaining the order of items on the same line
row: entry.line - 1,
row: (entry.line || 1) - 1,
type: entry.level === 'error' ? 'error' : 'warning',
text: entry.message,
text: entry.message ?? '',
source: 'compile', // NOTE: this is used in Ace for filtering the annotations
ruleId: entry.ruleId,
command: entry.command,
@@ -177,9 +210,9 @@ export function buildLogEntryAnnotations(entries, fileTreeData, rootDocId) {
// set firstOnLine for the first non-typesetting annotation on a line
if (entry.level !== 'typesetting') {
if (!seenLine[entry.line]) {
if (!seenLine[entry.line || 0]) {
annotation.firstOnLine = true
seenLine[entry.line] = true
seenLine[entry.line || 0] = true
}
}
@@ -191,8 +224,10 @@ export function buildLogEntryAnnotations(entries, fileTreeData, rootDocId) {
return logEntryAnnotations
}
export const buildRuleCounts = (entries = []) => {
const counts = {}
export const buildRuleCounts = (
entries: LogEntry[] = []
): Record<string, number> => {
const counts: Record<string, number> = {}
for (const entry of entries) {
const key = `${entry.level}_${entry.ruleId}`
counts[key] = counts[key] ? counts[key] + 1 : 1
@@ -200,16 +235,17 @@ export const buildRuleCounts = (entries = []) => {
return counts
}
export const buildRuleDeltas = (ruleCounts, previousRuleCounts) => {
const counts = {}
export const buildRuleDeltas = (
ruleCounts: Record<string, number>,
previousRuleCounts: Record<string, number>
): Record<string, number> => {
const counts: Record<string, number> = {}
// keys that are defined in the current log entries
for (const [key, value] of Object.entries(ruleCounts)) {
const previousValue = previousRuleCounts[key] ?? 0
counts[`delta_${key}`] = value - previousValue
}
// keys that are no longer defined in the current log entries
for (const [key, value] of Object.entries(previousRuleCounts)) {
if (!(key in ruleCounts)) {
counts[key] = 0
@@ -220,7 +256,7 @@ export const buildRuleDeltas = (ruleCounts, previousRuleCounts) => {
return counts
}
function buildURL(file, pdfDownloadDomain) {
function buildURL(file: PDFFile, pdfDownloadDomain?: string): string {
if (file.build && pdfDownloadDomain) {
// Downloads from the compiles domain must include a build id.
// The build id is used implicitly for access control.
@@ -230,13 +266,15 @@ function buildURL(file, pdfDownloadDomain) {
return `${window.origin}${file.url}`
}
function normalizeFilePath(path, rootDocDirname) {
function normalizeFilePath(
path: string,
rootDocDirname?: string | null
): string {
path = path.replace(/\/\//g, '/')
path = path.replace(
/^.*\/compiles\/[0-9a-f]{24}(-[0-9a-f]{24})?\/(\.\/)?/,
''
)
path = path.replace(/^\/compile\//, '')
if (rootDocDirname) {
@@ -246,11 +284,15 @@ function normalizeFilePath(path, rootDocDirname) {
return path
}
function isTransientWarning(warning) {
return TRANSIENT_WARNING_REGEX.test(warning.message)
function isTransientWarning(warning: LatexLogEntry): boolean {
return TRANSIENT_WARNING_REGEX.test(warning.message || '')
}
async function fetchFileWithSizeLimit(url, signal, maxSize) {
async function fetchFileWithSizeLimit(
url: string,
signal: AbortSignal,
maxSize: number
): Promise<string> {
let result = ''
try {
const abortController = new AbortController()
@@ -267,11 +309,13 @@ async function fetchFileWithSizeLimit(url, signal, maxSize) {
throw new Error('Failed to fetch log file')
}
const reader = response.body.pipeThrough(new TextDecoderStream())
for await (const chunk of reader) {
result += chunk
if (result.length > maxSize) {
abortController.abort()
const reader = response.body?.pipeThrough(new TextDecoderStream())
if (reader) {
for await (const chunk of reader) {
result += chunk
if (result.length > maxSize) {
abortController.abort()
}
}
}
} catch (e) {

View File

@@ -14,6 +14,7 @@ export type LogEntry = {
type?: string
messageComponent?: React.ReactNode
contentDetails?: string[]
command?: string
}
export type ErrorLevel =

View File

@@ -9,7 +9,8 @@ import { createTag } from '../../util/api'
import { MAX_TAG_LENGTH } from '../../util/tag'
import { ColorPicker } from '../color-picker/color-picker'
import { debugConsole } from '@/utils/debugging'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -80,7 +81,7 @@ export default function CreateTagModal({
return (
<OLModal show animation onHide={onClose} id={id} backdrop="static">
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('create_new_tag')}</OLModalTitle>
</OLModalHeader>

View File

@@ -4,7 +4,8 @@ import { Tag } from '../../../../../../app/src/Features/Tags/types'
import useAsync from '../../../../shared/hooks/use-async'
import { deleteTag } from '../../util/api'
import { debugConsole } from '@/utils/debugging'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -46,7 +47,7 @@ export default function DeleteTagModal({
return (
<OLModal show animation onHide={onClose} id={id} backdrop="static">
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('delete_tag')}</OLModalTitle>
</OLModalHeader>

View File

@@ -9,7 +9,8 @@ import { editTag } from '../../util/api'
import { getTagColor, MAX_TAG_LENGTH } from '../../util/tag'
import { ColorPicker } from '../color-picker/color-picker'
import { debugConsole } from '@/utils/debugging'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -88,7 +89,7 @@ export function EditTagModal({ id, tag, onEdit, onClose }: EditTagModalProps) {
return (
<OLModal show animation onHide={onClose} id={id} backdrop="static">
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('edit_tag')}</OLModalTitle>
</OLModalHeader>

View File

@@ -8,7 +8,8 @@ import { Tag } from '../../../../../../app/src/Features/Tags/types'
import { getTagColor } from '../../util/tag'
import { ColorPicker } from '../color-picker/color-picker'
import { debugConsole } from '@/utils/debugging'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -89,7 +90,7 @@ export function ManageTagModal({
return (
<OLModal show animation onHide={onClose} id={id} backdrop="static">
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('edit_tag')}</OLModalTitle>
</OLModalHeader>

View File

@@ -7,7 +7,8 @@ import * as eventTracking from '../../../../infrastructure/event-tracking'
import { isSmallDevice } from '../../../../infrastructure/event-tracking'
import Notification from '@/shared/components/notification'
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -79,7 +80,7 @@ function ProjectsActionModal({
id="action-project-modal"
backdrop="static"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{title}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -16,7 +16,8 @@ import { getUserFacingMessage } from '../../../../infrastructure/fetch-json'
import { debugConsole } from '@/utils/debugging'
import { isSmallDevice } from '../../../../infrastructure/event-tracking'
import Notification from '@/shared/components/notification'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -104,7 +105,7 @@ function RenameProjectModal({
id="rename-project-modal"
backdrop="static"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('rename_project')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -1,5 +1,5 @@
import ModalContentNewProjectForm from './modal-content-new-project-form'
import OLModal from '@/shared/components/ol/ol-modal'
import { OLModal } from '@/shared/components/ol/ol-modal'
type BlankProjectModalProps = {
onHide: () => void

View File

@@ -1,4 +1,4 @@
import OLModal from '@/shared/components/ol/ol-modal'
import { OLModal } from '@/shared/components/ol/ol-modal'
import ModalContentNewProjectForm from './modal-content-new-project-form'
type ExampleProjectModalProps = {

View File

@@ -74,7 +74,7 @@ function ModalContentNewProjectForm({ onCancel, template = 'none' }: Props) {
return (
<>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('new_project')}</OLModalTitle>
</OLModalHeader>

View File

@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -94,7 +95,7 @@ function UploadProjectModal({ onHide, openProject }: UploadProjectModalProps) {
id="upload-project-modal"
backdrop="static"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle as="h3">{t('upload_zipped_project')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -2,7 +2,8 @@ import { useCallback, useEffect, useRef, useState } from 'react'
import bannerImage from '../../../images/brl-banner.png'
import usePersistedState from '../../../../../shared/hooks/use-persisted-state'
import * as eventTracking from '../../../../../infrastructure/event-tracking'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -90,7 +91,7 @@ export default function BRLBanner() {
return (
<OLModal show={showModal} onHide={handleHide}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('latam_discount_modal_title')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -3,7 +3,8 @@ import { useTranslation } from 'react-i18next'
import bannerImage from '../../../images/inr-banner.png'
import usePersistedState from '../../../../../shared/hooks/use-persisted-state'
import * as eventTracking from '../../../../../infrastructure/event-tracking'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -88,7 +89,7 @@ export default function INRBanner() {
return (
<OLModal show={showModal} onHide={handleHide} backdrop="static">
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('inr_discount_modal_title')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -5,7 +5,8 @@ import clpBannerImage from '../../../images/clp-banner.png'
import penBannerImage from '../../../images/pen-banner.png'
import usePersistedState from '../../../../../shared/hooks/use-persisted-state'
import * as eventTracking from '../../../../../infrastructure/event-tracking'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -133,7 +134,7 @@ export default function LATAMBanner() {
return (
<OLModal show={showModal} onHide={handleHide} backdrop="static">
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('latam_discount_modal_title')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -8,7 +8,8 @@ import { postJSON } from '../../../../../../infrastructure/fetch-json'
import { isSmallDevice } from '../../../../../../infrastructure/event-tracking'
import OLTooltip from '@/shared/components/ol/ol-tooltip'
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -120,7 +121,7 @@ function CompileErrorModal({
return (
<>
<OLModal show onHide={handleClose}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>
{project.name}: {t('pdf_unavailable_for_download')}
</OLModalTitle>

View File

@@ -1,7 +1,8 @@
import { FC, memo } from 'react'
import OLButton from '@/shared/components/ol/ol-button'
import { useTranslation } from 'react-i18next'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -5,7 +5,8 @@ import teaserVideo from '../images/teaser-track-changes.mp4'
import teaserImage from '../images/teaser-track-changes.gif'
import { startFreeTrial, upgradePlan } from '@/main/account-upgrade'
import { memo } from 'react'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -31,7 +32,7 @@ function UpgradeTrackChangesModal({
return (
<OLModal show={show} onHide={() => setShow(false)}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('upgrade_to_review')}</OLModalTitle>
</OLModalHeader>
<OLModalBody className="upgrade-track-changes-modal">

View File

@@ -1,7 +1,8 @@
import { useTranslation, Trans } from 'react-i18next'
import { MergeAndOverride } from '../../../../../../../../types/utils'
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -32,7 +33,7 @@ function ConfirmationModal({
return (
<OLModal show={show} onHide={onHide}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('confirm_primary_email_change')}</OLModalTitle>
</OLModalHeader>
<OLModalBody className="pb-0">

View File

@@ -4,7 +4,8 @@ import { FetchError, postJSON } from '@/infrastructure/fetch-json'
import useAsync from '../../../../shared/hooks/use-async'
import { UserEmailData } from '../../../../../../types/user-email'
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -56,7 +57,7 @@ function ResendConfirmationCodeModal({
id="action-project-modal"
backdrop="static"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('confirm_your_email')}</OLModalTitle>
</OLModalHeader>

View File

@@ -71,7 +71,7 @@ function LeaveModalContent({
return (
<>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('delete_account')}</OLModalTitle>
</OLModalHeader>

View File

@@ -1,6 +1,6 @@
import { useState, useCallback } from 'react'
import LeaveModalContent from './modal-content'
import OLModal from '@/shared/components/ol/ol-modal'
import { OLModal } from '@/shared/components/ol/ol-modal'
type LeaveModalProps = {
isOpen: boolean

View File

@@ -4,7 +4,8 @@ import OLBadge from '@/shared/components/ol/ol-badge'
import getMeta from '../../../../utils/meta'
import { sendMB } from '../../../../infrastructure/event-tracking'
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -208,7 +209,7 @@ function UnlinkConfirmationModal({
return (
<OLModal show={show} onHide={handleHide}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{title}</OLModalTitle>
</OLModalHeader>

View File

@@ -6,7 +6,8 @@ import GoogleLogo from '../../../../shared/svgs/google-logo'
import OrcidLogo from '../../../../shared/svgs/orcid-logo'
import LinkingStatus from './status'
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -168,7 +169,7 @@ function UnlinkConfirmModal({
return (
<OLModal show={show} onHide={handleHide}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>
{t('unlink_provider_account_title', { provider: title })}
</OLModalTitle>

View File

@@ -20,7 +20,7 @@ export default function EditorOverLimitModalContent({
return (
<>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('do_you_need_edit_access')}</OLModalTitle>
</OLModalHeader>

View File

@@ -5,7 +5,7 @@ import { useProjectContext } from '@/shared/context/project-context'
import { useEditorContext } from '@/shared/context/editor-context'
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
import { sendMB } from '@/infrastructure/event-tracking'
import OLModal from '@/shared/components/ol/ol-modal'
import { OLModal } from '@/shared/components/ol/ol-modal'
const EditorOverLimitModal = () => {
const [show, setShow] = useState(false)

View File

@@ -3,7 +3,8 @@ import { useEditorContext } from '@/shared/context/editor-context'
import { lazy, Suspense } from 'react'
import { FullSizeLoadingSpinner } from '@/shared/components/loading-spinner'
import ClickableElementEnhancer from '@/shared/components/clickable-element-enhancer'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -43,7 +44,7 @@ export default function ShareProjectModalContent({
return (
<OLModal show={show} onHide={cancel} animation={animation}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('share_project')}</OLModalTitle>
</OLModalHeader>

View File

@@ -3,7 +3,8 @@ import { Trans, useTranslation } from 'react-i18next'
import { transferProjectOwnership } from '../utils/api'
import { useProjectContext } from '@/shared/context/project-context'
import { useLocation } from '@/shared/hooks/use-location'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -45,7 +46,7 @@ export default function TransferOwnershipModal({
return (
<OLModal show onHide={cancel}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('change_project_owner')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -19,7 +19,7 @@ export default function ViewOnlyAccessModalContent({
return (
<>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('view_only_access')}</OLModalTitle>
</OLModalHeader>

View File

@@ -5,7 +5,7 @@ import { useProjectContext } from '@/shared/context/project-context'
import { useEditorContext } from '@/shared/context/editor-context'
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
import { sendMB } from '@/infrastructure/event-tracking'
import OLModal from '@/shared/components/ol/ol-modal'
import { OLModal } from '@/shared/components/ol/ol-modal'
const ViewOnlyAccessModal = () => {
const [show, setShow] = useState(false)

View File

@@ -1,4 +1,5 @@
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -273,7 +274,7 @@ const FigureModalContent = () => {
}
return (
<OLModal onHide={hide} className="figure-modal" show>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>
{helpShown
? t('help')

View File

@@ -5,7 +5,8 @@ import {
DropdownToggle,
} from '@/shared/components/dropdown/dropdown-menu'
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -1,4 +1,5 @@
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -19,7 +20,7 @@ export const TableGeneratorHelpModal = () => {
onHide={hideHelp}
className="table-generator-help-modal"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('help')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -16,7 +16,8 @@ import { setColumnWidth } from '../commands'
import { UNITS, WidthSelection, WidthUnit } from './column-width'
import { useCodeMirrorViewContext } from '../../../codemirror-context'
import { CopyToClipboard } from '@/shared/components/copy-to-clipboard'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -137,7 +138,7 @@ const ColumnWidthModalBody = () => {
onHide={closeColumnWidthModal}
className="table-generator-width-modal"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('set_column_width')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -13,21 +13,19 @@ import { memo } from 'react'
import OLListGroupItem from '@/shared/components/ol/ol-list-group-item'
import sparkleWhite from '@/shared/svgs/sparkle-small-white.svg'
import sparkle from '@/shared/svgs/ai-sparkle-text.svg'
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
export const MathDropdown = memo(function MathDropdown() {
const { t } = useTranslation()
const view = useCodeMirrorViewContext()
const { writefullInstance } = useEditorContext()
const wfRebrandEnabled = isSplitTestEnabled('overleaf-assist-bundle')
return (
<ToolbarButtonMenu
id="toolbar-math"
label={t('toolbar_insert_math')}
icon={<MaterialIcon type="calculate" />}
>
{wfRebrandEnabled && writefullInstance && (
{writefullInstance && (
<>
<DropdownHeader className="ol-cm-toolbar-header mx-2">
{t('toolbar_insert_math_lowercase')}

View File

@@ -12,7 +12,6 @@ import { MathDropdown } from './math-dropdown'
import { TableDropdown } from './table-dropdown'
import { LegacyTableDropdown } from './table-inserter-dropdown-legacy'
import { withinFormattingCommand } from '@/features/source-editor/utils/tree-operations/formatting'
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
import { isMac } from '@/shared/utils/os'
import { useProjectContext } from '@/shared/context/project-context'
import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-properties-context'
@@ -42,8 +41,6 @@ export const ToolbarItems: FC<{
const symbolPaletteAvailable = getMeta('ol-symbolPaletteAvailable')
const showGroup = (group: string) => !overflowed || overflowed.has(group)
const wfRebrandEnabled = isSplitTestEnabled('overleaf-assist-bundle')
return (
<>
{showGroup('group-history') && (
@@ -155,11 +152,7 @@ export const ToolbarItems: FC<{
icon="book_5"
/>
<InsertFigureDropdown />
{wfRebrandEnabled && writefullInstance ? (
<TableDropdown />
) : (
<LegacyTableDropdown />
)}
{writefullInstance ? <TableDropdown /> : <LegacyTableDropdown />}
</div>
)}
{showGroup('group-list') && (

View File

@@ -297,7 +297,7 @@ function useCodeMirrorScope(view: EditorView) {
// which editor keybindings are active ('default' | 'vim' | 'emacs')
ol_editor_keybindings: settingsRef.current.mode,
// whether Writefull is present ('extension' | 'integration' | 'none')
ol_extensions_writefull: window.writefull?.type ?? 'none',
ol_extensions_writefull: window.writefull ? 'integration' : 'none',
// whether Grammarly is present
ol_extensions_grammarly: grammarlyExtensionPresent(),
},

View File

@@ -4,7 +4,8 @@ import { deleteJSON } from '../../../../infrastructure/fetch-json'
import { useSubscriptionDashboardContext } from '../../context/subscription-dashboard-context'
import { useLocation } from '../../../../shared/hooks/use-location'
import { debugConsole } from '@/utils/debugging'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -3,7 +3,8 @@ import { useSubscriptionDashboardContext } from '../../context/subscription-dash
import { useCallback, useMemo, useState } from 'react'
import { postJSON } from '@/infrastructure/fetch-json'
import { useLocation } from '@/shared/hooks/use-location'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalHeader,
} from '@/shared/components/ol/ol-modal'
@@ -88,7 +89,7 @@ export default function PauseSubscriptionModal() {
backdrop="static"
>
<OLModalBody>
<OLModalHeader closeButton style={{ border: 0 }} />
<OLModalHeader style={{ border: 0 }} />
<img
src={PauseDuck}
alt="Need to duck out for a while?"

View File

@@ -4,7 +4,8 @@ import { SubscriptionDashModalIds } from '../../../../../../../../../../types/su
import { postJSON } from '../../../../../../../../infrastructure/fetch-json'
import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
import { useLocation } from '../../../../../../../../shared/hooks/use-location'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -4,7 +4,8 @@ import LoadingSpinner from '../../../../../../../../shared/components/loading-sp
import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
import { ChangeToGroupPlan } from '../change-to-group-plan'
import { IndividualPlansTable } from '../individual-plans-table'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalHeader,
OLModalTitle,
@@ -43,7 +44,7 @@ export function ChangePlanModal() {
return (
<OLModal id={modalId} show animation onHide={handleCloseModal} size="lg">
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('change_plan')}</OLModalTitle>
</OLModalHeader>

View File

@@ -12,7 +12,8 @@ import GenericErrorAlert from '../../../../generic-error-alert'
import { subscriptionUpdateUrl } from '../../../../../../data/subscription-url'
import { getRecurlyGroupPlanCode } from '../../../../../../util/recurly-group-plan-code'
import { useLocation } from '../../../../../../../../shared/hooks/use-location'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -175,7 +176,7 @@ export function ChangeToGroupModal() {
onHide={handleCloseModal}
backdrop="static"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle className="lh-sm">
{t('customize_your_group_subscription')}
{showGroupDiscount && (

View File

@@ -9,7 +9,8 @@ import getMeta from '../../../../../../../../utils/meta'
import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
import { subscriptionUpdateUrl } from '../../../../../../data/subscription-url'
import { useLocation } from '../../../../../../../../shared/hooks/use-location'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -5,7 +5,8 @@ import { postJSON } from '../../../../../../../../infrastructure/fetch-json'
import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
import { cancelPendingSubscriptionChangeUrl } from '../../../../../../data/subscription-url'
import { useLocation } from '../../../../../../../../shared/hooks/use-location'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -1,7 +1,8 @@
import { useTranslation } from 'react-i18next'
import { SubscriptionDashModalIds } from '../../../../../../../../../../types/subscription/dashboard/modal-ids'
import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -2,7 +2,8 @@ import { useState } from 'react'
import { SubscriptionDashModalIds } from '../../../../../../../../types/subscription/dashboard/modal-ids'
import { Trans, useTranslation } from 'react-i18next'
import { useSubscriptionDashboardContext } from '@/features/subscription/context/subscription-dashboard-context'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,

View File

@@ -1,5 +1,6 @@
import OLButton from '@/shared/components/ol/ol-button'
import OLModal, {
import {
OLModal,
OLModalBody,
OLModalFooter,
OLModalHeader,
@@ -28,7 +29,7 @@ function LeaveProjectModal({
id="action-project-modal"
backdrop="static"
>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{t('leave_project')}</OLModalTitle>
</OLModalHeader>
<OLModalBody>

View File

@@ -21,7 +21,7 @@ export default function WordCountModalContent({
return (
<>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>
{t('word_count_lower')}{' '}
<SplitTestBadge

View File

@@ -1,7 +1,7 @@
import { memo } from 'react'
import WordCountModalContent from './word-count-modal-content'
import withErrorBoundary from '../../../infrastructure/error-boundary'
import OLModal from '@/shared/components/ol/ol-modal'
import { OLModal } from '@/shared/components/ol/ol-modal'
const WordCountModal = memo(function WordCountModal({
show,

View File

@@ -151,60 +151,6 @@ const en = {
'errors.error-hit-limit-freemium.heading': 'Youre on fire!',
'errors.error-hit-limit-freemium.body':
'Youve hit your Writefull quota. Upgrade now for unlimited language suggestions and LaTeX support, and early access to upcoming features like TikZ generation.',
'checkout.need-more-info': "Need more info? We're here to help.",
'checkout.free-plan-question': 'Whats included in the Free plan?',
'checkout.free-plan-answer':
"The Free plan offers a limited number of language edits a day, restricted use of the writing features (to paraphrase, change style, explain or summarize, and more), and limited access to TeXGPT and the LaTeX generation options. You're notified when you've hit your daily limit. Please note that limits might change daily so that we can maintain certain levels of quality of service.",
'checkout.premium-plan-question':
'Do I get different language edits if I upgrade to Premium?',
'checkout.premium-plan-answer':
'Both the Free and Premium plans let you choose the language model that revises your text: Writefulls model, which is ideal for research writing in English, or GPT, which works with any language or text type. With the Free plan, you get a limited number of edits each day, but Premium gives you unlimited access. Plus, when you upgrade to Premium, you can add custom prompts to GPT, giving you more control over how your text is revised.',
'checkout.custom-prompt-question':
"What does the 'custom prompt for GPT language edits' mean?",
'checkout.custom-prompt-answer':
'With this feature, you can give the GPT model specific instructions on how to revise your text. For example, you can ask it to keep certain terms unchanged, make your writing more assertive, or focus only on essential language corrections. Its a great way to tailor the edits to your needs! This option is available with the Premium plan.',
'checkout.supercomplete-question': "What is the 'Supercomplete' feature?",
'checkout.supercomplete-answer':
"The 'Supercomplete' feature makes your writing even more efficient by completing your sentences and LaTeX code when prompted. This is a fully Premium feature.",
'checkout.upcoming-features-question':
'What other features are likely to come?',
'checkout.upcoming-features-answer':
'We have a long list of upcoming features! Two of these are translation and diagram generation using TikZ.',
'checkout.overleaf-subscription-question':
'I already pay for Overleaf. Do I still need Writefull Premium?',
'checkout.overleaf-subscription-answer':
'Yes, Writefull and Overleaf have integrated, but are two separate services with their own subscriptions.',
'checkout.group-discounts-question': 'Do you offer group discounts?',
'checkout.group-discounts-answer':
'We do! Go here to calculate pricing depending on your group size. Select a plan and invite others to join.',
'checkout.fair-usage-policy': '* Fair usage policy applies.',
'checkout.still-have-questions': 'Do you still have questions?',
'checkout.contact-us': 'Contact Us',
'checkout.monthly': 'Monthly',
'checkout.yearly': 'Yearly',
'checkout.free': 'FREE',
'checkout.basic-access': 'Basic access to Writefull',
'checkout.features': 'Features:',
'checkout.limited-language-edits': 'Limited language edits',
'checkout.limited-writing-features': 'Limited usage of writing features',
'checkout.limited-texgpt': 'Limited usage of TeXGPT',
'checkout.custom-prompt': 'Custom prompt for GPT language edits',
'checkout.supercomplete': 'Supercomplete',
'checkout.supercomplete-coming': 'Supercomplete (coming soon)',
'checkout.priority-access': 'Priority access to new Writefull features',
'checkout.premium': 'WRITEFULL PREMIUM',
'checkout.all-unlocked': 'All of Writefull unlocked',
'checkout.unlimited-language-edits': 'Unlimited language edits*',
'checkout.unlimited-writing-features':
'Unlimited usage of writing features*',
'checkout.unlimited-texgpt': 'Unlimited usage of TeXGPT',
'checkout.redirecting': 'Redirecting...',
'checkout.select-plan': 'Select Writefull plan',
'checkout.looking-for': 'Looking for',
'checkout.group-licenses': 'group licenses',
'checkout.per-month': 'Per month',
'checkout.your-current-plan': 'Your current plan',
'checkout.billed-as-one-payment-of': 'billed as one payment of',
'toolbar.abstract-generator.name': 'Abstract Generator',
'toolbar.title-generator.name': 'Title Generator',
'toolbar.create-table.name': 'Create tables',
@@ -554,64 +500,6 @@ const es = {
'errors.error-hit-limit-freemium.heading': 'Estás que ardes',
'errors.error-hit-limit-freemium.body':
'Has agotado tu cuota de Writefull. Actualiza ahora para obtener sugerencias de lenguaje ilimitadas y soporte en LaTeX, y acceso anticipado a las próximas funciones de generación como TikZ.',
'checkout.need-more-info':
'¿Necesitas más información? Estamos aquí para ayudar.',
'checkout.free-plan-question': '¿Qué incluye el plan gratuito?',
'checkout.free-plan-answer':
'El plan gratuito ofrece un número limitado de ediciones de lenguaje al día, uso restringido de las funciones de escritura (para parafrasear, cambiar el estilo, explicar o resumir, y más), y acceso limitado a TeXGPT y las opciones de generación de LaTeX. Se te notificará cuando hayas alcanzado tu límite diario. Ten en cuenta que los límites pueden cambiar diariamente para que podamos mantener ciertos niveles de calidad de servicio.',
'checkout.premium-plan-question':
'¿Obtengo diferentes sugerencias de lenguaje si actualizo a Premium?',
'checkout.premium-plan-answer':
'Tanto los planes gratuitos como los Premium te permiten elegir el modelo de lenguaje que revisa tu texto: el modelo de Writefull, que es ideal para la escritura de investigación en inglés, o GPT, que funciona con cualquier idioma o tipo de texto. Con el plan gratuito, obtienes un número limitado de sugerencias, pero Premium te da acceso ilimitado. Además, cuando actualizas a Premium, puedes agregar prompts personalizados a GPT, dándote más control sobre cómo se revisa tu texto.',
'checkout.custom-prompt-question':
"¿Qué significa el 'prompt personalizado para ediciones de lenguaje GPT'?",
'checkout.custom-prompt-answer':
'Con esta función, puedes dar al modelo GPT instrucciones específicas sobre cómo revisar tu texto. Por ejemplo, puedes pedirle que mantenga ciertos términos sin cambios, que haga tu escritura más asertiva, o que se enfoque solo en correcciones esenciales del lenguaje. ¡Es una excelente manera de adaptar las sugerencias a tus necesidades! Esta opción está disponible con el plan Premium.',
'checkout.supercomplete-question': "¿Qué es la función 'Supercomplete'?",
'checkout.supercomplete-answer':
"La función 'Supercomplete' hace que tu escritura sea aún más eficiente al completar tus oraciones y código LaTeX cuando se le solicita. Esta es una función completamente Premium.",
'checkout.upcoming-features-question':
'¿Qué otras funciones es probable que vengan?',
'checkout.upcoming-features-answer':
'¡Tenemos una larga lista de funciones próximas! Dos de estas son la traducción y la generación de diagramas usando TikZ.',
'checkout.overleaf-subscription-question':
'Ya pago por Overleaf. ¿Aún necesito Writefull Premium?',
'checkout.overleaf-subscription-answer':
'Sí, Writefull y Overleaf se han integrado, pero son dos servicios separados con sus propias suscripciones.',
'checkout.group-discounts-question': '¿Ofrecen descuentos para grupos?',
'checkout.group-discounts-answer':
'¡Sí! Ve aquí para calcular el precio dependiendo del tamaño de tu grupo. Selecciona un plan e invita a otros a unirse.',
'checkout.fair-usage-policy': '* Se aplica la política de uso justo.',
'checkout.still-have-questions': '¿Aún tienes preguntas?',
'checkout.contact-us': 'Contáctanos',
'checkout.monthly': 'Mensual',
'checkout.yearly': 'Anual',
'checkout.free': 'GRATIS',
'checkout.basic-access': 'Acceso básico a Writefull',
'checkout.features': 'Características:',
'checkout.limited-language-edits': 'Ediciones de lenguaje limitadas',
'checkout.limited-writing-features':
'Uso limitado de funciones de escritura',
'checkout.limited-texgpt': 'Uso limitado de TeXGPT',
'checkout.custom-prompt':
'Prompt personalizado para sugerencias de lenguaje GPT',
'checkout.supercomplete': 'Supercomplete',
'checkout.supercomplete-coming': 'Supercomplete (pronto)',
'checkout.priority-access':
'Acceso prioritario a nuevas funciones de Writefull',
'checkout.premium': 'WRITEFULL PREMIUM',
'checkout.all-unlocked': 'Todo Writefull desbloqueado',
'checkout.unlimited-language-edits': 'Sugerencias de lenguaje ilimitadas*',
'checkout.unlimited-writing-features':
'Uso ilimitado de funciones de escritura*',
'checkout.unlimited-texgpt': 'Uso ilimitado de TeXGPT',
'checkout.redirecting': 'Redirigiendo...',
'checkout.select-plan': 'Seleccionar plan de Writefull',
'checkout.looking-for': 'Buscando',
'checkout.group-licenses': 'licencias grupales',
'checkout.per-month': 'Por mes',
'checkout.your-current-plan': 'Tu plan actual',
'checkout.billed-as-one-payment-of': 'facturado como un solo pago de',
'toolbar.abstract-generator.name': 'Generar Abstract',
'toolbar.title-generator.name': 'Generar Título',
'toolbar.create-table.name': 'Crear tablas',

View File

@@ -64,7 +64,7 @@ const parserReducer = function (maxErrors: number | null) {
}
}
type BibLogEntry = {
export type BibLogEntry = {
file: string
level: string
message: string

View File

@@ -19,7 +19,7 @@ export type LatexParserOptions = {
ignoreDuplicates?: boolean
}
type LatexLogEntry = {
export type LatexLogEntry = {
line: string | number | null
file: string | undefined
level: 'error' | 'warning' | 'typesetting'

View File

@@ -12,12 +12,24 @@ type OLModalProps = ModalProps & {
onHide: () => void
}
export default function OLModal({ children, ...props }: OLModalProps) {
type OLModalHeaderProps = ModalHeaderProps & {
closeButton?: boolean
}
export function OLModal({ children, ...props }: OLModalProps) {
return <Modal {...props}>{children}</Modal>
}
export function OLModalHeader({ children, ...props }: ModalHeaderProps) {
return <Modal.Header {...props}>{children}</Modal.Header>
export function OLModalHeader({
children,
closeButton = true,
...props
}: OLModalHeaderProps) {
return (
<Modal.Header closeButton={closeButton} {...props}>
{children}
</Modal.Header>
)
}
export function OLModalTitle({ children, ...props }: ModalTitleProps) {

View File

@@ -25,7 +25,7 @@ import {
buildRuleDeltas,
handleLogFiles,
handleOutputFiles,
} from '../../features/pdf-preview/util/output-files'
} from '@/features/pdf-preview/util/output-files'
import { useProjectContext } from './project-context'
import { useEditorContext } from './editor-context'
import { buildFileList } from '../../features/pdf-preview/util/file-list'
@@ -81,7 +81,7 @@ export type CompileContext = {
logEntryAnnotations?: Record<string, Annotation[]>
outputFilesArchive?: string
pdfDownloadUrl?: string
pdfFile?: PdfFile
pdfFile?: PdfFile | null
pdfUrl?: string
pdfViewer?: string
position?: PdfScrollPosition
@@ -167,7 +167,7 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
const { pdfViewer, syntaxValidation } = userSettings
// low level details for metrics
const [pdfFile, setPdfFile] = useState<PdfFile | undefined>()
const [pdfFile, setPdfFile] = useState<PdfFile | null | undefined>()
// the project is considered to be "uncompiled" if a doc has changed, or finished saving, since the last compile started.
const [uncompiled, setUncompiled] = useState(false)
@@ -296,7 +296,7 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
}, [compiling])
const _buildLogEntryAnnotations = useCallback(
(entries: any) =>
(entries: LogEntry[]) =>
buildLogEntryAnnotations(entries, fileTreeData, lastCompileRootDocId),
[fileTreeData, lastCompileRootDocId]
)

View File

@@ -5,33 +5,11 @@ export interface WritefullEvents {
}
'writefull-received-suggestions': { numberOfSuggestions: number }
'writefull-register-as-auto-account': { email: string }
'writefull-shared-analytics': { eventName: string; segmentation: object }
'writefull-ai-assist-show-paywall': { origin?: string }
}
type InsertPosition = {
parentSelector: string
insertBeforeSelector?: string
}
export interface WritefullAPI {
init({
toolbarPosition,
iconPosition,
hasAgreedToTOS,
overleafUserId,
overleafLabels,
}: {
toolbarPosition: InsertPosition
iconPosition: InsertPosition
hasAgreedToTOS: boolean
overleafUserId: string
overleafLabels: {
autoImport: boolean
autoCreatedAccount: boolean
splitTests: Record<string, boolean>
}
}): Promise<void>
init(): Promise<void>
addEventListener<eventName extends keyof WritefullEvents>(
name: eventName,
callback: (detail: WritefullEvents[eventName]) => void

View File

@@ -1,52 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react'
import LoadingSpinner, {
FullSizeLoadingSpinner,
} from '@/shared/components/loading-spinner'
type Story = StoryObj<typeof LoadingSpinner>
export const Default: Story = {
args: {
loadingText: 'Loading content...',
},
}
export const WithDelay: Story = {
args: {
delay: 500,
loadingText: 'This will appear after a 500ms delay...',
},
}
export const FullSize: StoryObj<typeof FullSizeLoadingSpinner> = {
render: args => <FullSizeLoadingSpinner {...args} />,
args: {
loadingText: 'Loading entire section...',
size: 'sm',
},
}
const meta: Meta<typeof LoadingSpinner> = {
title: 'Shared / Components / Loading Spinner',
component: LoadingSpinner,
parameters: {
layout: 'centered',
},
argTypes: {
delay: {
control: 'select',
options: [0, 500],
},
size: {
control: 'radio',
options: ['lg', 'sm'],
},
},
args: {
size: 'sm',
delay: 0,
},
render: args => <LoadingSpinner {...args} />,
}
export default meta

View File

@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react'
import { figmaDesignUrl } from '../../../.storybook/utils/figma-design-url'
import OLModal, {
import { figmaDesignUrl } from './../../../.storybook/utils/figma-design-url'
import {
OLModal,
OLModalHeader,
OLModalBody,
OLModalFooter,
@@ -99,7 +100,7 @@ const meta: Meta<typeof OLModal> = {
},
render: ({ title, children, footer, ...args }) => (
<OLModal {...args}>
<OLModalHeader closeButton>
<OLModalHeader>
<OLModalTitle>{title}</OLModalTitle>
</OLModalHeader>
<OLModalBody>{children}</OLModalBody>

View File

@@ -1624,7 +1624,9 @@
"payment_error_3ds_failed": "We couldnt complete your payment because authentication wasnt successful. Please try again or choose a different payment method. If the problem continues please <0>contact us</0>.",
"payment_error_generic": "Sorry, something went wrong. Please try again. If the problem continues please <0>contact us</0>.",
"payment_error_intermittent_error": "We were unable to process your payment. Please try again later or <0>contact us</0> for assistance.",
"payment_error_invalid_payment_method": "We couldnt process your payment because your payment details appear to be invalid. Please check the information you entered and try again. If the problem continues, please try a different payment method or <0>contact us</0>.",
"payment_error_update_payment_method": "Your payment was declined. Please <0>update your billing information</0> and try again.",
"payment_error_update_payment_method_checkout": "Your payment was declined. Please update your billing information and try again.",
"payment_method_accepted": "__paymentMethod__ accepted",
"payment_provider_unreachable_error": "Sorry, there was an error talking to our payment provider. Please try again in a few moments.\nIf you are using any ad or script blocking extensions in your browser, you may need to temporarily disable them.",
"payment_summary": "Payment summary",
@@ -2274,6 +2276,7 @@
"sync_with_a_github_repository": "Sync with a GitHub repository.",
"synctex_error_recompile_and_try_again": "That didnt work. Recompile and try again.",
"synctex_failed": "Couldnt find the corresponding source file",
"syntax_checks": "Syntax checks",
"syntax_validation": "Code check",
"tab_connecting": "Connecting with the editor",
"tab_no_longer_connected": "This tab is no longer connected with the editor",

View File

@@ -83,6 +83,7 @@
"@contentful/rich-text-html-renderer": "^16.0.2",
"@contentful/rich-text-types": "^16.0.2",
"@google-cloud/bigquery": "^6.0.1",
"@google-cloud/storage": "^6.10.1",
"@node-oauth/oauth2-server": "^5.1.0",
"@node-saml/passport-saml": "^5.1.0",
"@overleaf/access-token-encryptor": "*",

Some files were not shown because too many files have changed in this diff Show More