From 592b4d3daddb775bb23e2a5ea667c85ca8470d66 Mon Sep 17 00:00:00 2001 From: claude Date: Fri, 12 Jun 2026 17:20:21 +0000 Subject: [PATCH] Fix translations, center logo/footer, add tile zoom control - i18n: unwrap webpack module object on dynamic JSON import (lang.default ?? lang) so French bundle keys are correctly registered in the i18next store - Login logo: use flex centering on wrapper instead of display:block + margin:auto - Footer (project list + login): align-items:center on .row for vertical centering - Tile zoom: S/M/L control in header with CSS custom property (--lum-card-scale) that scales grid column width and card thumbnail height; persisted in localStorage Co-Authored-By: Claude Sonnet 4.6 --- services/web/app/views/user/login.pug | 2 +- .../components/project-list-lumiere.tsx | 66 ++++++++++++++++++- services/web/frontend/js/i18n.ts | 5 +- .../stylesheets/pages/login-register.scss | 15 ++++- .../pages/project-list-lumiere.scss | 47 ++++++++++++- 5 files changed, 127 insertions(+), 8 deletions(-) diff --git a/services/web/app/views/user/login.pug b/services/web/app/views/user/login.pug index a6425d077b..1d4b35651f 100644 --- a/services/web/app/views/user/login.pug +++ b/services/web/app/views/user/login.pug @@ -9,7 +9,7 @@ block content .container .row .col-12 - .text-center.mb-4 + .lumiere-logo-center.mb-4 img.verso-login-logo( src=buildImgPath('ol-brand/verso-logo.svg') alt='Verso' diff --git a/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx b/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx index 39617178c2..e76c6ba989 100644 --- a/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx +++ b/services/web/frontend/js/features/project-list/components/project-list-lumiere.tsx @@ -1,4 +1,4 @@ -import { memo, useCallback, useEffect, useRef } from 'react' +import React, { memo, useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useProjectListContext } from '../context/project-list-context' import { Project } from '../../../../../types/project/dashboard/api' @@ -28,6 +28,46 @@ import { CompileAndDownloadProjectPDFButtonTooltip } from './table/cells/action- import { ArchiveProjectButtonTooltip } from './table/cells/action-buttons/archive-project-button' import { TrashProjectButtonTooltip } from './table/cells/action-buttons/trash-project-button' +// ── Tile zoom ───────────────────────────────────────────────────────────────── + +type ZoomLevel = 0.75 | 1 | 1.35 +const ZOOM_OPTIONS: { value: ZoomLevel; label: string }[] = [ + { value: 0.75, label: 'S' }, + { value: 1, label: 'M' }, + { value: 1.35, label: 'L' }, +] +const ZOOM_STORAGE_KEY = 'lumiere-card-scale' + +function useLumiereCardScale(): [ZoomLevel, (z: ZoomLevel) => void] { + const [scale, setScale] = useState(() => { + try { + const stored = localStorage.getItem(ZOOM_STORAGE_KEY) + if (stored) { + const val = parseFloat(stored) + if ((ZOOM_OPTIONS.map(o => o.value) as number[]).includes(val)) { + return val as ZoomLevel + } + } + } catch (_) { + // storage unavailable + } + return 1 + }) + + const updateScale = useCallback((z: ZoomLevel) => { + setScale(z) + try { + localStorage.setItem(ZOOM_STORAGE_KEY, String(z)) + } catch (_) { + // ignore + } + }, []) + + return [scale, updateScale] +} + +// ── Format helpers ───────────────────────────────────────────────────────────── + type FormatVariant = 'latex' | 'typst' | 'quarto' | 'quarto-slides' function getFormatVariant( @@ -137,6 +177,7 @@ export function ProjectListLumiere() { const navbarProps = getMeta('ol-navbar') const footerProps = getMeta('ol-footer') const { t } = useTranslation() + const [cardScale, setCardScale] = useLumiereCardScale() const { error, visibleProjects, @@ -206,6 +247,23 @@ export function ProjectListLumiere() { filter={filter} selectedTag={selectedTag} /> + + {ZOOM_OPTIONS.map(({ value, label }) => ( + setCardScale(value)} + aria-pressed={cardScale === value} + > + {label} + + ))} + {t('no_projects')}