diff --git a/services/web/app/src/Features/Project/UserSettingsHelper.mjs b/services/web/app/src/Features/Project/UserSettingsHelper.mjs index e99c74731a..cad1e6e709 100644 --- a/services/web/app/src/Features/Project/UserSettingsHelper.mjs +++ b/services/web/app/src/Features/Project/UserSettingsHelper.mjs @@ -30,6 +30,7 @@ async function buildUserSettings(_req, _res, user) { overallTheme: getOverallTheme(user), mathPreview: user.ace.mathPreview, breadcrumbs: user.ace.breadcrumbs, + editorTabs: user.ace.editorTabs ?? true, nonBlinkingCursor: user.ace.nonBlinkingCursor ?? false, referencesSearchMode: user.ace.referencesSearchMode, darkModePdf: user.ace.darkModePdf ?? false, diff --git a/services/web/app/src/Features/User/UserController.mjs b/services/web/app/src/Features/User/UserController.mjs index ac88451221..782f9d18e2 100644 --- a/services/web/app/src/Features/User/UserController.mjs +++ b/services/web/app/src/Features/User/UserController.mjs @@ -425,6 +425,9 @@ async function updateUserSettings(req, res, next) { if (body.breadcrumbs != null) { user.ace.breadcrumbs = Boolean(body.breadcrumbs) } + if (body.editorTabs != null) { + user.ace.editorTabs = Boolean(body.editorTabs) + } if (body.nonBlinkingCursor != null) { user.ace.nonBlinkingCursor = Boolean(body.nonBlinkingCursor) } diff --git a/services/web/app/src/models/User.mjs b/services/web/app/src/models/User.mjs index 4e21612f18..f992a2979d 100644 --- a/services/web/app/src/models/User.mjs +++ b/services/web/app/src/models/User.mjs @@ -106,6 +106,7 @@ export const UserSchema = new Schema( lineHeight: { type: String }, mathPreview: { type: Boolean, default: true }, breadcrumbs: { type: Boolean, default: true }, + editorTabs: { type: Boolean, default: true }, nonBlinkingCursor: { type: Boolean, default: false }, referencesSearchMode: { type: String, default: 'advanced' }, // 'advanced' or 'simple' darkModePdf: { type: Boolean, default: false }, diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index c053303570..26263f3134 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -1358,7 +1358,9 @@ "only_project_owner_can_link_github": "", "open_action_menu": "", "open_advanced_reference_search": "", + "open_each_file_in_its_own_tab": "", "open_file": "", + "open_files_in_tabs": "", "open_link": "", "open_path": "", "open_pdf_in_separate_tab": "", @@ -1837,6 +1839,7 @@ "show_all_projects": "", "show_breadcrumbs": "", "show_document_preamble": "", + "show_editor_tabs": "", "show_equation_preview": "", "show_file_tree": "", "show_in_code": "", diff --git a/services/web/frontend/js/features/editor-left-menu/context/project-settings-context.tsx b/services/web/frontend/js/features/editor-left-menu/context/project-settings-context.tsx index d8396a9a61..6483cc7c4d 100644 --- a/services/web/frontend/js/features/editor-left-menu/context/project-settings-context.tsx +++ b/services/web/frontend/js/features/editor-left-menu/context/project-settings-context.tsx @@ -33,6 +33,7 @@ type ProjectSettingsSetterContextValue = { setPdfViewer: (pdfViewer: UserSettings['pdfViewer']) => void setMathPreview: (mathPreview: UserSettings['mathPreview']) => void setBreadcrumbs: (breadcrumbs: UserSettings['breadcrumbs']) => void + setEditorTabs: (editorTabs: UserSettings['editorTabs']) => void setNonBlinkingCursor: ( nonBlinkingCursor: UserSettings['nonBlinkingCursor'] ) => void @@ -95,6 +96,8 @@ export const ProjectSettingsProvider: FC = ({ setMathPreview, breadcrumbs, setBreadcrumbs, + editorTabs, + setEditorTabs, nonBlinkingCursor, setNonBlinkingCursor, darkModePdf, @@ -149,6 +152,8 @@ export const ProjectSettingsProvider: FC = ({ setMathPreview, breadcrumbs, setBreadcrumbs, + editorTabs, + setEditorTabs, nonBlinkingCursor, setNonBlinkingCursor, darkModePdf, @@ -199,6 +204,8 @@ export const ProjectSettingsProvider: FC = ({ setMathPreview, breadcrumbs, setBreadcrumbs, + editorTabs, + setEditorTabs, nonBlinkingCursor, setNonBlinkingCursor, darkModePdf, diff --git a/services/web/frontend/js/features/editor-left-menu/hooks/use-user-wide-settings.tsx b/services/web/frontend/js/features/editor-left-menu/hooks/use-user-wide-settings.tsx index ce6e162ba8..92658eb580 100644 --- a/services/web/frontend/js/features/editor-left-menu/hooks/use-user-wide-settings.tsx +++ b/services/web/frontend/js/features/editor-left-menu/hooks/use-user-wide-settings.tsx @@ -24,6 +24,7 @@ export default function useUserWideSettings() { pdfViewer, mathPreview, breadcrumbs, + editorTabs, nonBlinkingCursor, darkModePdf, zotero, @@ -130,6 +131,13 @@ export default function useUserWideSettings() { [saveUserSettings] ) + const setEditorTabs = useCallback( + (editorTabs: UserSettings['editorTabs']) => { + saveUserSettings('editorTabs', editorTabs) + }, + [saveUserSettings] + ) + const setNonBlinkingCursor = useCallback( (nonBlinkingCursor: UserSettings['nonBlinkingCursor']) => { saveUserSettings('nonBlinkingCursor', nonBlinkingCursor) @@ -196,6 +204,8 @@ export default function useUserWideSettings() { setMathPreview, breadcrumbs, setBreadcrumbs, + editorTabs, + setEditorTabs, nonBlinkingCursor, setNonBlinkingCursor, darkModePdf, diff --git a/services/web/frontend/js/features/ide-react/components/editor/editor-panel.tsx b/services/web/frontend/js/features/ide-react/components/editor/editor-panel.tsx index f65f45b74a..6a52be9e70 100644 --- a/services/web/frontend/js/features/ide-react/components/editor/editor-panel.tsx +++ b/services/web/frontend/js/features/ide-react/components/editor/editor-panel.tsx @@ -5,11 +5,11 @@ import FileView from '@/features/file-view/components/file-view' import { fileViewFile } from '@/features/ide-react/util/file-view' import MultipleSelectionPane from '@/features/ide-react/components/editor/multiple-selection-pane' import { TabsContainer } from '@/features/source-editor/components/tabs/tabs-container' -import { isSplitTestEnabled } from '@/utils/splitTestUtils' +import { useAreTabsEnabled } from '../../hooks/use-are-tabs-enabled' export default function EditorPanel() { const { selectedEntityCount, openEntity } = useFileTreeOpenContext() - const tabsEnabled = isSplitTestEnabled('editor-tabs') + const tabsEnabled = useAreTabsEnabled() return (
diff --git a/services/web/frontend/js/features/ide-react/components/toolbar/menu-bar.tsx b/services/web/frontend/js/features/ide-react/components/toolbar/menu-bar.tsx index 1817487216..928ecf2797 100644 --- a/services/web/frontend/js/features/ide-react/components/toolbar/menu-bar.tsx +++ b/services/web/frontend/js/features/ide-react/components/toolbar/menu-bar.tsx @@ -25,6 +25,7 @@ import getMeta from '@/utils/meta' import EditorCloneProjectModalWrapper from '@/features/clone-project-modal/components/editor-clone-project-modal-wrapper' import useOpenProject from '@/shared/hooks/use-open-project' import importOverleafModules from '../../../../../macros/import-overleaf-module.macro' +import { useFeatureFlag } from '@/shared/context/split-test-context' const menubarExtraComponents = importOverleafModules( 'menubarExtraComponents' @@ -46,6 +47,8 @@ export const ToolbarMenuBar = () => { const showSupport = getMeta('ol-showSupport') const showDocumentation = getMeta('ol-wikiEnabled') + const hasEditorTabs = useFeatureFlag('editor-tabs') + useCommandProvider( () => [ { @@ -204,8 +207,14 @@ export const ToolbarMenuBar = () => { [t] ) - const { mathPreview, setMathPreview, breadcrumbs, setBreadcrumbs } = - useProjectSettingsContext() + const { + mathPreview, + setMathPreview, + breadcrumbs, + setBreadcrumbs, + editorTabs, + setEditorTabs, + } = useProjectSettingsContext() const toggleMathPreview = useCallback(() => { setMathPreview(!mathPreview) @@ -215,6 +224,10 @@ export const ToolbarMenuBar = () => { setBreadcrumbs(!breadcrumbs) }, [setBreadcrumbs, breadcrumbs]) + const toggleEditorTabs = useCallback(() => { + setEditorTabs(!editorTabs) + }, [setEditorTabs, editorTabs]) + const { setActiveModal } = useRailContext() const openKeyboardShortcutsModal = useCallback(() => { setActiveModal('keyboard-shortcuts') @@ -252,6 +265,16 @@ export const ToolbarMenuBar = () => { } onClick={toggleBreadcrumbs} /> + {hasEditorTabs && ( + + } + onClick={toggleEditorTabs} + /> + )} = ({ children }) => { const { openEntity } = useFileTreeOpenContext() const { openDocWithId, openFileWithId } = useEditorManagerContext() - const tabsEnabled = isSplitTestEnabled('editor-tabs') + const tabsEnabled = useAreTabsEnabled() const { userSettings } = useUserSettingsContext() const { previewTabs } = userSettings diff --git a/services/web/frontend/js/features/ide-react/hooks/use-are-tabs-enabled.ts b/services/web/frontend/js/features/ide-react/hooks/use-are-tabs-enabled.ts new file mode 100644 index 0000000000..635fadaa94 --- /dev/null +++ b/services/web/frontend/js/features/ide-react/hooks/use-are-tabs-enabled.ts @@ -0,0 +1,8 @@ +import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context' +import { useFeatureFlag } from '@/shared/context/split-test-context' + +export const useAreTabsEnabled = () => { + const { editorTabs } = useProjectSettingsContext() + const featureFlag = useFeatureFlag('editor-tabs') + return featureFlag && Boolean(editorTabs) +} diff --git a/services/web/frontend/js/features/review-panel/components/review-panel-tabs-header-portal.tsx b/services/web/frontend/js/features/review-panel/components/review-panel-tabs-header-portal.tsx index 5e0ea456eb..96437295d7 100644 --- a/services/web/frontend/js/features/review-panel/components/review-panel-tabs-header-portal.tsx +++ b/services/web/frontend/js/features/review-panel/components/review-panel-tabs-header-portal.tsx @@ -2,11 +2,11 @@ import { FC } from 'react' import { createPortal } from 'react-dom' import ReviewPanelHeader from './review-panel-header' import { useTabsContext } from '@/features/ide-react/context/tabs-context' -import { useFeatureFlag } from '@/shared/context/split-test-context' import useReviewPanelLayout from '../hooks/use-review-panel-layout' +import { useAreTabsEnabled } from '@/features/ide-react/hooks/use-are-tabs-enabled' const ReviewPanelTabsHeaderPortal: FC = () => { - const editorTabsEnabled = useFeatureFlag('editor-tabs') + const editorTabsEnabled = useAreTabsEnabled() const { headerSlot } = useTabsContext() const { showHeader } = useReviewPanelLayout() diff --git a/services/web/frontend/js/features/settings/components/editor-settings/editor-tabs-setting.tsx b/services/web/frontend/js/features/settings/components/editor-settings/editor-tabs-setting.tsx new file mode 100644 index 0000000000..404a433e67 --- /dev/null +++ b/services/web/frontend/js/features/settings/components/editor-settings/editor-tabs-setting.tsx @@ -0,0 +1,18 @@ +import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context' +import { useTranslation } from 'react-i18next' +import ToggleSetting from '../toggle-setting' + +export default function EditorTabsSetting() { + const { editorTabs, setEditorTabs } = useProjectSettingsContext() + const { t } = useTranslation() + + return ( + + ) +} diff --git a/services/web/frontend/js/features/settings/components/editor-settings/preview-tabs-setting.tsx b/services/web/frontend/js/features/settings/components/editor-settings/preview-tabs-setting.tsx index c4ef544c0a..d0391a5dfc 100644 --- a/services/web/frontend/js/features/settings/components/editor-settings/preview-tabs-setting.tsx +++ b/services/web/frontend/js/features/settings/components/editor-settings/preview-tabs-setting.tsx @@ -3,7 +3,8 @@ import ToggleSetting from '../toggle-setting' import { useTranslation } from 'react-i18next' export default function PreviewTabsSetting() { - const { previewTabs, setPreviewTabs } = useProjectSettingsContext() + const { previewTabs, setPreviewTabs, editorTabs } = + useProjectSettingsContext() const { t } = useTranslation() return ( @@ -13,6 +14,7 @@ export default function PreviewTabsSetting() { description={t('tabs_open_in_preview_mode_until_you_interact_with_them')} checked={previewTabs} onChange={setPreviewTabs} + disabled={!editorTabs} /> ) } diff --git a/services/web/frontend/js/features/settings/context/settings-modal-context.tsx b/services/web/frontend/js/features/settings/context/settings-modal-context.tsx index bf3da53043..062b9688e4 100644 --- a/services/web/frontend/js/features/settings/context/settings-modal-context.tsx +++ b/services/web/frontend/js/features/settings/context/settings-modal-context.tsx @@ -36,6 +36,7 @@ import type { SettingsSection, SettingsSectionHook, } from '@/features/settings/context/types' +import EditorTabsSetting from '../components/editor-settings/editor-tabs-setting' const [referenceSearchSettingModule] = importOverleafModules( 'referenceSearchSetting' @@ -111,6 +112,11 @@ export const SettingsModalProvider: FC = ({ key: 'syntaxValidation', component: , }, + { + key: 'editorTabs', + component: , + hidden: !hasEditorTabs, + }, { key: 'previewTabs', component: , diff --git a/services/web/frontend/js/features/source-editor/components/codemirror-toolbar.tsx b/services/web/frontend/js/features/source-editor/components/codemirror-toolbar.tsx index b9ba81a92f..f1f325ae6d 100644 --- a/services/web/frontend/js/features/source-editor/components/codemirror-toolbar.tsx +++ b/services/web/frontend/js/features/source-editor/components/codemirror-toolbar.tsx @@ -36,6 +36,7 @@ import { useFeatureFlag } from '@/shared/context/split-test-context' import importOverleafModules from '../../../../macros/import-overleaf-module.macro' import { useLayoutContext } from '@/shared/context/layout-context' import ReviewPanelHeaderBuffer from '@/features/review-panel/components/review-panel-header-buffer' +import { useAreTabsEnabled } from '@/features/ide-react/hooks/use-are-tabs-enabled' const sourceEditorToolbarComponents = importOverleafModules( 'sourceEditorToolbarComponents' @@ -160,7 +161,7 @@ const Toolbar = memo(function Toolbar() { }, [buildOverflow, insideTable, resizeRef]) const showActions = !state.readOnly && !insideTable - const tabsVisible = useFeatureFlag('editor-tabs') + const tabsVisible = useAreTabsEnabled() if (focusMode) { return null diff --git a/services/web/frontend/js/features/source-editor/components/tabs/tab.tsx b/services/web/frontend/js/features/source-editor/components/tabs/tab.tsx index fa9e15045c..3c0b10c162 100644 --- a/services/web/frontend/js/features/source-editor/components/tabs/tab.tsx +++ b/services/web/frontend/js/features/source-editor/components/tabs/tab.tsx @@ -98,6 +98,9 @@ export const Tab = memo(function Tab({ const onDragLeave = useCallback( (e: React.DragEvent) => { e.stopPropagation() + if (e.currentTarget.contains(e.relatedTarget as Node | null)) { + return + } throttledOnDragOver.cancel() setDropTargetPosition(null) }, diff --git a/services/web/frontend/js/features/source-editor/components/tabs/tabs-context-menu.tsx b/services/web/frontend/js/features/source-editor/components/tabs/tabs-context-menu.tsx index 774c78c360..c6bb59e919 100644 --- a/services/web/frontend/js/features/source-editor/components/tabs/tabs-context-menu.tsx +++ b/services/web/frontend/js/features/source-editor/components/tabs/tabs-context-menu.tsx @@ -123,7 +123,7 @@ export function TabsContextMenu() { // focus the tab setting window.dispatchEvent( new CustomEvent('ui.focus-setting', { - detail: 'previewTabs', + detail: 'editorTabs', }) ) close() diff --git a/services/web/frontend/js/features/source-editor/extensions/index.ts b/services/web/frontend/js/features/source-editor/extensions/index.ts index 65ed2c91ad..da9546cbfb 100644 --- a/services/web/frontend/js/features/source-editor/extensions/index.ts +++ b/services/web/frontend/js/features/source-editor/extensions/index.ts @@ -58,7 +58,6 @@ import { tooltipsReposition } from './tooltips-reposition' import { selectionListener } from '@/features/source-editor/extensions/selection-listener' import { contextMenu } from './context-menu' import { tabsListener } from './tabs-listener' -import { isSplitTestEnabled } from '@/utils/splitTestUtils' const moduleExtensions: Array<(options: Record) => Extension> = importOverleafModules('sourceEditorExtensions').map( @@ -180,5 +179,5 @@ export const createExtensions = (options: Record): Extension[] => [ fileTreeItemDrop(), tooltipsReposition(), selectionListener(options.setEditorSelection), - isSplitTestEnabled('editor-tabs') ? tabsListener() : [], + tabsListener(options.settings.editorTabs), ] diff --git a/services/web/frontend/js/features/source-editor/extensions/tabs-listener.ts b/services/web/frontend/js/features/source-editor/extensions/tabs-listener.ts index 488a7236c0..1c08ae61c9 100644 --- a/services/web/frontend/js/features/source-editor/extensions/tabs-listener.ts +++ b/services/web/frontend/js/features/source-editor/extensions/tabs-listener.ts @@ -1,17 +1,31 @@ -import { Transaction } from '@codemirror/state' +import { isSplitTestEnabled } from '@/utils/splitTestUtils' +import { Compartment, Transaction, TransactionSpec } from '@codemirror/state' import { EditorView } from '@codemirror/view' export const TAB_USER_EDIT_EVENT = 'tab-user-edit' export const tabsEvents = new EventTarget() -export const tabsListener = () => - EditorView.updateListener.of(update => { - if (!update.docChanged) return - for (const transaction of update.transactions) { - if (!transaction.annotation(Transaction.remote)) { - tabsEvents.dispatchEvent(new Event(TAB_USER_EDIT_EVENT)) - return - } +const tabsListenerCompartment = new Compartment() + +const updateListener = EditorView.updateListener.of(update => { + if (!update.docChanged) return + for (const transaction of update.transactions) { + if (!transaction.annotation(Transaction.remote)) { + tabsEvents.dispatchEvent(new Event(TAB_USER_EDIT_EVENT)) + return } - }) + } +}) + +export const tabsListener = (enabled: boolean) => { + return tabsListenerCompartment.of( + enabled && isSplitTestEnabled('editor-tabs') ? updateListener : [] + ) +} + +export const setEditorTabs = (enabled: boolean): TransactionSpec => ({ + effects: tabsListenerCompartment.reconfigure( + enabled && isSplitTestEnabled('editor-tabs') ? updateListener : [] + ), +}) diff --git a/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts b/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts index c6f3ca2441..daf7fd43e2 100644 --- a/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts +++ b/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts @@ -64,6 +64,7 @@ import { useEditorSelectionContext } from '@/shared/context/editor-selection-con import { useActiveEditorTheme } from '@/shared/hooks/use-active-editor-theme' import { useFeatureFlag } from '@/shared/context/split-test-context' import { isCmVisualEditorAvailable } from '../utils/visual-editor' +import { setEditorTabs } from '../extensions/tabs-listener' function useCodeMirrorScope(view: EditorView) { const { fileTreeData } = useFileTreeData() @@ -89,6 +90,7 @@ function useCodeMirrorScope(view: EditorView) { mode, syntaxValidation, mathPreview, + editorTabs, nonBlinkingCursor, referencesSearchMode, } = userSettings @@ -160,6 +162,7 @@ function useCodeMirrorScope(view: EditorView) { mode, syntaxValidation, mathPreview, + editorTabs, nonBlinkingCursor, referencesSearchMode, }) @@ -471,6 +474,13 @@ function useCodeMirrorScope(view: EditorView) { }) }, [view, mathPreview]) + useEffect(() => { + settingsRef.current.editorTabs = editorTabs + window.setTimeout(() => { + view.dispatch(setEditorTabs(editorTabs)) + }) + }, [view, editorTabs]) + useEffect(() => { settingsRef.current.nonBlinkingCursor = nonBlinkingCursor window.setTimeout(() => { diff --git a/services/web/frontend/js/shared/context/user-settings-context.tsx b/services/web/frontend/js/shared/context/user-settings-context.tsx index 4ef15d47c9..979516824a 100644 --- a/services/web/frontend/js/shared/context/user-settings-context.tsx +++ b/services/web/frontend/js/shared/context/user-settings-context.tsx @@ -29,6 +29,7 @@ export const defaultSettings: UserSettings = { fontFamily: 'monaco', lineHeight: 'normal', mathPreview: true, + editorTabs: true, referencesSearchMode: 'advanced', breadcrumbs: true, nonBlinkingCursor: false, diff --git a/services/web/frontend/stylesheets/pages/editor/tabs.scss b/services/web/frontend/stylesheets/pages/editor/tabs.scss index a9b7016722..55e5cea53f 100644 --- a/services/web/frontend/stylesheets/pages/editor/tabs.scss +++ b/services/web/frontend/stylesheets/pages/editor/tabs.scss @@ -2,6 +2,8 @@ --editor-tab-max-path-length: 250px; --editor-tab-divider-color: var(--neutral-70); --editor-tab-row-height: var(--review-panel-header-height, 36px); + --editor-tab-drop-indicator-width: 5px; + --editor-tab-divider-width: 1px; } @include theme('light') { @@ -41,12 +43,7 @@ flex: 1; background: var(--bg-secondary-themed); overflow-x: auto; - - &.editor-tabs-row-hovered { - .editor-file-tab:last-child { - border-right-color: var(--border-primary-themed); - } - } + scrollbar-width: thin; } .editor-file-tab { @@ -54,8 +51,9 @@ --tab-text-color: var(--content-secondary-themed); display: flex; - border-right: 1px solid var(--editor-tab-divider-color); - border-left: 1px solid transparent; + position: relative; + border-right: var(--editor-tab-divider-width) solid + var(--editor-tab-divider-color); padding: var(--spacing-01) var(--spacing-02) 0 var(--spacing-02); &:first-child { @@ -137,12 +135,27 @@ font-size: var(--font-size-01); } - &.tab-drop-right { - border-right-color: var(--border-primary-themed); + &.tab-drop-left::after, + &.tab-drop-right::after, + .editor-tabs-row-hovered &:last-child::after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + width: var(--editor-tab-drop-indicator-width); + background-color: var(--border-primary-themed); } - &.tab-drop-left { - border-left-color: var(--border-primary-themed); + &.tab-drop-right::after, + .editor-tabs-row-hovered &:last-child::after { + right: calc( + var(--editor-tab-drop-indicator-width) / + -2 - var(--editor-tab-divider-width) + ); + } + + &.tab-drop-left::after { + left: calc(var(--editor-tab-drop-indicator-width) / -2); } &.tab-temporary { diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 40ce28eb1f..983bfb26f4 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -1778,7 +1778,9 @@ "open_action_menu": "Open __name__ action menu", "open_advanced_reference_search": "Open advanced reference search", "open_as_template": "Open as Template", + "open_each_file_in_its_own_tab": "Open each file in its own tab", "open_file": "Edit file", + "open_files_in_tabs": "Open files in tabs", "open_link": "Go to page", "open_path": "Open __path__", "open_pdf_in_separate_tab": "Open PDF in separate tab", @@ -2386,6 +2388,7 @@ "show_all_projects": "Show all projects", "show_breadcrumbs": "Show breadcrumbs", "show_document_preamble": "Show document preamble", + "show_editor_tabs": "Show editor tabs", "show_equation_preview": "Show equation preview", "show_file_tree": "Show file tree", "show_in_code": "Show in code", diff --git a/services/web/test/frontend/features/source-editor/components/tabs.spec.tsx b/services/web/test/frontend/features/source-editor/components/tabs.spec.tsx index 70e7cb4ec4..b29d4b6337 100644 --- a/services/web/test/frontend/features/source-editor/components/tabs.spec.tsx +++ b/services/web/test/frontend/features/source-editor/components/tabs.spec.tsx @@ -137,7 +137,7 @@ function makeEditorViewProvider() { const editorView = new EditorView({ state: EditorState.create({ extensions: [ - tabsListener(), + tabsListener(true), EditorView.contentAttributes.of({ 'data-testid': 'mock-editor-view', }), @@ -675,10 +675,10 @@ describe('File Tabs', function () { 'have.been.calledWithMatch', Cypress.sinon.match({ type: 'ui.toggle-left-menu', detail: true }) ) - // ...and focuses the previewTabs setting + // ...and focuses the editorTabs setting cy.get('@dispatchEvent').should( 'have.been.calledWithMatch', - Cypress.sinon.match({ type: 'ui.focus-setting', detail: 'previewTabs' }) + Cypress.sinon.match({ type: 'ui.focus-setting', detail: 'editorTabs' }) ) // and the context menu closes diff --git a/services/web/types/user-settings.ts b/services/web/types/user-settings.ts index 5051fc783c..9021a5d7ec 100644 --- a/services/web/types/user-settings.ts +++ b/services/web/types/user-settings.ts @@ -27,6 +27,7 @@ export type UserSettings = { mathPreview: boolean referencesSearchMode: 'advanced' | 'simple' breadcrumbs: boolean + editorTabs: boolean nonBlinkingCursor: boolean darkModePdf: boolean zotero: RefProviderSettings