fix: XS badge column, lumiere on welcome and 404 pages
Build and Deploy Verso / deploy (push) Successful in 14m39s

- XS compact row: format column 70px→96px so "QUARTO SLIDES" stays on one line;
  trim owner/date cols slightly to compensate
- Welcome page (0 projects): Lumière branch now renders before the 0-projects
  check; ProjectListLumiere renders WelcomePageContent when totalProjectsCount=0
  so new users get the full onboarding experience in the Lumière shell
- 404 page: notFound() now detects the user's overallTheme and passes isLumiere
  to the template; layout-base.pug sets data-lumiere on the body; error-pages.scss
  and project-list-lumiere.scss add [data-lumiere='true'] rules for the body
  background gradient, navbar white+stripe, and styled error box

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
claude
2026-06-13 16:33:23 +00:00
parent c5883e5954
commit b8d5cb9816
6 changed files with 95 additions and 12 deletions
@@ -5,14 +5,26 @@ import {
} from '@overleaf/validation-tools'
import Errors, { NotFoundError } from './Errors.js'
import SessionManager from '../Authentication/SessionManager.mjs'
import UserGetter from '../User/UserGetter.mjs'
import SamlLogHandler from '../SamlLog/SamlLogHandler.mjs'
import HttpErrorHandler from './HttpErrorHandler.mjs'
import { plainTextResponse } from '../../infrastructure/Response.mjs'
import { expressifyErrorHandler } from '@overleaf/promise-utils'
function notFound(req, res) {
async function notFound(req, res) {
res.status(404)
res.render('general/404', { title: 'page_not_found' })
const sessionUser = SessionManager.getSessionUser(req.session)
let isLumiere = false
if (sessionUser?._id) {
try {
const user = await UserGetter.promises.getUser(sessionUser._id, {
'ace.overallTheme': 1,
signUpDate: 1,
})
isLumiere = (user?.ace?.overallTheme ?? '') === 'lumiere-'
} catch {}
}
res.render('general/404', { title: 'page_not_found', isLumiere })
}
function forbidden(req, res) {
+1
View File
@@ -106,6 +106,7 @@ html(
'red-nav-bar-for-admins': !settings.isDevEnv && hasFeature('saas') && hasAdminAccess(),
}
data-theme='light'
data-lumiere=isLumiere ? 'true' : 'false'
)
if settings.recaptcha && settings.recaptcha.siteKeyV3
script(
@@ -32,6 +32,7 @@ import OwnerCell from './table/cells/owner-cell'
import LastUpdatedCell from './table/cells/last-updated-cell'
import ActionsCell from './table/cells/actions-cell'
import InlineTags from './table/cells/inline-tags'
import WelcomePageContent from './welcome-page-content'
// ── Tile zoom ─────────────────────────────────────────────────────────────────
@@ -227,6 +228,7 @@ export function ProjectListLumiere() {
const {
error,
visibleProjects,
totalProjectsCount,
searchText,
setSearchText,
filter,
@@ -344,7 +346,9 @@ export function ProjectListLumiere() {
</button>
</div>
)}
{visibleProjects.length === 0 ? (
{totalProjectsCount === 0 ? (
<WelcomePageContent />
) : visibleProjects.length === 0 ? (
<p className="lumiere-empty">{t('no_projects')}</p>
) : (
<div
@@ -100,6 +100,14 @@ function ProjectListPageContent() {
return loadingComponent
}
if (overallTheme === 'lumiere-') {
return (
<DsNavStyleProvider>
<ProjectListLumiere />
</DsNavStyleProvider>
)
}
if (totalProjectsCount === 0) {
return (
<>
@@ -111,14 +119,6 @@ function ProjectListPageContent() {
)
}
if (overallTheme === 'lumiere-') {
return (
<DsNavStyleProvider>
<ProjectListLumiere />
</DsNavStyleProvider>
)
}
return (
<DsNavStyleProvider>
<ProjectListDsNav />
@@ -1,3 +1,39 @@
// Lumière-themed overrides for 404 / error pages.
// $lum-* vars are declared in project-list-lumiere.scss which is imported
// earlier in all.scss, so they are available here.
[data-lumiere='true'] {
.error-container {
min-height: 60vh;
justify-content: center;
}
.error-details {
background: rgba(255, 255, 255, 0.88);
border-radius: 16px;
padding: 3rem;
box-shadow: 0 4px 24px rgba(42, 157, 143, 0.12);
}
.error-status {
color: #2a9d8f;
}
.error-description {
color: #64748b;
}
.error-btn {
background: #2a9d8f !important;
border-color: #2a9d8f !important;
border-radius: 8px;
&:hover {
background: #21867a !important;
border-color: #21867a !important;
}
}
}
.error-container {
display: flex;
align-items: center;
@@ -746,7 +746,7 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi
.lumiere-compact-row {
display: grid;
// checkbox | name+tags | format | owner | date | actions
grid-template-columns: 28px 1fr 70px 130px 160px auto;
grid-template-columns: 28px 1fr 96px 120px 150px auto;
align-items: center;
column-gap: 0.85rem;
padding: 0.45rem 0.75rem;
@@ -987,3 +987,33 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi
--link-hover-color: #{$lum-teal};
--link-visited-color: #64748b;
}
// ── Global Lumière rules (apply to every page when data-lumiere is set) ────
// These cover pages that don't carry .project-list-lumiere (404, welcome).
[data-lumiere='true'] {
background: linear-gradient(160deg, #f0faf8 0%, #e4f4f1 50%, #daeef5 100%);
min-height: 100vh;
// Shared white navbar with teal accent stripe (marketing pages + welcome)
.navbar-default {
background-color: #ffffff !important;
border-bottom: none !important;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06), 0 2px 8px rgba(0, 0, 0, 0.04);
position: relative;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, $lum-teal 0%, $lum-blue 100%);
}
.navbar-brand { color: $lum-text !important; }
.navbar-nav > li > a { color: $lum-text-sub !important; }
.navbar-nav > li > a:hover { color: $lum-teal !important; }
}
}