Add Church History Timeline 2.0 with depth layering and parallax - #153
Add Church History Timeline 2.0 with depth layering and parallax#153hawkandheron-dev wants to merge 2 commits into
Conversation
The Church History and Heresies timelines are two pages over the same CH_
tables, rendered by the same Timeline component and differing only in adapter
and config. Between them they put 258 people, 85 events, 20 movements and 9
era brackets on one screen over a manuscript photograph — too dense to read,
and split so that following Athanasius means changing pages to see the Arian
crisis he was fighting.
CH Timeline 2.0 is a third page that keeps the data, the tour, the overlapping
lifespans, the drilldown and the Cormorant / Alegreya Sans type, and changes
three things:
* White ground. The photograph is replaced by strata of grey rules that
drift at different fractions of the pan.
* No period brackets. Eras survive as colour only, remapped from the old
scheme to the conventional historical periods and derived from dates
rather than read from CH_People.era_id — so no migration.
* Depth. Emperors, heresiarchs, contested figures, movements and every
event move to a watercolour layer behind the main figures, and come into
focus for the person being read: their connections, their movements,
their councils, and the emperors who reigned in their lifetime.
Because the drilldown has to stay readable while that background is in focus,
TimelineModal gains a docked side-panel variant and the detail now renders
outside .timeline-container, so panel and timeline are flex siblings and the
timeline narrows rather than being covered.
Every change to shared code is opt-in behind a new prop or a defaulted
argument, so the two existing pages render exactly as before; both gain a link
across, and 2.0 joins the site nav.
Three defects found by rendering the real dataset rather than a fixture:
* Point stacking sized its collision boxes for the HTML callout each point
renders. A layer drawing bare markers inherited that ~360-year box and
stacked 85 events into dozens of rows — a cascade off the bottom of the
screen. stackPoints takes a markerWidth for label-less layers.
* Scaling the background's pan offset to fake a parallax silently
unregistered the two axes, sliding the background's axis (1-k)*panOffsetY
away from the foreground's. The CSS scale about the shared axis already
carries the foreshortening honestly; the pan multiplier is gone.
* Movements sat below the axis, stranding an empty band between the axis
and the reigns everywhere outside the fourth century. They now sit above
it, behind the figures they were currents among.
Tests: 34 unit (eras, focus sets, point stacking) and 7 e2e covering the white
ground, the remapped legend, the blurred background, the depth control and the
docked panel. The tour was walked scene for scene against both pages and is
identical.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cd1a53d4d4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| { id: 'forward', label: 'Front', title: 'Bring the whole background into focus — or hold Alt' }, | ||
| ]; | ||
|
|
||
| export const Timeline = forwardRef(function Timeline({ data, config, onViewportChange, onItemClick, suppressModal = false, authContext, allPeople, adminContext, contributorContext, onEntityUpdated, onDataChanged, showBackgroundImage = false, layoutSizes, animatingIds, animatingPointIds, hideLegend = false, isTourMode = false, backData, focusIds, depthMode, isFocusPreview = false, detailVariant = 'modal', onPersonHover, onPersonSelect, onDepthModeChange }, ref) { |
There was a problem hiding this comment.
Include the background dataset on mobile
When useMobileDetect() is true, the early return passes only data to MobileTimeline and drops the newly accepted backData prop. Because this adapter deliberately puts every event, movement, emperor, heresiarch, and contested figure in backData, mobile users receive only the foreground lifespans rather than the unified timeline; flatten both layers for the mobile swimlane even if depth effects remain desktop-only.
Useful? React with 👍 / 👎.
| const filteredBackData = useMemo( | ||
| () => (backData ? applyFilters(backData, filters) : null), | ||
| [backData, filters] |
There was a problem hiding this comment.
Include background items in year summaries
Although the background is filtered here, getYearSummary() later reads only filteredData, so every CH 2.0 summary omits all events, councils, documents, movements, and reigning monarchs. This also breaks the bundled year-530 tour scene, whose narrative explicitly opens the summary to show the reigning monarch and nearby events; merge filteredBackData into the summary inputs.
Useful? React with 👍 / 👎.
| transform: `scale(${scale})`, | ||
| transformOrigin: `50% ${axisScreenY}px`, |
There was a problem hiding this comment.
Preserve the background layer's horizontal time alignment
In watercolour and hover modes, CSS scale(...) scales both axes, despite the depth configuration requiring vertical foreshortening only. Consequently, background dates are compressed toward the viewport center—for example, at the configured 0.965 scale an item near a 1400px viewport edge shifts about 25px away from its foreground year—and the focus overlay repeats the same transform; use a Y-only transform so background reigns and events remain time-aligned.
Useful? React with 👍 / 👎.
| if (!suppressModal) { | ||
| setSelectedItem({ type, item }); | ||
| } | ||
| onPersonSelect?.(type === 'person' ? item?.id ?? null : null, type, item); |
There was a problem hiding this comment.
Update depth focus when navigating within the detail panel
This updates depth focus for direct canvas clicks, but connection and reference pills navigate through handleModalItemSelect, which only replaces selectedItem. After opening person A and selecting person B—or an event—from the docked panel, the panel shows the new item while the canvas remains focused on A's background; route modal selections through the same onPersonSelect update, clearing focus for non-person items.
Useful? React with 👍 / 👎.
The deployed page threw on load:
@clerk/clerk-react: useAuth can only be used within the <ClerkProvider />
component.
ChurchHistory2App is a fork of ChurchHistorySupabaseApp, which renders its
Clerk-aware branch — and so calls useAuth — whenever a publishable key is
present. That branch needs a provider above it. main-church-history-supabase.jsx
supplies one; main-church-history-2.jsx was copied from main-church-history.jsx,
which mounts the provider-free ChurchHistoryApp, so it had none.
The entry point now mirrors the Supabase one, gating on the same two globals
the app reads. If those conditions ever diverge the app throws, so a test
asserts they stay in step.
Nothing caught this because every e2e case passed `clerkKey: ''`, leaving
`hasClerk` false and rendering only the unauthenticated branch. The suite
exercised one half of a fork and never the half that ships. Two additions
close that gap:
* tests/unit/clerk-provider-wiring.test.js — a static invariant over every
src/main-*.jsx: an entry point mounting an app that touches Clerk context
must import ClerkProvider. Verified to fail on the broken entry point,
naming it and the app it mounts.
* A case in the 2.0 e2e spec that loads with a key present and asserts the
canvas renders with no missing-provider error. Also verified against the
broken build, where it reproduces the reported error exactly.
Audited all thirteen entry points against the invariant: no other instance.
Deploying profile-site with
|
| Latest commit: |
4ee3fc5
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://dec98530.profile-site-bgf.pages.dev |
| Branch Preview URL: | https://claude-ch-timeline-2-redesig.profile-site-bgf.pages.dev |
Summary
This PR introduces Church History Timeline 2.0, a new unified timeline application that merges the Church History and Heresies timelines into a single view with a sophisticated depth-layering system. The background layer (emperors, heresiarchs, movements, and events) is rendered as a blurred watercolour wash by default and comes into focus when a foreground figure is selected, revealing their contextual background without cluttering the main view.
Key Changes
New CH Timeline 2.0 App (
ChurchHistory2App.jsx): Main application component with authentication, tour support, and depth-focus managementData Adapter (
churchHistory2Adapter.js): Unified Supabase adapter combining Church History and Heresies data sourcesDepth Layering System (
DepthLayers.jsx): Two-canvas rendering for background layerFocus Set Computation (
focusSet.js): Determines which background items relate to a selected foreground figureEra System (
churchHistory2Eras.js): Date-derived era assignment replacing database-stored valuesParallax Field (
ParallaxField.jsx): Depth-cue background using layered grey rulesConfiguration (
churchHistory2Data.js): Presentation config for the new timelineTimeline Component Updates: Extended
Timeline.jsxandTimelineCanvas.jsxto support:Detail Panel Variant (
TimelineModal.jsx): New 'panel' layout optionLegend Updates (
TimelineLegend.jsx): Support for two independent filter setsTesting: Comprehensive test coverage
church-history-2.spec.js) with minimal fixture datachurch-history-2-focus.test.js)church-history-2-eras.test.js)https://claude.ai/code/session_01U7bEJazMWsAznBk65t9ei1