Lift the month/week/day renderer out of shape-calendar.ts into a new
frontend/src/client/calendar/mount-calendar.ts module so /events
Kalender (next commit) and Custom Views shape=calendar both go
through the same code path.
shape-calendar.ts becomes a thin adapter (ViewRow → CalendarItem +
defaultView=render.calendar.default_view, urlState=true). The
extracted module adds:
- update(items) on the returned handle so /events can re-mount on
filter changes without rebuilding state.
- destroy() for clean teardown when /events switches shapes.
- A 'Heute' button in the toolbar (cal.today, DE+EN added to i18n.ts
+ i18n-keys.ts).
- Optional opts.urlPrefix for surfaces that may share a URL with
another calendar.
mountCalendar reads ?cal_view / ?cal_date when opts.urlState=true.
/events will mount with urlState=true after the next commit so the
Kalender tab + day-view drill remain refresh-stable (per §11 Q3 in
the design doc).
Pure-helper test suite (mount-calendar.test.ts) covers isoDate,
startOfDay, startOfWeek, shift, bucketByDate, filterByDay, isToday —
12 assertions, all green. DOM rendering covered by manual smoke (no
jsdom in this repo's bun test setup; see verfahrensablauf-core.test.
ts comment for the convention).