@@ -59,102 +131,144 @@ const permissions = {
write: true,
}
-export const Latex = (args: any, { globals: { theme } }: any) => {
- // FIXME: useScope has no effect
- useScope({
- editor: {
- sharejs_doc: mockDoc(content.tex, changes.tex),
- open_doc_name: 'example.tex',
- },
- rootFolder: {
- name: 'rootFolder',
- id: 'root-folder-id',
- type: 'folder',
- children: [
- {
- name: 'example.tex.tex',
- id: 'example-doc-id',
- type: 'doc',
+export const Latex: Story = {
+ decorators: [
+ Story =>
+ ScopeDecorator(Story, {
+ mockCompileOnLoad: true,
+ providers: {
+ FileTreePathProvider,
+ EditorOpenDocProvider: LatexEditorOpenDocProvider,
+ },
+ }),
+
+ (Story, { globals }) => {
+ // FIXME: useScope has no effect
+ useScope({
+ rootFolder: {
+ name: 'rootFolder',
+ id: 'root-folder-id',
+ type: 'folder',
+ children: [
+ {
+ name: 'example.tex.tex',
+ id: 'example-doc-id',
+ type: 'doc',
+ selected: false,
+ $$hashKey: 'object:89',
+ },
+ {
+ name: 'frog.jpg',
+ id: 'frog-image-id',
+ type: 'file',
+ linkedFileData: null,
+ created: '2023-05-04T16:11:04.352Z',
+ $$hashKey: 'object:108',
+ },
+ ],
selected: false,
- $$hashKey: 'object:89',
},
- {
- name: 'frog.jpg',
- id: 'frog-image-id',
- type: 'file',
- linkedFileData: null,
- created: '2023-05-04T16:11:04.352Z',
- $$hashKey: 'object:108',
+ settings: {
+ ...settings,
+ overallTheme: globals.theme === 'default-' ? '' : globals.theme,
},
- ],
- selected: false,
- },
- settings: {
- ...settings,
- overallTheme: theme === 'default-' ? '' : theme,
- },
- permissions,
- })
+ permissions,
+ })
- useMeta({
- 'ol-showSymbolPalette': true,
- })
+ useMeta({
+ 'ol-showSymbolPalette': true,
+ })
- return
+ return
+ },
+ ],
}
-export const Markdown = (args: any, { globals: { theme } }: any) => {
- useScope({
- editor: {
- sharejs_doc: mockDoc(content.md, changes.md),
- open_doc_name: 'example.md',
- },
- settings: {
- ...settings,
- overallTheme: theme === 'default-' ? '' : theme,
- },
- permissions,
- })
+export const Markdown: Story = {
+ decorators: [
+ Story =>
+ ScopeDecorator(Story, {
+ mockCompileOnLoad: true,
+ providers: {
+ FileTreePathProvider,
+ EditorOpenDocProvider: MarkdownEditorOpenDocProvider,
+ },
+ }),
- return
+ (Story, { globals }) => {
+ // FIXME: useScope has no effect
+ useScope({
+ settings: {
+ ...settings,
+ overallTheme: globals.theme === 'default-' ? '' : globals.theme,
+ },
+ permissions,
+ })
+
+ return
+ },
+ ],
}
-export const Visual = (args: any, { globals: { theme } }: any) => {
- useScope({
- editor: {
- sharejs_doc: mockDoc(content.tex, changes.tex),
- open_doc_name: 'example.tex',
- showVisual: true,
- },
- settings: {
- ...settings,
- overallTheme: theme === 'default-' ? '' : theme,
- },
- permissions,
- })
- useMeta({
- 'ol-showSymbolPalette': true,
- 'ol-mathJaxPath': 'https://unpkg.com/mathjax@3.2.2/es5/tex-svg-full.js',
- 'ol-project_id': '63e21c07946dd8c76505f85a',
- })
+export const Visual: Story = {
+ decorators: [
+ Story =>
+ ScopeDecorator(Story, {
+ mockCompileOnLoad: true,
+ providers: {
+ FileTreePathProvider,
+ EditorOpenDocProvider: LatexEditorOpenDocProvider,
+ },
+ }),
- return
+ (Story, { globals }) => {
+ // FIXME: useScope has no effect, so this does not default to the visual editor
+ useScope({
+ editor: {
+ showVisual: true,
+ },
+ settings: {
+ ...settings,
+ overallTheme: globals.theme === 'default-' ? '' : globals.theme,
+ },
+ permissions,
+ })
+
+ useMeta({
+ 'ol-showSymbolPalette': true,
+ 'ol-mathJaxPath': 'https://unpkg.com/mathjax@3.2.2/es5/tex-svg-full.js',
+ 'ol-project_id': '63e21c07946dd8c76505f85a',
+ })
+
+ return
+ },
+ ],
}
-export const Bibtex = (args: any, { globals: { theme } }: any) => {
- useScope({
- editor: {
- sharejs_doc: mockDoc(content.bib, changes.bib),
- open_doc_name: 'example.bib',
- },
- settings: {
- ...settings,
- overallTheme: theme === 'default-' ? '' : theme,
- },
- permissions,
- })
+export const Bibtex: Story = {
+ decorators: [
+ Story =>
+ ScopeDecorator(Story, {
+ mockCompileOnLoad: true,
+ providers: {
+ FileTreePathProvider,
+ EditorOpenDocProvider: BibtexEditorOpenDocProvider,
+ },
+ }),
- return
+ (Story, { globals }) => {
+ // FIXME: useScope has no effect
+ useScope({
+ settings: {
+ ...settings,
+ overallTheme: globals.theme === 'default-' ? '' : globals.theme,
+ },
+ permissions,
+ })
+
+ return
+ },
+ ],
}
const MAX_DOC_LENGTH = 2 * 1024 * 1024 // ol-maxDocLength
diff --git a/services/web/test/frontend/components/pdf-preview/pdf-logs-entries.spec.tsx b/services/web/test/frontend/components/pdf-preview/pdf-logs-entries.spec.tsx
index 60326d8d3f..a3067b566e 100644
--- a/services/web/test/frontend/components/pdf-preview/pdf-logs-entries.spec.tsx
+++ b/services/web/test/frontend/components/pdf-preview/pdf-logs-entries.spec.tsx
@@ -11,6 +11,7 @@ import {
import { EditorView } from '@codemirror/view'
import { OpenDocuments } from '@/features/ide-react/editor/open-documents'
import { LogEntry } from '@/features/pdf-preview/util/types'
+import { EditorViewContext } from '@/features/ide-react/context/editor-view-context'
describe('
', function () {
const fakeFindEntityResult: FindResult = {
@@ -48,6 +49,19 @@ describe('
', function () {
)
}
+ const EditorViewProvider: FC
= ({ children }) => {
+ const value = {
+ view: new EditorView({ doc: '\\documentclass{article}' }),
+ setView: cy.stub(),
+ }
+
+ return (
+
+ {children}
+
+ )
+ }
+
const logEntries: LogEntry[] = [
{
file: 'main.tex',
@@ -62,10 +76,6 @@ describe('', function () {
},
]
- const scope = {
- 'editor.view': new EditorView({ doc: '\\documentclass{article}' }),
- }
-
beforeEach(function () {
cy.interceptCompile()
cy.interceptEvents()
@@ -73,7 +83,7 @@ describe('', function () {
it('displays human readable hint', function () {
cy.mount(
-
+
)
@@ -84,8 +94,11 @@ describe('', function () {
it('opens doc on click', function () {
cy.mount(
@@ -114,8 +127,11 @@ describe('', function () {
cy.mount(
@@ -154,8 +170,11 @@ describe('', function () {
cy.mount(
diff --git a/services/web/test/frontend/components/pdf-preview/pdf-synctex-controls.spec.tsx b/services/web/test/frontend/components/pdf-preview/pdf-synctex-controls.spec.tsx
index b2a64b89a4..f8c379c51e 100644
--- a/services/web/test/frontend/components/pdf-preview/pdf-synctex-controls.spec.tsx
+++ b/services/web/test/frontend/components/pdf-preview/pdf-synctex-controls.spec.tsx
@@ -3,7 +3,10 @@ import { cloneDeep } from 'lodash'
import { useDetachCompileContext as useCompileContext } from '../../../../frontend/js/shared/context/detach-compile-context'
import { useFileTreeData } from '../../../../frontend/js/shared/context/file-tree-data-context'
import { useEffect } from 'react'
-import { EditorProviders } from '../../helpers/editor-providers'
+import {
+ EditorProviders,
+ makeEditorOpenDocProvider,
+} from '../../helpers/editor-providers'
import { mockScope } from './scope'
import { detachChannel, testDetachChannel } from '../../helpers/detach-channel'
import { FindResult } from '@/features/file-tree/util/path'
@@ -73,6 +76,22 @@ const WithSelectedEntities = ({
return null
}
+function mockProviders() {
+ return {
+ EditorOpenDocProvider: makeEditorOpenDocProvider({
+ openDocName: 'main.tex',
+ currentDocument: {
+ doc_id: 'test-doc',
+ getSnapshot: () => 'some doc content',
+ hasBufferedOps: () => false,
+ on: () => {},
+ off: () => {},
+ leaveAndCleanUpPromise: () => Promise.resolve(),
+ },
+ }),
+ }
+}
+
describe('', function () {
beforeEach(function () {
window.metaAttributesCache.set('ol-project_id', 'test-project')
@@ -84,9 +103,10 @@ describe('', function () {
cy.interceptCompile()
const scope = mockScope()
+ const providers = mockProviders()
cy.mount(
-
+
@@ -145,9 +165,10 @@ describe('', function () {
cy.interceptCompile()
const scope = mockScope()
+ const providers = mockProviders()
cy.mount(
-
+
', function () {
cy.interceptCompile()
const scope = mockScope()
+ const providers = mockProviders()
cy.mount(
-
+
', function () {
cy.interceptCompile()
const scope = mockScope()
+ const providers = mockProviders()
cy.mount(
-
+
@@ -218,9 +241,10 @@ describe('', function () {
cy.interceptCompile()
const scope = mockScope()
+ const providers = mockProviders()
cy.mount(
-
+
@@ -279,9 +303,10 @@ describe('', function () {
cy.interceptCompile()
const scope = mockScope()
+ const providers = mockProviders()
cy.mount(
-
+
@@ -317,9 +342,10 @@ describe('', function () {
cy.interceptCompile()
const scope = mockScope()
+ const providers = mockProviders()
cy.mount(
-
+
@@ -338,9 +364,10 @@ describe('', function () {
cy.interceptCompile()
const scope = mockScope()
+ const providers = mockProviders()
cy.mount(
-
+
)
@@ -385,9 +412,10 @@ describe('', function () {
cy.interceptCompile()
const scope = mockScope()
+ const providers = mockProviders()
cy.mount(
-
+
diff --git a/services/web/test/frontend/components/pdf-preview/scope.tsx b/services/web/test/frontend/components/pdf-preview/scope.tsx
index e174efbf73..397813faa2 100644
--- a/services/web/test/frontend/components/pdf-preview/scope.tsx
+++ b/services/web/test/frontend/components/pdf-preview/scope.tsx
@@ -6,7 +6,6 @@ export const mockScope = () => ({
pdfViewer: 'pdfjs',
},
editor: {
- open_doc_name: 'main.tex',
sharejs_doc: {
doc_id: 'test-doc',
getSnapshot: () => 'some doc content',
diff --git a/services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.jsx b/services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.jsx
index dfce8134d1..793ff7b16d 100644
--- a/services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.jsx
+++ b/services/web/test/frontend/features/share-project-modal/components/share-project-modal.test.jsx
@@ -12,6 +12,7 @@ import {
USER_ID,
} from '../../../helpers/editor-providers'
import { location } from '@/shared/components/location'
+import useScopeValue from '@/shared/hooks/use-scope-value'
async function changePrivilegeLevel(screen, { current, next }) {
const select = screen.getByDisplayValue(current)
@@ -820,7 +821,14 @@ describe('', function () {
fetchMock.get(`/project/${project._id}/tokens`, {})
fetchMock.post('express:/project/:projectId/settings/admin', 204)
- renderWithEditorContext(, {
+ let setPublicAccessLevel = function () {}
+
+ function WrappedModal() {
+ setPublicAccessLevel = useScopeValue('project.publicAccesLevel')[1]
+ return
+ }
+
+ renderWithEditorContext(, {
scope: {
project: { ...project, publicAccesLevel: 'private' },
},
@@ -839,13 +847,10 @@ describe('', function () {
publicAccessLevel: 'tokenBased',
})
- // NOTE: updating the scoped project data manually,
- // as the project data is usually updated via the websocket connection
- window.overleaf.unstable.store.set('project', {
- ...project,
- publicAccesLevel: 'tokenBased',
- })
- // watchCallbacks.project({ ...project, publicAccesLevel: 'tokenBased' })
+ // NOTE: the project data is usually updated via the websocket connection
+ // but we can't do that so we're doing it via the scope value store (this
+ // will be via the project context when this value has been migrated)
+ setPublicAccessLevel('tokenBased')
await screen.findByText('Link sharing is on')
const disableButton = await screen.findByRole('button', {
@@ -859,13 +864,7 @@ describe('', function () {
publicAccessLevel: 'private',
})
- // NOTE: updating the scoped project data manually,
- // as the project data is usually updated via the websocket connection
- window.overleaf.unstable.store.set('project', {
- ...project,
- publicAccesLevel: 'private',
- })
- // watchCallbacks.project({ ...project, publicAccesLevel: 'private' })
+ setPublicAccessLevel('private')
await screen.findByText('Link sharing is off')
})
diff --git a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts
index 1f78dcf93a..701e3bc4b3 100644
--- a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts
+++ b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts
@@ -17,8 +17,8 @@ export const mockScope = (
return {
editor: {
sharejs_doc: mockDoc(content, docOptions),
- open_doc_name: 'test.tex',
- open_doc_id: docId,
+ openDocName: 'test.tex',
+ currentDocumentId: docId,
showVisual: false,
wantTrackChanges: false,
},
diff --git a/services/web/test/frontend/helpers/editor-providers.jsx b/services/web/test/frontend/helpers/editor-providers.jsx
index 4f722525c7..f2ed404f66 100644
--- a/services/web/test/frontend/helpers/editor-providers.jsx
+++ b/services/web/test/frontend/helpers/editor-providers.jsx
@@ -9,12 +9,15 @@ import {
IdeReactContext,
} from '@/features/ide-react/context/ide-react-context'
import { IdeEventEmitter } from '@/features/ide-react/create-ide-event-emitter'
+import { ReactScopeValueStore } from '@/features/ide-react/scope-value-store/react-scope-value-store'
import { ReactScopeEventEmitter } from '@/features/ide-react/scope-event-emitter/react-scope-event-emitter'
import { ConnectionContext } from '@/features/ide-react/context/connection-context'
+import { EditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context'
import { ReactContextRoot } from '@/features/ide-react/context/react-context-root'
import useEventListener from '@/shared/hooks/use-event-listener'
import useDetachLayout from '@/shared/hooks/use-detach-layout'
import { LayoutContext } from '@/shared/context/layout-context'
+import useExposedState from '@/shared/hooks/use-exposed-state'
// these constants can be imported in tests instead of
// using magic strings
@@ -119,6 +122,8 @@ export function EditorProviders({
off: () => {},
leaveAndCleanUpPromise: async () => {},
},
+ openDocName: null,
+ currentDocumentId: null,
},
project: {
_id: projectId,
@@ -144,6 +149,11 @@ export function EditorProviders({
providers={{
ConnectionProvider: makeConnectionProvider(socket),
IdeReactProvider: makeIdeReactProvider(scope, socket),
+ EditorOpenDocProvider: makeEditorOpenDocProvider({
+ openDocId: scope.editor.currentDocumentId,
+ openDocName: scope.editor.openDocName,
+ currentDocument: scope.editor.sharejs_doc,
+ }),
LayoutProvider: makeLayoutProvider(layoutContext),
...providers,
}}
@@ -202,15 +212,16 @@ const makeIdeReactProvider = (scope, socket) => {
// TODO: path for nested entries
scopeStore.set(key, value)
}
- scopeStore.set('editor.sharejs_doc', scope.editor.sharejs_doc)
const scopeEventEmitter = new ReactScopeEventEmitter(
new IdeEventEmitter()
)
+ const unstableStore = new ReactScopeValueStore()
return {
socket,
scopeStore,
scopeEventEmitter,
+ unstableStore,
}
})
@@ -219,10 +230,10 @@ const makeIdeReactProvider = (scope, socket) => {
...window.overleaf,
unstable: {
...window.overleaf?.unstable,
- store: ideContextValue.scopeStore,
+ store: ideContextValue.unstableStore,
},
}
- }, [ideContextValue.scopeStore])
+ }, [ideContextValue.unstableStore])
return (
@@ -235,6 +246,44 @@ const makeIdeReactProvider = (scope, socket) => {
return IdeReactProvider
}
+export function makeEditorOpenDocProvider(initialValues) {
+ const {
+ currentDocumentId: initialCurrentDocumentId,
+ openDocName: initialOpenDocName,
+ currentDocument: initialCurrentDocument,
+ } = initialValues
+ const EditorOpenDocProvider = ({ children }) => {
+ const [currentDocumentId, setCurrentDocumentId] = useExposedState(
+ initialCurrentDocumentId,
+ 'editor.open_doc_id'
+ )
+ const [openDocName, setOpenDocName] = useExposedState(
+ initialOpenDocName,
+ 'editor.open_doc_name'
+ )
+ const [currentDocument, setCurrentDocument] = useState(
+ initialCurrentDocument
+ )
+
+ const value = {
+ currentDocumentId,
+ setCurrentDocumentId,
+ openDocName,
+ setOpenDocName,
+ currentDocument,
+ setCurrentDocument,
+ }
+
+ return (
+
+ {children}
+
+ )
+ }
+
+ return EditorOpenDocProvider
+}
+
const makeLayoutProvider = layoutContextOverrides => {
const layout = {
...layoutContextDefault,
From affd1bea497db5a7f53ed949adc0670211d25892 Mon Sep 17 00:00:00 2001
From: Tim Down <158919+timdown@users.noreply.github.com>
Date: Wed, 2 Jul 2025 08:53:42 +0100
Subject: [PATCH 060/253] Merge pull request #26586 from
overleaf/td-file-view-error-reset
Reset file view loading and error state when switching between files
GitOrigin-RevId: 44be9bf71fe9541ed78db3421bf356997850e6ec
---
.../js/features/ide-react/components/editor-and-pdf.tsx | 5 ++++-
.../js/features/ide-redesign/components/editor-panel.tsx | 5 ++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/services/web/frontend/js/features/ide-react/components/editor-and-pdf.tsx b/services/web/frontend/js/features/ide-react/components/editor-and-pdf.tsx
index 6a9e4cd441..a7ae12515e 100644
--- a/services/web/frontend/js/features/ide-react/components/editor-and-pdf.tsx
+++ b/services/web/frontend/js/features/ide-react/components/editor-and-pdf.tsx
@@ -58,7 +58,10 @@ export const EditorAndPdf: FC = () => {
>
{selectedEntityCount === 0 && }
{selectedEntityCount === 1 && openEntity?.type === 'fileRef' && (
-
+
)}
{selectedEntityCount > 1 && (
diff --git a/services/web/frontend/js/features/ide-redesign/components/editor-panel.tsx b/services/web/frontend/js/features/ide-redesign/components/editor-panel.tsx
index 18fcad1395..d8204da5da 100644
--- a/services/web/frontend/js/features/ide-redesign/components/editor-panel.tsx
+++ b/services/web/frontend/js/features/ide-redesign/components/editor-panel.tsx
@@ -12,7 +12,10 @@ export default function EditorPanel() {
{selectedEntityCount === 0 &&
}
{selectedEntityCount === 1 && openEntity?.type === 'fileRef' && (
-
+
)}
{selectedEntityCount > 1 && (
From ebb2cff2afd5fecdfca452d975f131a6623d879e Mon Sep 17 00:00:00 2001
From: Tim Down <158919+timdown@users.noreply.github.com>
Date: Wed, 2 Jul 2025 08:54:46 +0100
Subject: [PATCH 061/253] Merge pull request #26697 from
overleaf/td-custom-logo-sp
Restore custom logo feature on SP project dashboard
GitOrigin-RevId: c3ceafa8756968bfbb92f3fca22889e11a39dc40
---
.../components/project-list-ds-nav.tsx | 6 ++++-
.../bootstrap-5/navbar/default-navbar.tsx | 22 ++++++++++++++++---
.../navbar/header-logo-or-title.tsx | 15 ++++++++++---
.../js/shared/svgs/overleaf-black.svg | 9 ++++++++
.../js/shared/svgs/overleaf-white.svg | 1 +
.../bootstrap-5/components/nav.scss | 5 ++++-
.../bootstrap-5/components/navbar.scss | 5 ++++-
.../types/css-properties-with-variables.tsx | 4 ++++
8 files changed, 58 insertions(+), 9 deletions(-)
create mode 100644 services/web/frontend/js/shared/svgs/overleaf-black.svg
create mode 100644 services/web/frontend/js/shared/svgs/overleaf-white.svg
create mode 100644 services/web/types/css-properties-with-variables.tsx
diff --git a/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx b/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
index 8f3b3a8e5d..f8c8014e1c 100644
--- a/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
+++ b/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
@@ -55,7 +55,11 @@ export function ProjectListDsNav() {
return (
-
+
diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/navbar/default-navbar.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/navbar/default-navbar.tsx
index 2480b7f061..8e5429dbde 100644
--- a/services/web/frontend/js/features/ui/components/bootstrap-5/navbar/default-navbar.tsx
+++ b/services/web/frontend/js/features/ui/components/bootstrap-5/navbar/default-navbar.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react'
+import React, { useState } from 'react'
import { sendMB } from '@/infrastructure/event-tracking'
import { useTranslation } from 'react-i18next'
import { Button, Container, Nav, Navbar } from 'react-bootstrap'
@@ -13,9 +13,15 @@ import MaterialIcon from '@/shared/components/material-icon'
import { useContactUsModal } from '@/shared/hooks/use-contact-us-modal'
import { UserProvider } from '@/shared/context/user-context'
import { X } from '@phosphor-icons/react'
+import overleafWhiteLogo from '@/shared/svgs/overleaf-white.svg'
+import overleafBlackLogo from '@/shared/svgs/overleaf-black.svg'
+import type { CSSPropertiesWithVariables } from '../../../../../../../types/css-properties-with-variables'
-function DefaultNavbar(props: DefaultNavbarMetadata) {
+function DefaultNavbar(
+ props: DefaultNavbarMetadata & { overleafLogo?: string }
+) {
const {
+ overleafLogo,
customLogo,
title,
canDisplayAdminMenu,
@@ -49,10 +55,20 @@ function DefaultNavbar(props: DefaultNavbarMetadata) {
className="navbar-default navbar-main"
expand="lg"
onToggle={expanded => setExpanded(expanded)}
+ style={
+ {
+ '--navbar-brand-image-default-url': `url("${overleafWhiteLogo}")`,
+ '--navbar-brand-image-redesign-url': `url("${overleafBlackLogo}")`,
+ } as CSSPropertiesWithVariables
+ }
>
-
+
{enableUpgradeButton ? (