obot.roadmap · requirement #75 · design mockup · 2026-07-24

Where the participant profile lives

v1 docked the profile below the chart. This page puts it in a right-hand rail, adds the two adverse-event tracks, and lets you switch between the surfacing options while the real chart and the real profile module are running — so the choice can be made by using it rather than by reading about it.

Requirement obot.roadmap#75 Idea discussion #49 v1 #45 · options report Design doc 75_design.html

9 decisions open for @jwildfire

Live. The chart and the profile block are the real safety.viz modules. Mocked. The rail, the expand state, and both AE tracks. Try it. Click any point in the chart, then switch surfacing options.

Surfacing
When it opens

What you are looking at

The mockup runs the committed safety.viz-1.4.1 dev bundle — the same asset the demo site and gsm.safety vendor — against the CDISC Pilot ADaM demo data. That keeps the findings honest: the narrow-rail problems below are real measurements of the shipped module, not predictions.

Real, from the shipped module

The Hepatic Safety Explorer, its control sidebar, its selection event, and the whole participant-profile block: header, labs-over-time spaghetti, measure table with sparklines and insets, and the worst-first cohort stepper. Switching to “right, stacked” or “right, tabbed” moves the chart’s actual sidebar element into the rail — the layouts are demonstrated, not drawn.

Mocked, standing in for v2

The rail itself, the push/overlay behaviour, the expand state, the standardization control in the rail head, and the two adverse-event tracks. The AE tracks read the real adae.csv for the selected participant; they are rendered by this page, not by the library. The cohort button dispatches a selection event directly so the stepper can be seen without navigating to the composite view.

The controls sidebar is already on the left

The idea asked for the profile to behave like “the right-hand controls sidebar.” In the shipped shell the control sidebar renders on the leftrenderShell appends aside.sv-sidebar before the main column in a flex row. So there is no slot to fight over: the profile can take the right rail and the controls can stay where they are.

What that changes

The “same slot / stacked / swap” question becomes a real choice rather than a forced one. Opposite rails costs nothing to build and reads cleanly — inputs on the left, the participant being read on the right — but it does put the chart between two panels, which on a 1280px laptop leaves the eDISH scatter about 560px wide with both open.

Mitigation in the mockup: “Collapse the chart’s controls” collapses the control sidebar to its rail the first time a profile opens, which returns most of that width. Uncheck it in the toolbar to feel the difference.

What the rail costs the profile

Measured on this page: the measure table will not lay out below 455px of content width, so a rail narrower than about 500px clips its sparkline column — and at 320px it loses MAX as well. The demographics header wraps to two rows somewhere below 400px. This is the single largest piece of v2 work that is not new functionality: a narrow layout for contents designed for a full-width dock. Drag the width slider to watch it break.

The AE tracks, on the lab chart’s clock

The reason to grow the profile past labs is the question a reviewer actually asks: did anything happen to this person around the time their transaminases moved? That question is only answerable if the two tracks share an axis — so in this mockup the AE timeline is pixel-locked to the spaghetti chart’s x-scale, reading its live chartArea and tick positions rather than approximating them.

AE summary — not a chart

One participant has five to twenty events. A bar chart of counts by body system at that scale is decoration. The summary is four numbers (events, highest severity, serious, ongoing), a severity mix bar, and the top body systems as a list. Severity uses the reserved status ramp — mild, moderate, severe — and is always labelled, never colour alone.

AE timeline — one row per event

Rows are ordered worst-first, bars run start day to end day on the shared axis, events still ongoing at the last visit run to the edge with an open cap, and serious events carry a ring plus their label. Ten rows fit the rail; the rest are counted, not hidden silently.

Data implication: the demo lab file carries visits, not study days, so this page derives a nominal day per visit. The real requirement is a study-day column in the lab domain — without it the shared axis is an approximation.

The four surfacing options, side by side

Every row is live in the toolbar above — the recommendation is a starting point, not a decision.

OptionHow it behavesArgues forArgues against
Dock
v1, shipped
Profile renders full width below the chart; the page scrolls to it. Widest layout for the measure table and insets. Already built and evidenced. Prints and embeds without special cases. The chart leaves the viewport as you read; the click and its answer are never on screen together.
Opposite rails
recommended
Controls keep the left rail, profile takes a right rail, chart between them. No collision to resolve. Chart and profile visible together. Matches the mental model of inputs on one side, the thing being read on the other. Two panels squeeze the chart; needs the auto-collapse to stay comfortable below ~1400px.
Right, stacked Chart’s control sidebar moves to the right rail, above the profile, in one scrolling column. Chart gets the full width. Everything a reviewer touches lives on one side. The rail becomes long: controls must be scrolled past to reach the participant, or collapsed by hand.
Right, tabbed One right rail with Controls and Participant tabs; opening a profile switches the tab. Full-width chart with no scrolling penalty. Compact on narrow screens. Adjusting a filter while reading a participant means losing sight of one of them — the two things reviewers most often do together.

Nine open decisions

Each carries a recommendation (★) and stays open until @jwildfire picks. The design doc holds the same nine with the implementation consequences spelled out.

