Files
xamxam/TODO.md
T
Pontoporeia beff95c784 fix(css): restore bottom spacing on content pages in Firefox
apropos/licence/charte use .page-content as a nested scroll container
(overflow-y:auto) inside html/body {height:100%; overflow:hidden} flex.
Firefox drops/clips that container's own padding-bottom (and last-child
margin) from the scrollable area -- scrollHeight came out ~80px short and
the last ~80px of content was unreachable. Chromium honours the padding.

Move the trailing space off the container padding onto a real content
spacer (.page-content > article::after, display:block; height) which both
browsers count as scrollable content. Verified via Playwright (Chromium +
Firefox): delta between article and scrollHeight is now 0 in both on all
three pages, consistent at all breakpoints. not-found unaffected (loads
its own not-found.css). 294 PHPUnit tests pass.
2026-09-18 16:26:49 +02:00

89 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TODO
> Last updated: 2026-08-31
> Context: CSS waste strategy — full stream DEFERRED (tasks 10/u/w/x/y/z): unusedSymbols report showed only ~6.2KB/2.9% of 216KB reclaimable; pruning + per-page split parked (docs/css-split-analysis.md). #t (Interdit admin route) confirmed already implemented via /admin/media.php + media-viewer.php + recapitulatif link. No pending live tasks.
## Pending
- [x] #justfile-deploy-code-nginx-split [!medium] de-dup deploy-code: it bundled nginx config + server-side setup (full duplicate of deploy-nginx). Now code-only; deploy depends on deploy-nginx; provision-server's duplicate deploy-nginx removed; var/{cache,logs,tmp} mkdir moved into scripts/deploy-server.sh.
## In Progress
- [x] #cover-home-lazy-intersect-fix [!high] Fix lazy covers not loading after initial images. Root cause: home grid scrolls inside <main class="home-main"> (overflow-y:auto, body height:100vh flex), but htmx `revealed` listens to window scroll only — so below-fold covers never revealed. Fix: hx-trigger="intersect threshold:0.05 once" (IntersectionObserver works inside any scroll container). VERIFIED LIVE: progressive scroll loads covers 5→11→15→20→22/22 (before: stuck ~5). Committed fix(home): use intersect trigger.
- [x] #cover-image-webp-avif-serve-lite [!medium] Serve lightweight webp cover thumbnails instead of full-res PNG originals (~99% on worst cases). DONE VERIFIED LIVE 2026-08-31: `cwebp -resize 800 q80` sidecars (+ GD fallback for CMYK JPEGs) generated by ThesisFileHandler at upload (FilePond + legacy); /cover-fragment emits <picture> (webp source + original fallback); backfill scripts/generate-cover-webp.php + `just deploy-cover-webp`. PROD: tools installed (docs), 46/46 covers backfilled (44 cwebp + 2 GD/CMYK), app code deployed, /cover-fragment + /media?path=*.webp verified 200 image/webp 61KB (vs 15.8MB orig). AVIF dropped (no viable prod resize; WEBP sufficient). NOTE: just deploy-code's final nginx/permissions step needs sudo password (operational, pre-existing — not a regression).
## In Progress
- [x] #fix-toc-scrollbar-overlap-content-pages Fix TOC scrollbar overlapping text on about/charte/licence pages (desktop sticky sidebar). Root cause: `.toc` scroll container (overflow-y:auto, 180px col) had no right gutter, so native scrollbar sat on top of link text. Fix: padding-right (not margin) on the `.toc` scroll container + matching padding on summary.
## Pending
- [x] #home-htmx-lazy-cover-images [!medium] Add htmx lazyloading on home page cover images: `revealed`-triggered cover-fragment endpoint, swap-in `<img>`, settle fade CSS, load htmx on home
## In Progress
- [x] #cleanup-toc-fix-duplicate-id [+admin] Clear TOC links after cleanup restructure. Root cause: admin-toc.js assigned `sec.id = headingId` to sections, and on rebuild-because-of-htmx-swap the section + h2 ended up sharing the same id, so getElementById returned the <section> whose textContent dragged the whole data table into the TOC label. Fix: anchor links to the heading id and never give the section that id (no duplicate ids); verified via Playwright — TOC now lists exactly Tiléversements abandonnés / Corbeille / Vidéos PeerTube with no table text.
## In Progress
- [x] #cleanup-stats-under-headings [+admin] On the cleanup page, move each section's stat/count out of the heading to a line directly under it, and merge PeerTube into a single heading. cleanup-stats-fragment: "Téléversements abandonnés" / "Corbeille" h2 now bare, stat emitted as `<p class=n-meta>` under the h2. peertube-orphans-fragment: removed the separate "Vidéos orphelines" h3; the orphan count now appears as the stat line under the static "Vidéos PeerTube" h2. Verified via Playwright: TOC still lists exactly 3 clean entries; stats render under headings.
## In Progress
- [x] #cleanup-stat-card [+admin] Render the cleanup-page section stats as a proper card box (`cleanup-stat-card`, bigger than the old inline n-meta): padded panel (bg-secondary + border, radius 10px), step-1/700/accent text. Applied to the filepond, trash and PeerTube stat lines. Verified computed style via Playwright.
## In Progress
- [x] #cleanup-desc-and-tooltips [+admin] Cleanup page: add short French descriptions under each section heading (Téléversements abandonnés / Corbeille / Vidéos PeerTube) explaining what the data is and how safe deletion is (`.cleanup-section-desc`), and native title tooltips on the Supprimer and Restaurer buttons of each row. Verified via Playwright (descriptions + tooltips render on all rows).
## In Progress
- [x] #cleanup-auto-gc-uploads [+scripts/deploy] Abandoned FilePond uploads are now garbage-collected automatically (they are risk-free staging dirs never linked to a TFE). Added `scripts/cleanup-tmp-uploads.php` (dry-run by default, mirrors the admin cleanup filepond logic: missing-session via manifest.json + 2h fallback), hourly cron `deploy/xamxam-tmp-cleanup.cron`, `just deploy-tmp-cleanup-cron` + `just deploy-check-tmp-cleanup-log`, wired into `deploy-all-first`, and documented in docs/deployment.md. Tested dry-run + --no-dry-run + active-session protection locally.
## In Progress
- [x] #cleanup-remove-filepond-section [+admin] Since abandoned FilePond uploads are GC'd by cron, removed the "Téléversements abandonnés" section from the cleanup page and the index "Nettoyer" badge now counts trash only. cleanup-stats-fragment.php renders only the Corbeille section (and "Corbeille vide" when empty); index.php badge is trash-only; admin-cleanup-bulk.js simplified to trash_files[] only. Verified via Playwright: only Corbeille + Vidéos PeerTube sections/TOC, no filepond checkboxes, tooltips intact.
## In Progress
- [x] #cleanup-page-title [+admin] Simplify cleanup page title from "Nettoyer les fichiers temporaires" to "Nettoyage" (page now mostly manages the Corbeille). Updated `app/public/admin/cleanup.php` pageTitle and `app/templates/admin/cleanup.php` h1.
## In Progress
- [x] #docs-cleanup-changes [docs] Update docs for the cleanup-page + GC changes made: admin README cleanup.php entry -> "Cleanup page (Corbeille — restore/delete trashed files)"; deployment.md adds a paragraph explaining abandoned uploads are auto-GC'd hourly and the page (titled Nettoyage) now only shows the Corbeille; added `just cleanup-tmp-uploads` recipe and documented it in development.md alongside cleanup-drafts.
## Completed
- [x] #fix-content-page-bottom-spacing-firefox [!medium] Fix missing bottom spacing at scroll-end on apropos/licence/charte in Firefox. Root cause: `.page-content` is a nested scroll container (overflow-y:auto) inside html/body {height:100%; overflow:hidden} flex; Firefox drops/clips that container's own `padding-bottom` (and last-child margin) from the scrollable area while Chromium honours it — FF scrollHeight came out ~80px SHORTER and the last ~80px of content was unreachable (clipped). Fix: moved trailing space off the container padding (`padding-bottom: 0` at all breakpoints) onto a real content spacer `.page-content > article::after { display:block; height: var(--space-2xl) }` (`var(--space-xl)` below 768px), which both browsers count as scrollable content. VERIFIED via Playwright (Chromium + Firefox): before FF delta=−80 (clipped) vs Chrome 0; after both FF & Chrome delta=0 on licence/apropos and identical on charte; not-found page unaffected (loads its own not-found.min.css). 294 PHPUnit tests pass.
- [x] #cleanup-toc [+admin] Add sidebar TOC to admin cleanup page; drop the "Fichiers temporaires" section level and promote "Téléversements abandonnés" / "Corbeille" from h3 to h2 top-level sections (now TOC entries). Made admin-toc.js rebuild on htmx swap/settle so async-loaded fragment sections enter the TOC.
- [x] #audit-all-docs-and [!high] Audit all docs/ and classify accurate vs stale
- [x] #rewrite-development-md-to-match [!high] Rewrite development.md to match current just dev / app/ layout / PHPUnit
- [x] #rewrite-deployment-md-to-match [!high] Rewrite deployment.md to match just deploy / /var/www/xamxam/ / backup
- [x] #update-database-md-to-current [!high] Update database.md to current schema (tags, share_links, migrations, xamxam.db)
- [x] #update-security-md-to-current [!medium] Update security.md to current state (xamxam paths, resolved items)
- [x] #update-search-md-to-current [!medium] Update search.md to current schema (tags not keywords)
- [x] #verify-fix-import-md-csv-format [!medium] Verify/fix import.md CSV format against current import
- [x] #update-bookmarklet-md-form-field [!medium] Update bookmarklet.md form field names (cc2r, duration_pages/minutes, drop contact_public)
- [x] #consolidate-archive-historical-analysis-docs [!medium] Consolidate/archive historical analysis docs
- [x] #fix-nginx-readme-md-title [!low] Fix nginx README.md title + export.md cross-refs
- [x] #fix-app-storage-readme-md-missing-database-spec-quick-schema [!medium] Fix app/storage/README.md (missing DATABASE_SPEC/QUICK_SCHEMA refs, wrong path)
- [x] #update-css-md-asset-structure Update CSS.md asset structure + build bundling
- [x] #correct-file-uploads-md-limits-banner Correct file-uploads.md limits, banner removal, FilePond transition
- [x] #rewrite-app-public-admin-readme-md-to-current Rewrite app/public/admin/README.md to current structure
- [x] #fix-nginx-docs-security-headers-md-csp-x-frame-options Fix nginx/docs/SECURITY_HEADERS.md CSP/X-Frame-Options
- [x] #create-docs-readme-md-index-classifying Create docs/README.md index classifying all docs
- [x] #standardise-all-log-filenames Standardise all log filenames to xamxam-{service}[-{date}].log convention
- [x] #add-server-side-log-rename Add server-side log rename script + just recipe (migrate-log-names) for pre-existing logs
- [x] #move-production-app-logs Move production app logs from storage/logs to /var/log/xamxam + add logrotate config
- [x] #add-date-dropdown-to Add date dropdown to admin log viewer to browse retained daily log files
- [x] #fix-heure-de-d-p-t Fix heure de dépôt (submitted_at) fausse dans backoffice récapitulatif: timezone Europe/Brussels + interpréter datetimes SQLite (UTC) comme UTC
- [x] #add-date-de-d-p-t Add 'Date de dépôt' (date_depot) column: DB migration + view + recap display
- [x] #csv-export-emit-date-depot CSV export: emit date_depot as Brussels-local date
- [x] #csv-import-parse-date-depot CSV import: parse date_depot as Brussels-local and convert to UTC storage
- [x] #default-exemplaire-baiu Default Exemplaire BAIU + ERG to true on student (partage) submission in ThesisCreateController, replicating admin add-form default
- [x] #m-style-links-on Style links on licences, charte, a propos pages (underline + accent hover)
- [x] #recherche-backoffice-par-identifiant Recherche backoffice par identifiant : ajouter t.identifier a la WHERE de getThesesList/getThesesListCount
- [x] #add-admin-only-route-to [!low] Add admin-only route to open Interdit thesis files from backoffice --context repo
- [x] #identify-which-templates-load [!high] Identify which templates load base.min.css and the page types — DONE inventory: docs/css-split-analysis.md. Key finding: head.php loads base.min.css on EVERY full page (App::render => templates/head.php unconditional). is the per-page-type mechanism (public/tfe/repertoire/content-page/not-found/form/admin/system). Centralize inventory in docs/css-split-analysis.md. Gates both split (w) and unusedSymbols corpus (11).
- [x] #collect-content-sources-for [!high] Collect content sources for the unusedSymbols pass — Built scripts/css-content-sources.mjs: buildCorpus() returns {corpus, sources, totalBytes, safelist, prefixes}. Gathers templates/public/src PHP + first-party js/app JS (vendor excluded). Mined 22 exact dynamic classes (status-*, log-*, input-error, active, disabled, btn--*, fhb-*, admin-icon-btn--*, status-published/pending/badge) + 5 suffix prefixes (status-access--*, toc-level-*, admin-import-log__item--*, admin-body, student-body) from status-badge.php, SystemController statusClass/logLineClass, class=<?=...?> ternaries. Documented docs/css-split-analysis.md. Feeds task 12 report.
- [x] #write-report-script-to [!medium] Write report script to compute unused selectors + reclaimable bytes — Built scripts/css-unused-report.mjs + just css-report recipe. Extracts class/id symbols per dist bundle, matches against buildCorpus() corpus + safelists, measures reclaimable bytes via lightningcss transform unusedSymbols (no stripping to disk). FULL RESULT: 216,383B total, ~6.2KB (2.9%) reclaimable. See docs/css-split-analysis.md table. Feeds task 14 go/no-go.
- [x] #flag-or-safelist-dynamic [!medium] Flag or safelist dynamic classes before trusting the report — Vendor + dynamic safelists shipped. Dynamic: 22 exact classes (status-*, log-*, input-error, active, disabled, btn--*, fhb-*, admin-icon-btn--*, status-badge/published/pending) + 5 DB/state suffix prefixes (status-access--*, toc-level-*, admin-import-log__item--*, admin-body, student-body). VENDOR: filepond--* and htmx-* prefixes excluded (runtime vendor JS assembly) — without these the report inflated to 26% (56KB); with them honest 2.9%. Report surfaces needs-review (kept, not flagged removable).
- [x] #summarize-findings-and-decide [!low] Summarize findings and decide whether to proceed to pruning — Decision recorded in docs/css-split-analysis.md. Data: 216,383B total, ~6.2KB (2.9%) reclaimable; base.min.css only 484B. SPLIT: NO-GO (base.css already well-used; split adds complexity for ~2% of one bundle) -> defer u-w-x-y. PRUNING: CONDITIONAL-GO on hand-verifiable dead set only, from SOURCE css files, re-run just css-report after each edit, never touch needs-review/vendor-prefix classes.
## Deferred / Blocked
- [ ] #just-setup-backs-a [!medium] just setup backs a stale setup-dev.sh (clones php-live-reload, legacy admin/data/ dirs) — needs rewrite or removal
- [ ] #csv-export-writes-cc2r-exemplaire [!low] CSV export writes CC2r/Exemplaire columns that import's $cell mapping ignores — document or wire up
- [ ] #split-base-min-css-into-per-page [!medium] Split base.min.css into per-page bundles to reduce cross-page CSS waste — NO-GO (task 14): per-page split parked. base.min.css already well-used (only 484B/2.3% reclaimable); split adds build/template complexity for ~2% of one bundle. See docs/css-split-analysis.md decision.
- [ ] #define-per-page-type-css-bundle [!medium] Define per-page-type CSS bundle boundaries and entry files — Using the template->page-type map, decide which source files (components/*, utilities.css, reset, typography, etc.) belong in each page-type bundle. Document the mapping so shared-vs-page-specific splits are explicit and reviewable.
- [ ] #implement-per-page-bundles-in [!medium] Implement per-page bundles in scripts/build-css.mjs — Extend build-css.mjs with new bundle targets following the existing bundleCss/concatBundle pattern. Emit per-page-type .min.css outputs into app/public/assets/dist/. Keep base.min.css for pages not yet split.
- [ ] #wire-templates-to-load [!medium] Wire templates to load their page-type bundle instead of base.min.css — Update each template's <link> tags to point at the appropriate per-page bundle. Ensure no page loads CSS it doesn't need and that shared pages still get full styling.
- [ ] #measure-before-after-css-bytes [!low] Measure before/after CSS bytes shipped per page type — Moot: part of split stream which is NO-GO (task 14). Skip unless split is revived.
- [ ] #add-lightningcss-unusedsymbols-report-only [!high] Add lightningcss unusedSymbols report-only pass for base.min.css — SEQUENCING: do this (unusedSymbols report-only) BEFORE the per-page split (u). Reasoning: high priority, zero-risk diagnostic-only, produces reclaimable-byte go/no-go (14) against ~216KB baseline; if pruning is not worthwhile the split may not be either. Reuses inventory from v (docs/css-split-analysis.md). Start: 11 collect-content-sources.