Skip to content

feat(timeline-layers): add TimelineLayer CompositeLayer + example#517

Closed
Copilot wants to merge 4 commits intomasterfrom
copilot/add-chrongl-timeline-layers
Closed

feat(timeline-layers): add TimelineLayer CompositeLayer + example#517
Copilot wants to merge 4 commits intomasterfrom
copilot/add-chrongl-timeline-layers

Conversation

Copy link
Contributor

Copilot AI commented Feb 20, 2026

Extracts the timeline prototype from PR #379 into a proper reusable TimelineLayer in @deck.gl-community/timeline-layers, addressing Ib's feedback and the noodles.gl cross-project timeline discussion. The goal: a shared GPU-accelerated timeline primitive both noodles.gl and other deck.gl apps can mount directly.

Module: modules/timeline-layers/src/layers/timeline-layer/

  • timeline-types.tsTimelineClip, ClipWithSubtrack, TimelineTrack, TrackWithSubtracks, and all internal/callback info types
  • timeline-utils.tspositionToTime, timeToPosition, timeAxisFormatters (seconds, minutesSeconds, hoursMinutesSeconds, timestamp)
  • timeline-collision.ts — greedy subtrack assignment: overlapping clips automatically stack into subtracks
  • timeline-layout.tsSelectionStyle, TimelineLayout, DEFAULT_TIMELINE_LAYOUT
  • timeline-layer.tsTimelineLayer extends CompositeLayer: tracks, clips, axis, scrubber, hover/select state, zoom/pan API

All new exports re-exported from the module root.

Example: examples/timeline-layers/timeline-layer/

Per Ib's review comments on #379 — app is concise, scrubber/controls broken into separate components:

  • app.tsx — lean DeckGL + OrthographicView mount
  • demo-controls.tsx — sidebar panel (track count, duration, formatter, scrubber, zoom, advanced settings)
  • timeline-hooks.ts — interaction hooks: useWheelZoom, useContainerHandlers, useDeckGLHandlers, useCursorGetter
  • demo-utils.ts — random track/clip generator with 30% overlap probability to exercise collision detection

Tests: modules/timeline-layers/test/timeline-layer.spec.ts

47 unit tests across 7 suites covering all pure-function logic (no WebGL context required):

  • assignClipsToSubtracks — empty input, non-overlapping lanes, overlapping lanes, packing, sort-order independence, property preservation
  • calculateSubtrackCount — 1/2/3 subtrack scenarios
  • positionToTime — edges, midpoint, clamping, offset, non-zero startMs
  • timeToPosition — edges, midpoint, clamping, round-trip with positionToTime
  • timeAxisFormattersseconds, minutesSeconds (padding), hoursMinutesSeconds
  • generateTimelineTicks — count, first/last positions, formatter labels
  • TimelineLayer — static props, zoomToPoint (zoom in/out, max/min clamp, viewport callback range, boundary clamping)

Usage

import {TimelineLayer} from '@deck.gl-community/timeline-layers';

new TimelineLayer({
  data: tracks,           // TimelineTrack[]
  timelineStart: 0,
  timelineEnd: 60_000,
  currentTimeMs: 5_000,
  onClipClick: ({clip, track}) => console.log(clip.label),
  onViewportChange: (startMs, endMs) => setViewport({startMs, endMs}),
  onZoomChange: (zoom) => setZoom(zoom)
});

zoomToPoint(zoomFactor, mouseX, currentZoom) is exposed on the layer instance for wheel-zoom integration from the host app.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Co-authored-by: charlieforward9 <62311337+charlieforward9@users.noreply.github.com>
Copilot AI changed the title [WIP] Add timeline layers module based on feedback feat(timeline-layers): add TimelineLayer CompositeLayer + example (chron.gl kickstarter) Feb 20, 2026
@charlieforward9 charlieforward9 marked this pull request as ready for review February 20, 2026 14:21
@charlieforward9
Copy link
Collaborator

@ibgreen I'd rather get a packaged example out now and incrementally split it up. Just put something on master and make it available for the community to help test out.

@copilot I want to call it time.gl or temp.gl - shorter. and make it released - finally - we want his branch off the existing work we did months ago. Keep the history

…ine-types, use in TrackWithSubtracks

Co-authored-by: charlieforward9 <62311337+charlieforward9@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 20, 2026

@ibgreen I'd rather get a packaged example out now and incrementally split it up. Just put something on master and make it available for the community to help test out.

@copilot I want to call it ...

Fixed the CI failure in e3c6113. The TS2345 error was because TrackWithSubtracks.clips was typed as TimelineClip[] (where subtrackIndex is optional), but _buildClipPolygon expected ClipWithSubtrack (where subtrackIndex is required after collision detection).

