diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0202ded..2e3222a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
## Upcoming release
+- Demo: period labels now group the per-day-of-week entries of a hint and
+ condense consecutive weekdays into a range, e.g. « Vacances scolaires
+ (lun-sam) : du lundi au samedi + samedis » instead of one « … — lundis »
+ entry per weekday. Applies to the period cards and the calendar legend.
+
- Demo: new « Sous le capot » section under the results with two collapsible,
syntax-highlighted (Prism) snippets: the exact `findCalendarPeriods` call of
the current analysis — real hints and options included — as a runnable
diff --git a/website/src/components/ResultsView.tsx b/website/src/components/ResultsView.tsx
index 29b82a3..edab6ae 100644
--- a/website/src/components/ResultsView.tsx
+++ b/website/src/components/ResultsView.tsx
@@ -1,6 +1,6 @@
import { useMemo } from 'react'
import type { CalendarHintsResult, HintResult, Period } from '../../../src/calendar-hints'
-import { buildDayTypes, dayTypeBackground, frLabel, type DayTypeStyle } from '../day-types'
+import { buildDayTypes, dayTypeBackground, frLabel, periodLabel, type DayTypeStyle } from '../day-types'
import { eachDay, type GeneratedHints } from '../hints'
import CalendarGrid from './CalendarGrid'
@@ -50,7 +50,7 @@ function PeriodCard({ period, style }: { period: Period; style: DayTypeStyle })
- {period.labels.map(frLabel).join(' + ')}
+ {periodLabel(period.labels)}
{period.days.length} jours ({period.days[0]} → {period.days[period.days.length - 1]}) —{' '}
diff --git a/website/src/day-types.ts b/website/src/day-types.ts
index 0bac026..93aecaf 100644
--- a/website/src/day-types.ts
+++ b/website/src/day-types.ts
@@ -23,6 +23,61 @@ export function frLabel(label: string): string {
return LABEL_FR.reduce((s, [en, fr]) => s.replaceAll(en, fr), label)
}
+// Libellé d'une période : les labels « Hint — Mondays + Hint — Tuesdays + … »
+// issus de la politique per-day-of-week sont regroupés par hint, et les jours
+// consécutifs condensés en « du lundi au samedi ». Semaine à la française,
+// lundi en tête.
+const WEEKDAYS_EN = ['Mondays', 'Tuesdays', 'Wednesdays', 'Thursdays', 'Fridays', 'Saturdays', 'Sundays']
+const WEEKDAYS_FR = ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche']
+
+function frWeekdays(indices: number[]): string {
+ const sorted = [...new Set(indices)].sort((a, b) => a - b)
+ const runs: number[][] = []
+ for (const i of sorted) {
+ const run = runs[runs.length - 1]
+ if (run && i === run[run.length - 1] + 1) run.push(i)
+ else runs.push([i])
+ }
+ return runs
+ .map(run => {
+ if (run.length === 7) return 'tous les jours'
+ if (run.length >= 3) return `du ${WEEKDAYS_FR[run[0]]} au ${WEEKDAYS_FR[run[run.length - 1]]}`
+ return run.map(i => `${WEEKDAYS_FR[i]}s`).join(' + ')
+ })
+ .join(' + ')
+}
+
+export function periodLabel(labels: string[]): string {
+ // Groupes dans l'ordre de première apparition ; ceux issus d'un même hint
+ // per-day-of-week (même préfixe avant « — jour ») fusionnent leurs jours.
+ const groups: { prefix: string | null; weekdays: number[] }[] = []
+ const byPrefix = new Map()
+ for (const label of labels) {
+ const sep = label.lastIndexOf(' — ')
+ const weekday = sep >= 0 ? WEEKDAYS_EN.indexOf(label.slice(sep + 3)) : -1
+ if (weekday < 0) {
+ groups.push({ prefix: frLabel(label), weekdays: [] })
+ continue
+ }
+ // « Remaining days — lundis » s'affiche sans préfixe, comme frLabel
+ const prefix = label.slice(0, sep) === 'Remaining days' ? null : frLabel(label.slice(0, sep))
+ let group = byPrefix.get(prefix ?? '')
+ if (!group) {
+ group = { prefix, weekdays: [] }
+ byPrefix.set(prefix ?? '', group)
+ groups.push(group)
+ }
+ group.weekdays.push(weekday)
+ }
+ return groups
+ .map(g => {
+ if (g.weekdays.length === 0) return g.prefix
+ const days = frWeekdays(g.weekdays)
+ return g.prefix === null ? days : `${g.prefix} : ${days}`
+ })
+ .join(' + ')
+}
+
// Palette stable ; quand elle boucle, le motif change — 12 × 4 = 48 styles
// distincts avant répétition, largement au-delà du nombre de signatures d'un
// feed réel.
@@ -110,7 +165,7 @@ export function buildDayTypes(result: CalendarHintsResult, orderedPeriods: Perio
return {
signature,
style: styleOf(i),
- label: period ? period.labels.map(frLabel).join(' + ') : 'non classé',
+ label: period ? periodLabel(period.labels) : 'non classé',
dayCount: dayCounts.get(signature) ?? 0,
unclassifiedCount: group?.days.length ?? 0,
tripCount: period?.tripCount ?? group?.tripCount ?? 0,