Open D1

Default surfacing mode

  • Right rail, push — the chart re-lays out to make room.
  • Right rail, overlay — the rail floats above the chart’s right edge.
  • Dock — keep v1 behaviour as the default.

Why push: in eDISH the high-ALT, high-bilirubin corner sits top-right, which is exactly what an overlay covers — and it is the region a reviewer clicks from. Overlay wins back width the auto-collapse can win back honestly.

Open D2

Where the chart’s controls go

  • Opposite rails — controls stay left, profile takes the right.
  • Both right, stacked — one rail, controls above the profile.
  • Both right, tabbed — one rail, two tabs.

Why opposite rails: the controls are already on the left, so this is the only option with no migration cost for the seven adopting renderers, and it is the only one that keeps filters and the participant readable at the same time.

Open D3

What “full screen” means

  • Fill the renderer’s own container, chart dimmed behind it.
  • Fixed overlay over the whole browser viewport.
  • Native Fullscreen API on the profile element.

Why the container: the same module has to work inside a gsm.safety htmlwidget and an open.gismo panel, where escaping the container is either impossible or rude. Filling the container behaves identically in all three, and the expanded state can then show the module’s own control sidebar — which is simply the standalone renderer, already built.

Open D4

What becomes of the dock

  • Keep it as the narrow-viewport fallback and an explicit opt-in.
  • Keep it as an opt-in only; never automatic.
  • Remove it once the rail ships.

Why keep: the shell already collapses to a single column below 900px, where a 460px rail cannot exist. Falling back to the dock there costs nothing — it is the same module in the same slot — and it protects the evidence already recorded for the docked mounts.

Open D5

Where the AE tracks sit in the profile

  • Directly under the labs chart, before the measure table.
  • After the measure table, at the end of the block.
  • A tab per domain — Labs / AEs / Records.

Why adjacent: the shared axis only pays off if the two tracks touch. Tabs would undo the entire point of one profile — a participant’s story read in one pass.

Open D6

What the AE summary is

  • Stat tiles + severity mix + body-system list — no chart.
  • A bar chart by body system.
  • A table of terms only.

Why no chart: at one participant’s scale the counts are single digits. Four numbers and a mix bar answer “how bad, how many, anything serious” faster than any plot of the same data.

Open D7

The shared study-day axis

  • Union domain — labs and AEs rescale together to cover both.
  • Lab domain wins — AEs clip at the edge with a cap.
  • Independent axes per track.

Why union: AEs routinely run past the last lab draw; clipping hides exactly the late events a reviewer is looking for. The mockup currently clips (watch the caps) so the cost of the alternative is visible. Requires a study-day column in the lab domain — a new data requirement for v2.

Open D8

The cohort stepper in a rail

  • Sticky stepper strip at the rail top, expandable to the ranked list.
  • Stepper only, as shipped in v1.
  • List first — the rail shows the cohort, one click opens a participant.

Why sticky: in a scrolling rail the stepper leaves the viewport as soon as you read the measure table, so “next participant” becomes a scroll-up. Pinning it costs one CSS rule; the expandable list answers “which twelve am I stepping through?” without leaving the rail. Press Select an 8-participant cohort above to see today’s behaviour.

Open D9

Rollout scope

  • Lab family flips to the rail; ae-explorer and ae-timelines adopt the profile in the same pass.
  • Lab family only; AE renderers wait for a later requirement.
  • hep-explorer first, others follow per renderer.

Why together: #45 deferred ae-explorer and ae-timelines because the profile had no AE domain. Building the AE adapter removes that reason, and the two renderers already ingest exactly the records the adapter needs.

How it would be built

Ten safety.viz increments plus the requirement-matrix companion, each shippable on its own so an autonomous session can take them one at a time. The same list is drafted into the Tasks section of #75.

  1. Rail mode. profile: { mode: 'sidebar' } plus a shell rail slot: push behaviour, configurable width, open/close, focus handling. No AE work, no rollout — hep-explorer only. PPRF-RAIL-*
  2. Narrow layout pass for the v1 contents at 320–520px: header wrap, measure-table column priority, sparkline and inset behaviour.
  3. Expand state — fill the container, reveal the module’s own control sidebar, Escape to collapse. PPRF-EXP-*
  4. Responsive fallback — rail becomes the dock below the shell’s 900px breakpoint.
  5. AE domain adapter — a second data contract on the module (terms, severity, seriousness, start/end day), cleaned once per setData, shared with ae-explorer and ae-timelines. PPRF-AE-*
  6. AE summary track. PPRF-AESUM-*
  7. AE timeline track including the shared-axis contract and the union domain. PPRF-AXIS-*
  8. Sticky cohort stepper in rail context, with the ranked list. Updates PPRF-STEP-*.
  9. Rollout — the lab family defaults to the rail; ae-explorer and ae-timelines adopt the profile.
  10. Done gate — gallery demo, evidence page, API reference, coverage doc.
  11. obot.agent companion — v2 rows on the participant-profile matrix, and a supersession note on the v1 rows the rail replaces.