web(review): YYYY/MM path fallback + suggestion row below date + tighten 'a' gate

- suggestDateFromPath: when only year+month appear in the path
  (e.g. 2024/01/), synthesize day=01 so date-only foldering yields
  a usable suggestion instead of null.
- RightSidebar: move the suggestion row below the Taken-at input.
- BulkActionBar + gridKeyNav: show the "Accept date & Keep" button
  and fire the bare 'a' shortcut only when EVERY targeted photo has
  a path-derivable date — no more silent approve-without-fix for
  mixed selections.
- gridKeyNav: drop local cachedPhoto duplicate, use the shared one.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 11:41:40 +02:00
parent 97f51a05c4
commit a54d90a2d9
4 changed files with 65 additions and 82 deletions

View File

@@ -1,8 +1,12 @@
/**
* Best-effort calendar date guessed from a photo's filename / parent
* folders. Returns `YYYY-MM-DD` only when year + month + day are all
* present and form a real date; returns `null` for year-only or
* unparseable inputs.
* folders. Returns `YYYY-MM-DD`:
* - high-confidence when year + month + day are all parseable
* (basename or path)
* - month-anchored when only year + month appear in the path (day
* synthesised to `01` so the value drops cleanly into a date
* input; the user reviews + adjusts before committing)
* Returns `null` for year-only or unparseable inputs.
*
* Used by the EXIF Stripped review tab to pre-fill the metadata
* sidebar's date suggestion row.
@@ -32,8 +36,12 @@ function tryDate(y: number, m: number, d: number): string | null {
// `20070612` cleanly without bleeding into the trailing timestamp.
const BASENAME_YMD = /(?<!\d)(\d{4})[-_]?(\d{2})[-_]?(\d{2})(?!\d)/;
// Path variant accepts `/` as a separator too — `2007/06/12`, `2007-06-12/`.
// Path Y-M-D first (e.g. `2007/06/12`, `2007-06-12/`). Y-M fallback
// (`2024/01/`, `Photos/2024-01/inner`) trips when the user only foldered
// by month — the day defaults to `01` so the field has a sensible
// pre-filled value rather than nothing.
const PATH_YMD = /(?<!\d)(\d{4})[-_/](\d{2})[-_/](\d{2})(?!\d)/;
const PATH_YM = /(?<!\d)(\d{4})[-_/](\d{2})(?!\d)/;
export function suggestDateFromPath(input: Input): string | null {
const fileName = (input.fileName ?? '').trim();
@@ -46,9 +54,14 @@ export function suggestDateFromPath(input: Input): string | null {
}
if (path) {
const pMatch = path.match(PATH_YMD);
if (pMatch) {
const iso = tryDate(Number(pMatch[1]), Number(pMatch[2]), Number(pMatch[3]));
const ymd = path.match(PATH_YMD);
if (ymd) {
const iso = tryDate(Number(ymd[1]), Number(ymd[2]), Number(ymd[3]));
if (iso) return iso;
}
const ym = path.match(PATH_YM);
if (ym) {
const iso = tryDate(Number(ym[1]), Number(ym[2]), 1);
if (iso) return iso;
}
}