Skip to content

feat: Full Timeline overview below the Timeline graph (CLUE-719) - #3029

Merged
kswenson merged 8 commits into
masterfrom
CLUE-719-full-timeline
Oct 8, 2026
Merged

kswenson merged 8 commits into
masterfrom
CLUE-719-full-timeline

Conversation

@kswenson

@kswenson kswenson commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

CLUE-719

The Timeline tile now has a Full Timeline below the graph: the whole record at a glance, with a marker for the part the graph is showing. On a record spanning weeks or months, students can see where they are and get anywhere in one gesture, instead of hunting with the scrollbar alone.

  • The Full Timeline shows the whole record as a black waveform on white, with every event drawn in a darker, accessible color set from the Zeplin spec.
  • A teal overlay marks the range shown in the graph. Drag the overlay to move the view; the point you grabbed stays under the pointer. Click anywhere else on the Full Timeline to center the view there.
  • The scrollbar below works the same way: drag the thumb, or click the track to center the view there. The thumb and overlay are always the same width.
  • Each drag of the overlay or thumb is saved, and undone, as a single change, even when drags on the Full Timeline, the scrollbar and the graph overlap. A drag that moves the view is announced to screen readers.
  • The scrollbar thumb is the keyboard control (arrow keys, Page Up/Down, Home, End), and screen readers hear the time range in view as its value. While it has keyboard focus, the overlay shows the focus ring, so there is one tab stop rather than two.
  • Pressing the Full Timeline or the scrollbar selects the Timeline tile, as pressing the graph does.
  • In read-only views, neither the Full Timeline nor the scrollbar changes the view, matching the graph.

Event shapes: each event type now has a shape as well as a color: square, circle, triangle, inverted triangle, bars, diamond, then "?" for any type beyond the six colors. The shape appears under the event's number in the graph and, at half size, above the event in the Full Timeline.

Fix: the graph's unit label (e.g. "M/S**2") no longer overlaps the graph.

Design note: the scrollbar thumb is 45% black at rest, 55% on hover and 65% while dragging, a step darker than Zeplin's 40/50/60%, so that it has 3:1 contrast with its track.

Implementation

  • New useScrub hook (src/components/ui/) holds the press-and-drag logic shared by the Full Timeline and the scrollbar: grab the view if the press is on it, otherwise center it on the press; clamp at the ends; pointer capture; onScrubStart/onScrubEnd.
  • DynamicScrollbar stays a generic component and gains track presses (via useScrub), onScrubStart/onScrubEnd, a disabled prop that also takes the thumb out of the tab order, a thumbValueText prop for aria-valuetext, and the Zeplin hover and drag states. TimelineScrollbar, its thin wrapper, is replaced by FullTimeline, which renders the strip and the scrollbar together and maps the scrub callbacks to beginViewPreview/endViewPreview.
  • beginViewPreview/endViewPreview now nest, so overlapping gestures are saved together when the last one ends.
  • The Timeline tile selects itself on pointerdown instead of mousedown/touchstart, because the Full Timeline and the scrollbar cancel pointerdown, which suppresses the mousedown that would follow.
  • WaveformPanel gains an "overview" mode (black, 38px tall); the existing modes are unchanged. The overview is a second panel loading the full data range under its own caller id.
  • New EventShape component draws the shape for an event's color. describeView (the announcement text) moves out of TimelinePlot so the Full Timeline can share it.
  • The pointer-event tests share one jsdom PointerEvent mock, src/test/pointer-events.ts, instead of three copies.

Not in this PR

  • Time marker: the Full Timeline doesn't draw the marker yet; marker placement is CLUE-709.
  • Event Key: it still shows plain color swatches rather than shapes; the Zeplin checkbox designs belong with Event Key filtering (CLUE-671). Highlighting an event selected in the table is CLUE-672.
  • Clicking the thumb doesn't focus it. A press cancels the default action, as it did before this PR; keyboard users reach it with Tab.
  • A blank Full Timeline after an early zoom: if the graph zooms or pans before the first load finishes, it can abandon fetches the Full Timeline was waiting on, leaving it blank. The cause is in SeismicQueryService's shared cache and predates this PR, which makes it more likely; it's filed as CLUE-722.