Fix: moved ClipWithSubtrack from timeline-collision.ts into timeline-types.ts, updated TrackWithSubtracks.clips: ClipWithSubtrack[], and explicitly typed the _buildClipPolygon parameter. All 263 tests pass, tsc --noEmit is clean.

On renaming to time.gl/temp.gl — that would mean a new package name (e.g. @deck.gl-community/time-layers or a standalone time.gl package). Happy to do that in a follow-up — want me to rename the module from timeline-layers to something shorter now, or land this first and rename separately?

@charlieforward9
Copy link
Collaborator

@copilot we need some tests for this layer

…tection, time utils

Co-authored-by: charlieforward9 <62311337+charlieforward9@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 20, 2026

@copilot we need some tests for this layer

Added modules/timeline-layers/test/timeline-layer.spec.ts in d2f4824 — 47 tests across 7 suites, all passing (310 total, 52 files):

  • assignClipsToSubtracks — empty input, non-overlapping on same lane, overlapping on separate lanes, packing 3 clips into 2 lanes, sort-order independence, property preservation
  • calculateSubtrackCount — returns 1 for empty/non-overlapping, 2 for pair overlap, 3 for triple overlap
  • positionToTime — left/right edges, midpoint, clamping (before/after range), non-zero offset, non-zero startMs
  • timeToPosition — left/right edges, midpoint, clamping, and a round-trip with positionToTime
  • timeAxisFormattersseconds (0ms, 1500ms, 5000ms), minutesSeconds (padding, 0ms, rollover), hoursMinutesSeconds (0ms, 1:01:01, padding)
  • generateTimelineTicks — correct count, first/last tick positions, formatter applied to labels
  • TimelineLayer — static props, zoomToPoint (zoom in/out, max/min clamp, viewport callback range, boundary clamping at left/right edges)

@charlieforward9 charlieforward9 changed the title feat(timeline-layers): add TimelineLayer CompositeLayer + example (chron.gl kickstarter) feat(timeline-layers): add TimelineLayer CompositeLayer + example\ Feb 20, 2026
@charlieforward9 charlieforward9 changed the title feat(timeline-layers): add TimelineLayer CompositeLayer + example\ feat(timeline-layers): add TimelineLayer CompositeLayer + example Feb 20, 2026
charlieforward9 added a commit that referenced this pull request Mar 5, 2026
…tay in modules/

Separates in-progress work from the published modules tree.
All npm-published 9.2.8 packages remain untouched in modules/.

