From 3b17b0a46affa5702e5fc4f2221d4e8d9867d859 Mon Sep 17 00:00:00 2001 From: Domagoj Kriskovic Date: Fri, 27 Feb 2026 15:02:23 +0100 Subject: [PATCH] feat: implement Overleaf Code experiment with Python execution support GitOrigin-RevId: 54ca98525b2ae056fb34b3713320e868b8c19613 --- .../ide-react/components/layout/editor.tsx | 8 ++- .../js/features/overleaf-code/labs-widget.tsx | 49 +++++++++++++++++++ services/web/frontend/js/utils/labs-utils.ts | 5 +- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 services/web/frontend/js/features/overleaf-code/labs-widget.tsx diff --git a/services/web/frontend/js/features/ide-react/components/layout/editor.tsx b/services/web/frontend/js/features/ide-react/components/layout/editor.tsx index deab008812..e534dc6a06 100644 --- a/services/web/frontend/js/features/ide-react/components/layout/editor.tsx +++ b/services/web/frontend/js/features/ide-react/components/layout/editor.tsx @@ -10,6 +10,7 @@ import { FullSizeLoadingSpinner } from '@/shared/components/loading-spinner' import SymbolPalettePane from '@/features/ide-react/components/editor/symbol-palette-pane' import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-properties-context' import { PythonEditorSplit } from '@/features/ide-react/components/layout/python-editor-split' +import { isInExperiment } from '@/utils/labs-utils' export const Editor = () => { const { opening, errorState, showSymbolPalette } = @@ -19,6 +20,7 @@ export const Editor = () => { const isPythonDocument = openEntity?.type === 'doc' && openEntity.entity.name.toLowerCase().endsWith('.py') + const pythonExecutionEnabled = isInExperiment('overleaf-code') if (!currentDocumentId) { return null @@ -43,7 +45,11 @@ export const Editor = () => { order={1} className="ide-redesign-editor-panel" > - {isPythonDocument ? : } + {isPythonDocument && pythonExecutionEnabled ? ( + + ) : ( + + )} {isLoading && } {showSymbolPalette && ( diff --git a/services/web/frontend/js/features/overleaf-code/labs-widget.tsx b/services/web/frontend/js/features/overleaf-code/labs-widget.tsx new file mode 100644 index 0000000000..f69ac9e198 --- /dev/null +++ b/services/web/frontend/js/features/overleaf-code/labs-widget.tsx @@ -0,0 +1,49 @@ +import LabsExperimentWidget, { + LabsExperimentWidgetProps, +} from '@/shared/components/labs/labs-experiments-widget' +import MaterialIcon from '@/shared/components/material-icon' +import { isInExperiment } from '@/utils/labs-utils' +import { isSplitTestEnabled } from '@/utils/splitTestUtils' +import { useState } from 'react' + +type LabsWidgetProps = Pick & { + labsProgram: boolean +} + +const LabsWidget = ({ setErrorMessage, labsProgram }: LabsWidgetProps) => { + const [optedIn, setOptedIn] = useState(isInExperiment('overleaf-code')) + + if (!isSplitTestEnabled('overleaf-code')) { + return null + } + + const description = ( + + Run Python code while editing .py files + + ) + + return ( + + } + optedInDescription={description} + labsEnabled={labsProgram} + /> + ) +} + +export const hidden = () => !isSplitTestEnabled('overleaf-code') + +export default LabsWidget diff --git a/services/web/frontend/js/utils/labs-utils.ts b/services/web/frontend/js/utils/labs-utils.ts index 2ea9a89e4e..7de15b7a7d 100644 --- a/services/web/frontend/js/utils/labs-utils.ts +++ b/services/web/frontend/js/utils/labs-utils.ts @@ -3,7 +3,10 @@ import getMeta from './meta' // Should be `never` when no experiments are active. Otherwise it should be a // union of active experiment names e.g. `'experiment1' | 'experiment2'` -export type ActiveExperiment = 'monthly-texlive' | 'bibtex-visual-editor' +export type ActiveExperiment = + | 'monthly-texlive' + | 'bibtex-visual-editor' + | 'overleaf-code' export const isInExperiment = (experiment: ActiveExperiment): boolean => { const experiments = getMeta('ol-labsExperiments')