Merge pull request #32968 from overleaf/worktree-labs-feature-preview

Add labs preview modal to editor

GitOrigin-RevId: 0df33135febc8e94129bcdfdfb5c4981326dfab0
This commit is contained in:
Malik Glossop
2026-05-25 08:05:28 +00:00
committed by Copybot
parent 24ba0b86b1
commit eb9d586bdb
20 changed files with 761 additions and 69 deletions
@@ -902,6 +902,10 @@ const _ProjectController = {
userSettings?.overallTheme
)
if (user.labsProgram) {
await Modules.promises.hooks.fire('assignLabsSplitTests', req, res)
}
res.render(template, {
title: project.name,
priority_title: true,
@@ -943,7 +947,6 @@ const _ProjectController = {
},
initialLoadingScreenTheme,
userSettings,
labsExperiments: user.labsExperiments ?? [],
privilegeLevel,
anonymous,
isTokenMember,
@@ -828,6 +828,22 @@ async function _loadSplitTestInfoInLocals(locals, splitTestName, session) {
phase,
badgeInfo: splitTest.badgeInfo?.[phase],
}
if (phase === 'labs') {
const variant = currentVersion.variants?.[0]
info.labsDetails = {
title: splitTest.labsTitle || '',
description: splitTest.labsDescription || '',
icon: splitTest.labsIcon || '',
surveyLink: splitTest.badgeInfo?.labs?.url || '',
isFull: SplitTestUtils.isExperimentFull(variant),
versionCreatedAt:
currentVersion.createdAt instanceof Date
? currentVersion.createdAt.toISOString()
: currentVersion.createdAt,
}
}
if (Settings.devToolbar.enabled) {
info.active = currentVersion.active
info.variants = currentVersion.variants.map(variant => ({
@@ -329,6 +329,17 @@ async function switchToNextPhase(
)
}
// Labs phase requires labs config to be set
if (lastVersionCopy.phase === LABS_PHASE) {
if (!splitTest.labsTitle || !splitTest.labsDescription) {
// Cannot be a required fields as we do not always promote to labs
throw new Errors.InvalidError(
'Labs phase requires Labs Title and Labs Description to be set',
{ name }
)
}
}
// Labs phase requires exactly one variant
if (lastVersionCopy.phase === LABS_PHASE) {
if (lastVersionCopy.variants.length !== 1) {
@@ -14,7 +14,17 @@ function getVersion(splitTest, versionNumber) {
})
}
function isExperimentFull(variant) {
const { userLimit, userCount } = variant
if (typeof userLimit === 'number') {
const currentCount = userCount ?? 0
return currentCount >= userLimit
}
return false
}
export default {
getCurrentVersion,
getVersion,
isExperimentFull,
}
@@ -3,7 +3,6 @@ meta(name="ol-projectName" content=projectName)
meta(name="ol-canUseClsiCache" data-type="boolean" content=canUseClsiCache)
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)
meta(name="ol-learnedWords" data-type="json" content=learnedWords)
meta(name="ol-anonymous" data-type="boolean" content=anonymous)
meta(name="ol-brandVariation" data-type="json" content=brandVariation)