Moved to dev/ (flat):
- modules/timeline-layers -> dev/timeline-layers (in progress, refs #517 #379)
  - examples/timeline-layers/horizon-graph-layer -> dev/timeline-layers/examples/
- examples/arrow-layers-wip -> dev/arrow-layers (renamed, drop -wip suffix)

Config:
- package.json + lerna.json: add dev/* to workspaces/packages
- tsconfig.json: add dev/ to include, add timeline-layers path alias
- vitest.config.ts: include dev/ in node/browser/headless globs and coverage
- keep modules/basemap-props/** excluded from node tests (uses global describe)

Rationale from #512: dev/ keeps WIP visible without polluting docs search
or the published modules tree. Design proposals live in GitHub Issues/Discussions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
charlieforward9 added a commit that referenced this pull request Mar 5, 2026
…etup

- Add dev/timeline-layers/examples/* to root workspace glob so yarn
  resolves the package correctly
- Add vite.config.local.mjs (aliases @deck.gl-community/timeline-layers
  to src/ so dist/ is never stale during dev)
- Fix tsconfig.json extends depth (examples/ moved one level deeper vs
  original PR path)
- Update example README with prototype video + end-goal screenshot from
  PR #379, start-local instructions, and context links to #379/#517/#38

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
charlieforward9 added a commit that referenced this pull request Mar 5, 2026
…ng (#544)

* fix(website): remove three/wild-forest from sidebar until doc page exists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ci): add PR test workflow, vitest coverage, and unit tests

Closes a degree of #82 by establishing an automated test baseline for every PR.
Applies reviewer feedback from #512 re: dev/ directory for WIP/RFC content.

## CI workflow (.github/workflows/test.yml)
- Triggers on push to master and all pull_request events
- lint job: runs yarn lint (no build required)
- test-node job: builds, runs vitest node project with v8 coverage, uploads to Coveralls
- test-headless job: installs Playwright Chromium, runs headless browser tests

Modeled after deck.gl's test.yml (see visgl/deck.gl vitest-migration-rfc) using
@vitest/browser + Playwright as first-class visgl testing utilities.

## Coverage (vitest.config.ts + package.json)
- Added test.coverage block: provider v8, reporters text + lcov (for Coveralls)
- Scoped to modules/*/src/**, excludes basemap-props* and template
- Added test-ci script: vitest run --project node --coverage
- Added @vitest/coverage-v8 devDependency

## New unit tests (all 308 pass, 60 test files)
- modules/editable-layers/test/immutable-feature-collection.node.spec.ts
  ImmutableFeatureCollection: add/delete/replace/removePosition, polygon ring
  wrapping, 3D elevation preservation, immutability guarantees (17 tests)
- modules/editable-layers/test/imports.node.spec.ts
  Public API surface smoke test: draw, alter, and measurement mode exports (6 tests)
- modules/layers/test/imports.node.spec.ts (2 tests)
- modules/widgets/test/imports.node.spec.ts (4 tests)
- modules/geo-layers/test/imports.node.spec.ts (3 tests)

## dev/ directory
Per ibgreen's feedback in #512: establishes dev/ as the canonical home for RFCs
and WIP content that should NOT be indexed by the docs site search.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: remove dev/ directory — RFCs move to GitHub Issues/Discussions

Per discussion: RFCs are better tracked as GitHub Issues or Discussions
where commenting and cross-linking are native. The docs tree stays focused
on true usability docs and basic rationale only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: flat dev/ for WIP modules and examples, published modules stay in modules/

Separates in-progress work from the published modules tree.
All npm-published 9.2.8 packages remain untouched in modules/.

Moved to dev/ (flat):
- modules/timeline-layers -> dev/timeline-layers (in progress, refs #517 #379)
  - examples/timeline-layers/horizon-graph-layer -> dev/timeline-layers/examples/
- examples/arrow-layers-wip -> dev/arrow-layers (renamed, drop -wip suffix)

Config:
- package.json + lerna.json: add dev/* to workspaces/packages
- tsconfig.json: add dev/ to include, add timeline-layers path alias
- vitest.config.ts: include dev/ in node/browser/headless globs and coverage
- keep modules/basemap-props/** excluded from node tests (uses global describe)

Rationale from #512: dev/ keeps WIP visible without polluting docs search
or the published modules tree. Design proposals live in GitHub Issues/Discussions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: update yarn.lock after timeline-layers workspace path change

Moving timeline-layers from modules/ to dev/ changed its workspace
identifier. Yarn 4 hardened mode on public PRs treats installs as
--immutable, so the lockfile must be committed with the updated path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(website): update horizon-graph-layer path after move to dev/timeline-layers

The example was moved from examples/timeline-layers/horizon-graph-layer to
dev/timeline-layers/examples/horizon-graph-layer. Update the website component
require() and code link to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(website): update timeline-layers webpack alias to dev/ path

Follows the move of modules/timeline-layers → dev/timeline-layers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add TimelineLayer to timeline-layers module with example

Co-authored-by: charlieforward9 <62311337+charlieforward9@users.noreply.github.com>

* fix(timeline-layers): resolve TS2345 - move ClipWithSubtrack to timeline-types, use in TrackWithSubtracks

Co-authored-by: charlieforward9 <62311337+charlieforward9@users.noreply.github.com>

* test(timeline-layers): add unit tests for TimelineLayer, collision detection, time utils

Co-authored-by: charlieforward9 <62311337+charlieforward9@users.noreply.github.com>

* chore(dev/timeline-layers): wire example into workspace + local dev setup

- Add dev/timeline-layers/examples/* to root workspace glob so yarn
  resolves the package correctly
- Add vite.config.local.mjs (aliases @deck.gl-community/timeline-layers
  to src/ so dist/ is never stale during dev)
- Fix tsconfig.json extends depth (examples/ moved one level deeper vs
  original PR path)
- Update example README with prototype video + end-goal screenshot from
  PR #379, start-local instructions, and context links to #379/#517/#38

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): grant pull-requests+checks write for Coveralls PR comment

The global permissions block only had contents:read. Coveralls needs
pull-requests:write to post a coverage comment and checks:write for
the status check. Scoped to test-node job only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): post coverage summary directly as PR comment

Replace Coveralls upload (which requires the Coveralls GitHub App to be
installed on the org to post PR comments) with an actions/github-script
step that reads the vitest coverage table from stdout and posts/updates
it as a PR comment directly. No external service setup needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(ci): remove duplicate test.yaml superseded by test.yml

test.yaml was the original single-job workflow. test.yml replaced it
with parallel lint/test-node/test-headless jobs. Both were running on
every PR causing duplicate test-node checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): compact coverage comment to overall % only

Replace verbose per-file table with a single "All files" totals line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ci): show coverage delta vs master in PR comment

- Save "All files" line as artifact on master pushes
- On PRs, find + download latest master baseline artifact
- Show delta (▲/▼/●) next to each metric vs master

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
@charlieforward9
Copy link
Collaborator

Superceded by #544

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.

2 participants