[web] Add AI workbench to alpha users (#29417)

Co-authored-by: Alf Eaton <alf.eaton@overleaf.com>
GitOrigin-RevId: 79bb329932b1e6fcc88f648bca9cc4bee215cd41
This commit is contained in:
Mathias Jakobsen
2025-11-11 09:06:08 +00:00
committed by Copybot
co-authored by Alf Eaton
parent c84cfc815a
commit 8024fe2c58
49 changed files with 8446 additions and 127 deletions
@@ -124,6 +124,7 @@
"ai_feedback_the_suggestion_didnt_fix_the_error": "",
"ai_feedback_the_suggestion_wasnt_the_best_fix_available": "",
"ai_feedback_there_was_no_code_fix_suggested": "",
"ai_workbench": "",
"alignment": "",
"all_borders": "",
"all_events": "",
@@ -49,6 +49,7 @@ export default /** @type {const} */ ([
'shuffle',
'smart_toy',
'space_dashboard',
'star',
'strikethrough_s',
'table_chart',
'table_chart',
@@ -52,7 +52,9 @@ export default function RailPanel({
>
<Tab.Content className="ide-rail-tab-content">
{railTabs
.filter(({ hide }) => !hide)
.filter(({ hide }) => {
return typeof hide === 'function' ? !hide() : !hide
})
.map(({ key, component, mountOnFirstLoad }) => (
<Tab.Pane
eventKey={key}
@@ -26,6 +26,14 @@ import RailResizeHandle from './rail-resize-handle'
import RailModals from './rail-modals'
import RailOverflowDropdown from './rail-overflow-dropdown'
import useRailOverflow from '../../hooks/use-rail-overflow'
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
const moduleRailEntries = (
importOverleafModules('railEntries') as {
import: { default: RailElement }
path: string
}[]
).map(({ import: { default: element } }) => element)
export const RailLayout = () => {
const { sendEvent } = useEditorAnalytics()
@@ -79,6 +87,7 @@ export const RailLayout = () => {
title: t('chat'),
hide: !getMeta('ol-capabilities')?.includes('chat'),
},
...moduleRailEntries,
],
[t, features.trackChangesVisible, view]
)
@@ -125,7 +134,13 @@ export const RailLayout = () => {
} else {
// HACK: Apparently the onSelect event is triggered with href attributes
// from DropdownItems
if (!railTabs.some(tab => !tab.hide && tab.key === key)) {
if (
!railTabs.some(tab =>
typeof tab.hide === 'function'
? !tab.hide()
: !tab.hide && tab.key === key
)
) {
// Attempting to open a non-existent tab
return
}
@@ -150,7 +165,9 @@ export const RailLayout = () => {
)
useEffect(() => {
const validTabKeys = railTabs.filter(tab => !tab.hide).map(tab => tab.key)
const validTabKeys = railTabs
.filter(tab => (typeof tab.hide === 'function' ? !tab.hide() : !tab.hide))
.map(tab => tab.key)
if (!validTabKeys.includes(selectedTab) && isOpen) {
// If the selected tab is no longer valid (e.g. due to permissions changes),
// switch back to the file tree
@@ -199,7 +216,9 @@ export const RailLayout = () => {
<Nav activeKey={selectedTab} className="ide-rail-tabs-nav">
<div className="ide-rail-tabs-wrapper" ref={tabWrapperRef}>
{tabsInRail
.filter(({ hide }) => !hide)
.filter(({ hide }) =>
typeof hide === 'function' ? !hide() : !hide
)
.map(({ icon, key, indicator, title, disabled }) => (
<RailTab
open={isOpen && selectedTab === key}
@@ -23,6 +23,7 @@ export type RailTabKey =
| 'review-panel'
| 'chat'
| 'full-project-search'
| 'workbench'
export type RailModalKey = 'keyboard-shortcuts' | 'contact-us' | 'dictionary'
@@ -8,7 +8,7 @@ export type RailElement = {
component: ReactElement | null
indicator?: ReactElement
title: string
hide?: boolean
hide?: boolean | (() => boolean)
disabled?: boolean
mountOnFirstLoad?: boolean
}