Testing

  • Jest:
    • useScrub through DynamicScrollbar: track press centering and clamping, grabbing within the view or on a thumb widened past it, a second pointer's moves and presses ignored, scrub start and end, cancel, lost pointer capture, unmount, disabled.
    • FullTimeline: overlay and event placement, events clipped to the strip, centering, grabbing the overlay, saving a drag only when it ends, overlapping strip and scrollbar drags saved as one change (counted), a scrub ended and saved when the data goes away, read-only, the scrollbar's label and value text, and announcing only when the view moves.
    • Also: tile selection from a press the control cancels, the model's nested previews, EventShape, EventOverlay, and the overview mode of WaveformPanel.
    • Full unit suite green.
  • Manually, on the branch preview: https://collaborative-learning.concord.org/branch/full-timeline/?appMode=qa&fakeClass=673&fakeUser=student:1&qaGroup=1&unit=seismic&problem=1.1
    1. Add a Wave Runner tile (second toolbar button), pick a station and the Compact Model, and set a date range of a few days.
    2. Click Timeline It!, then click the graph a couple of times to zoom in so the overlay is narrower than the Full Timeline.
    3. Drag the overlay and the scrollbar thumb, click elsewhere on each, undo (one step per gesture), and Tab to the thumb and use the arrow keys.
  • The tile cache had no data for the stations tried locally, so manual testing used a temporary synthetic waveform and synthetic events (not part of this PR). The event shapes were checked by the reviewer's eye rather than by me.

Builds on #3027 (CLUE-673); this PR targets that branch until it merges. The navigation info modal (CLUE-720) follows separately.

🤖 Generated with Claude Code

The Full Timeline shows the whole record as a black waveform on white, with
every event drawn in its own accessible color set, and an overlay marking the
part of the record shown in the graph. Dragging the overlay or the scrollbar
thumb moves the view, keeping the grabbed point under the pointer; pressing
elsewhere on either centers the view there. Each drag is saved, and undone, as
a single change, and neither does anything in read-only views. While the thumb
has keyboard focus, the overlay shows the focus ring.

Each event type now has a shape as well as a color - square, circle, triangle,
inverted triangle, bars, diamond, then "?" - shown under the event's number in
the graph and above the event in the Full Timeline.

- DynamicScrollbar gains track presses, scrub start and end callbacks, a
  disabled state and hover and drag styles; useScrub holds the press-and-drag
  logic it shares with the Full Timeline
- WaveformPanel gains an "overview" mode
- The graph's unit label no longer overlaps the graph

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kswenson
kswenson requested a balanced review from Copilot October 8, 2026 01:18
@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.77301% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.13%. Comparing base (e4d3ff9) to head (ff22346).

Files with missing lines Patch % Lines
src/components/ui/use-scrub.ts 97.72% 1 Missing ⚠️
src/plugins/timeline/components/describe-view.ts 85.71% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3029      +/-   ##
==========================================
+ Coverage   87.07%   87.13%   +0.05%     
==========================================
  Files        1059     1063       +4     
  Lines       60837    60939     +102     
  Branches    16246    16272      +26     
==========================================
+ Hits        52974    53097     +123     
+ Misses       7839     7818      -21     
  Partials       24       24              
Flag Coverage Δ
cypress-regression 70.24% <5.60%> (-0.09%) ⬇️
cypress-smoke 40.43% <5.60%> (-0.08%) ⬇️
jest 61.96% <98.77%> (+0.10%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cypress

cypress Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

collaborative-learning    Run #20625

Run Properties:  status check passed Passed #20625  •  git commit ff22346f7a: Make the Full Timeline's "?" event shape larger and bolder [CLUE-719]
Project collaborative-learning
Branch Review CLUE-719-full-timeline
Run status status check passed Passed #20625
Run duration 03m 47s
Commit git commit ff22346f7a: Make the Full Timeline's "?" event shape larger and bolder [CLUE-719]
Committer Kirk Swenson
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 4
View all changes introduced in this branch ↗︎

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Concurrent scrub sessions and mismatched minimum-width geometry can break gesture history and visual synchronization.

2 open findings
What changed in this PR

Adds a navigable Full Timeline overview to the Timeline tile, including event shapes, scrubbing, keyboard navigation, accessibility announcements, and waveform overview rendering.

Changes:

  • Adds the Full Timeline strip and shared scrub interactions.
  • Adds shape-based event identification and darker overview colors.
  • Extends waveform and scrollbar components with overview and read-only support.
File Description
src/​plugins/​timeline/​components/​timeline.tsx Integrates Full Timeline.
src/​plugins/​timeline/​components/​timeline.scss Adjusts graph and unit-label layout.
src/​plugins/​timeline/​components/​timeline-scrollbar.tsx Removes obsolete wrapper.
src/​plugins/​timeline/​components/​timeline-plot.tsx Shares view descriptions.
src/​plugins/​timeline/​components/​timeline-event-colors.scss Adds overview colors.
src/​plugins/​timeline/​components/​full-timeline.tsx Implements overview navigation.
src/​plugins/​timeline/​components/​full-timeline.test.tsx Tests overview behavior.
src/​plugins/​timeline/​components/​full-timeline.scss Styles overview and overlay.
src/​plugins/​timeline/​components/​event-shape.tsx Renders event shapes.
src/​plugins/​timeline/​components/​event-shape.test.tsx Tests event shapes.
src/​plugins/​timeline/​components/​event-shape.scss Styles event shapes.
src/​plugins/​timeline/​components/​event-overlay.tsx Adds shapes to graph events.
src/​plugins/​timeline/​components/​event-overlay.test.tsx Tests graph event shapes.
src/​plugins/​timeline/​components/​event-overlay.scss Positions graph event shapes.
src/​plugins/​timeline/​components/​describe-view.ts Shares announcement formatting.
src/​plugins/​shared-seismogram/​components/​waveform-panel.tsx Adds overview rendering mode.
src/​plugins/​shared-seismogram/​components/​waveform-panel.test.tsx Tests overview waveform colors.
src/​plugins/​shared-seismogram/​components/​waveform-panel.scss Styles overview background.
src/​components/​ui/​use-scrub.ts Adds reusable pointer scrubbing.
src/​components/​ui/​dynamic-scrollbar.tsx Adds track scrubbing and disabling.
src/​components/​ui/​dynamic-scrollbar.test.tsx Expands scrollbar interaction tests.
src/​components/​ui/​dynamic-scrollbar.scss Adds interaction-state styling.

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

Comment thread src/plugins/timeline/components/full-timeline.scss Outdated
Comment thread src/plugins/timeline/components/full-timeline.tsx
- View previews nest, so overlapping gestures on the strip, the scrollbar
  and the graph are saved together, as one change, when the last one ends
- The overlay and scrollbar thumb stay inside their track when widened to
  their 8px minimum at the right end

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A press on the Full Timeline or the scrollbar selects the Timeline tile;
  the tile now listens for pointerdown, which those controls cancel
- A thumb or overlay widened to its minimum width drags all the way to
  either end
- A scrub in progress when the data range goes away ends and is saved,
  rather than leaving every later view change unsaved
- Events outside the data range are clipped to the strip or left out, and
  moving the view no longer redraws the events
- The scrollbar thumb reads the range in view as its value, and a press
  that leaves the view where it was announces nothing
- The thumb rests at 45% black (55% hover, 65% drag) for 3:1 contrast
  with its track
- WaveformPanel's mode classes are prefixed (mode-overview); the 8px
  minimum width and the overlay teal are shared rather than repeated
- Tests for a second pointer, lost pointer capture, overlapping drags
  saved as one change, and tile selection; the pointer tests share one
  PointerEvent mock

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Overlapping or sub-second scrubs can miss announcements, and boundary-only events are rendered incorrectly.

2 open findings
2 resolved since last review

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

Comment thread src/plugins/timeline/components/full-timeline.tsx Outdated
Comment thread src/plugins/timeline/components/full-timeline.tsx Outdated
- Overlapping scrubs of the strip and the scrollbar are announced once,
  when the last one ends, if the view moved at all
- Events that only touch the start or end of the data are left out, as
  the graph's visible events are

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Previews nest, so a missing endViewPreview leaves every later view change
unsaved. An end without a matching begin means the calls have gotten out of
step, so warn about it in development and tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kswenson
kswenson marked this pull request as ready for review October 8, 2026 07:38
@kswenson
kswenson requested a review from emcelroy October 8, 2026 07:38
Base automatically changed from CLUE-673-timeline-zoom-pan to master October 8, 2026 20:08

@emcelroy emcelroy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍 Excerpts from a Claude-generated review are below. They include some minor issues to consider. Also, you're probably already aware, but there are conflicts to resolve before merging.


This is good work. The code does what the PR description says. The new useScrub hook is small and handles the hard cases: a canceled pointer, a lost pointer capture, a second pointer, unmount during a drag, and a thumb that is drawn wider than the view. When two drags overlap, their changes are saved together when the last drag ends. This works because beginViewPreview/endViewPreview now nest, and the unmount cleanup in useScrub and TimelinePlot keeps the count in balance. FullTimelineEvents does not read the view, so the events are not drawn again when the view moves.

FullTimelineStrip is the only user of DynamicScrollbar, so the changes to that component do not affect other code.

I found no blocking problems. Two findings are small confirmed problems, and two are design questions. The rest are nits.

Findings

1. Records shorter than 2 seconds: the scrollbar can move the view past the end of the data (low)

  1. DynamicScrollbar sets totalRange = max(data range, minViewRange) and gives totalStart + totalRange to useScrub as the end of the track (src/components/ui/dynamic-scrollbar.tsx:73). FullTimeline sets minViewRange to 2000 ms.
  2. Example: a record of 1 second. Zooming is limited to the data range, so the view is also 1 second.
  3. The scrollbar track is 2 seconds long. If you drag the thumb to the right, the view can end at dataStart + 2s, which is after the end of the data.
  4. The keyboard does the same thing. shiftView also limits the view to the extended totalRange, so the End key moves the view past the end of the data.
  5. The strip uses the real data range, so it stops at the end of the data. The two controls then do not agree, and the thumb and the overlay do not have the same width.
  6. The clamp in timeline.tsx runs only when the data range changes, so it does not correct this.

The old scrollbar had the same math, so the problem is not new. But the new overlay makes the difference easy to see. Also, the PR description says "The thumb and overlay are always the same width", and that is not true in this case. This can be a follow-up.

2. disabled does not stop a drag that has already started (low, hardening)

disabled is read only in onPointerDown (src/components/ui/use-scrub.ts:57). If disabled becomes true during a drag, onPointerMove still calls onViewChange until pointerup.

In this PR, disabled is always readOnly. That value is fixed for each document pane, so I do not think this can happen now. But useScrub and DynamicScrollbar are generic, and a later caller could change disabled during a drag.

If you fix it, add the check to onPointerMove:

if (!drag || drag.pointerId !== e.pointerId || disabled) return;
  • Do not add the same check to endScrub. If you do, onScrubEnd is never called, and the preview count stays above zero. Then every later view change stays unsaved.
  • Ending the drag in an effect when disabled changes is more work and has more risk. The effect must release pointer capture through trackRef, and it repeats most of endScrub.

Related, on the base branch: when readOnly is true, TimelinePlot renders without pointer handlers (src/plugins/timeline/components/timeline-plot.tsx:143). If readOnly becomes true during a graph drag that has moved, endViewPreview is not called until the plot unmounts. Until then, view changes from every control stay unsaved. The trigger is equally unlikely, but if you change useScrub, it is a good time to make the graph consistent.

3. Read-only views: should there be a focusable control that states the range? (accessibility, question)

  • In read-only views, disabled sets tabIndex={-1} on the thumb (dynamic-scrollbar.tsx:111), and the strip has aria-hidden="true" (src/plugins/timeline/components/full-timeline.tsx:102).
  • These users do not lose the time range. The start and end times are plain text below the graph (TimeLabel in the timeline-range-row of timeline.tsx). tabIndex={-1} does not remove the thumb from the accessibility tree, so a screen reader user can still reach the slider and its aria-valuetext by reading through the page.
  • What they lose:
    • a Tab stop that states the range
    • the thumb's percentage, which tells where the view sits in the whole record
  • W3C guidance treats focus on disabled controls as a choice about how easy a control is to find, not as a rule.
  • Question: do read-only users need a focusable control that states the range? If yes, keep the thumb at tabIndex={0}. It already has aria-disabled, and handleKeyDown already ignores keys when disabled. Two tests require tabindex="-1" and would then need to change: src/components/ui/dynamic-scrollbar.test.tsx:379 and the read-only test in full-timeline.test.tsx.

4. The "Full Timeline" label can hide event shapes (visual, question)

.full-timeline-label has a white background and is centered on the top edge of the strip. It comes after the event shapes in the DOM, and the shapes are drawn at top: -9px (src/plugins/timeline/components/full-timeline.scss:58). Thus the label can cover the shape of an event near the middle of the record.

In the design spec, the label similarly covers event shapes. So it's likely that's acceptable, but I thought it was worth noting just to make sure we're all aware of that.

5. Small points (nits)

  • hasSelectionModifier (src/utilities/event-utils.ts:13) does not list PointerEvent in its signature. The code compiles because PointerEvent extends MouseEvent. Adding it would make the new pointerdown use clearer.
  • In the Full Timeline, the ? shape is drawn at about 6.5px. It may be hard to read. The PR says the shapes were "checked by the reviewer's eye", so check this one on the branch preview.

Tests

Tests that claim more than they prove

  1. "is selected by a press that a control cancels" (src/plugins/timeline/components/timeline-tile.test.tsx:172). The test adds a listener that cancels pointerdown. That listener has no effect, because jsdom never sends the mousedown that a browser sends after pointerdown. The test proves only that a pointerdown selects the tile. That still catches a return to the old mousedown handler. But the name suggests that the cancel case was checked, and it was not. Suggestion: rename the test, or remove the listener and add a comment.
  2. "grabs the overlay … as at its minimum width" (full-timeline.test.tsx:169). jsdom does not apply CSS, so in the test the overlay is never wider than the view. The test sends the press directly to the overlay at a point outside the view. It checks the correct code path, but not the CSS part. The name says that the case is simulated, so this is acceptable.

Gaps

  • Undo is not tested. No test runs undo after a drag. A test that undoes once and checks that both fields go back to their old values would prove the "single change" claim in the PR description.
  • Right-click is not tested. useScrub ignores e.button !== 0, but no scrollbar or Full Timeline test checks this. The graph has a test for it (timeline-plot.test.tsx:120).
  • The rounding in handleViewChange is not tested. In the tests, 1px is exactly 345,600 ms, so every value is already a whole number. To test the rounding, use a track width that gives fractions.
  • The keyboard is not tested through FullTimeline. No test checks that an arrow key changes the model and saves the change right away.
  • The overview height (38px) is not tested. The waveform test checks only the stroke and fill colors.
  • Minor: the event tests check the position of only one of the two events. The overlay test says the label is "centered on the event", but it does not compare the shape's position with the button's position.

Code comments

Almost all the comments are correct but there are some minor issues.

  1. Misleading: src/plugins/timeline/models/timeline-content.ts:241, "Unpaired calls are a bug: a missing end leaves later view changes unsaved."
    • This comment is inside the check for an extra end, but it explains the result of a missing end. This check cannot detect a missing end.
    • Here is what the check actually prevents. Without it, the count goes to -1. The next beginViewPreview then does not start a preview, so that gesture saves a change on every move.
    • Suggested text: "An end without a begin is a bug. Ignore it, so that the count can't go below zero and stop the next preview from starting."
  2. Imprecise: use-scrub.ts:126. grabOffset is "Distance from the start of the view to the point under the pointer". This is not true for a thumb drawn wider than the view, because the code then limits the offset to the view. Add "limited to the view".
  3. Imprecise: dynamic-scrollbar.test.tsx:354, "the thumb is drawn at its minimum width". In jsdom the thumb is not drawn wider. Use "would be drawn".
  4. Small point: the describeView doc says the text is for screen reader announcements. It is now also used as the scrollbar's aria-valuetext.

kswenson and others added 2 commits October 8, 2026 15:07
Brings in the final review round of the Timeline zoom and pan PR. Its
saved-view check (an undo mid-drag discards the preview) now applies
when the outermost of any nested previews ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Keep the view within records shorter than 2 seconds. The scrollbar
  no longer stretches its track to the shortest view, so the thumb
  and the overlay match and the view can't move past the data's end.
- A scrub that is disabled midway stops moving the view, and still
  ends normally.
- Clarify comments on preview nesting, `grabOffset` and `describeView`,
  and the name of a tile selection test that jsdom can't fully check.
- Accept pointer events in `hasSelectionModifier`'s signature.
- Test undo after a previewed gesture, scrollbar keys through the Full
  Timeline, and that right-button presses are ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kswenson

kswenson commented Oct 8, 2026

Copy link
Copy Markdown
Member Author

@emcelroy Thanks for the review! I merged master, which resolved the conflicts, and addressed most of your points in e48a5f7:

  • 1. Short records: fixed. The Full Timeline no longer gives the scrollbar a 2-second minimum, so for a record shorter than that, the track is the data range. The thumb and overlay match, and neither the thumb nor the keyboard can move the view past the end of the data.
  • 2. disabled mid-drag: fixed as you suggested, in onPointerMove only, so onScrubEnd still runs. I left the graph's read-only case alone, since it can't happen today either.
  • 3. Focusable thumb when read-only: leaving it as is. The range is shown as text below the graph, and the thumb can still be reached by reading through the page.
  • 4. Label covering shapes: as you noted, this matches the design spec, so no change.
  • 5. Nits: hasSelectionModifier now lists pointer events. I'll check the ? shape on the branch preview.
  • Tests: added undo after a drag (one undo restores both fields), an End key press through the Full Timeline (saved right away), right-button presses on the strip and the scrollbar, a 1-second record, and disabling during a drag. I renamed the tile selection test and added a comment saying what jsdom can't check.
  • Comments: all four fixed.

The merge also combines master's new check (an undo during a drag discards the drag instead of being saved over) with this branch's nested previews. The check now runs when the last nested preview ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kswenson
kswenson merged commit 32268e2 into master Oct 8, 2026
28 of 29 checks passed
@kswenson
kswenson deleted the CLUE-719-full-timeline branch October 8, 2026 22:42

This branch was previously deployed

1 inactive deployment
development — ff22346f Deployed Oct 8, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants