Re-evaluated after round 2. Assessment of the canvas renderer
(src/canvas-timeline.js + the src/timeline/* mixins and helper
modules). Validated by test/integration.js (66 layout/logic assertions) and
test/render-smoke.js (25 full render/interaction assertions), all green.
Deployment model (this re-eval assumes it): a private, internal tool, used on desktop browsers only, for single-day (24h) timelines, shipped as source / an npm package built from source. Under that model, several items that were "gaps" in the first analysis are explicitly out of scope by decision: HTML-template parity, full accessibility / screen-reader support, i18n / RTL / CJK, and touch/pinch. They are listed below for completeness but are not gating.
Status reflects round 2. Closed = resolved this round; Scoped out = intentionally not addressed per the deployment model.
| Risk | Sev | Status & notes |
|---|---|---|
No rangechange(d) events | Closed | Added rangechange (continuous) + debounced rangechanged, plus
itemover/itemout and contextmenu. Event parity gap closed. |
| No keyboard navigation | Closed (desktop) | Added focusable canvas, a focus ring, arrow-key item navigation (within-group), Shift+Arrow nudge, zoom/fit/Home/End/Esc/Enter. Not full screen-reader a11y (scoped out), but the keyboard model now exists for desktop power users. |
| Hardcoded font stack | Closed | fontFamily/fontSize options now drive all canvas text; plus an icon /
sprite-atlas system and 24-hour time labels. |
| Minimap / axis not aligned with body | Closed | Both the time axis and minimap are now offset by the label-panel + scrollbar width so labels and the window box pixel-align with the body grid. |
Follow-now setInterval | Closed (mitigated) | Still a 1s timer, but it now skips ticks while the tab is hidden and is cleared in
destroy(). rAF-alignment noted in optimizations.html as optional. |
| No published types / packaging | Closed | Shipped canvas-timeline.d.ts with a documented public API; package.json
exposes types/exports and a clean files allowlist, so it
installs as an npm package built from source. ESM-only (no UMD) — fine for the bundler-based app. |
| Stacking recomputed every pan/zoom | Open (acceptable) | ~8ms at 10k items, measured. Mid-drag restack is now skipped (rows frozen until drop). For single-day datasets this is comfortably within budget; visible-only/incremental stacking is documented in optimizations.html but deliberately not built. |
| No HTML item/group templates | Scoped out | Canvas draws text + shapes; styling is data-driven (colors, patterns, icons, accent strips).
The app does not rely on template. Rich content via DOM overlay on doubleClick/
contextmenu. |
| Accessibility / screen readers / i18n / RTL | Scoped out | Private, internal, desktop, single-locale tool — ARIA/SR/RTL/CJK explicitly out of scope. |
Missing item types (point, background) | Open (low) | Only box/range/block implemented. Map point→short range; background can
be approximated with a back-layer block. Implement only if a call-site needs them. |
| DST / timezone in axis labels | Open (low) | Fixed-ms tick stepping can land slightly off a clean local label around DST/month boundaries. Cosmetic for a 24h same-day view. Calendar-aware stepping noted in optimizations.html. |
| Touch / pinch | Scoped out | Desktop-only deployment; basic single-pointer touch still works, multi-touch pinch is not a goal. |
Round 1 added render-smoke.js, verified destroy() cleanup
and instance isolation, and confirmed block stacking/hit-test ordering — no crashing defects found.
Round 2 ran two further deep review passes and fixed every defect found, each as a
discrete, revertable commit (see bugs-and-issues.txt):
...options
re-introduced raw Date min/max, so zooming a bounded (single-day) timeline
produced NaN and a blank canvas. Fixed by normalizing last.nestedGroups hung the page in an unbounded tree-walk — now
guarded by the group count.NaN on zoom — _pixelToTime/_zoom now guard width ≤ 0.setItems discarded the window — an explicit start/end was
overridden by a data-fit, and every later setItems reset the user's pan/zoom. Now fits
only on first load; otherwise preserves and re-clamps the window.remove skipped bounds/height recompute (unlike add/update), so
group heights stayed too tall after deletions — made consistent.setWindow didn't restack (stale ungrouped layout) and a queued render
could fire after destroy() — both closed; axis & minimap alignment fixed; selected
items drawn on top; time-probe moved off the items.The renderer was also split into focused modules (layout/rendering/interaction mixins + theme/colors/ canvas-draw/time-format helpers) with no behavior change, raising readability without a build step.
point/background only if a call-site needs them.npm test runs in CI; a headless-browser pixel/interaction
suite (Playwright) and a perf-regression gate would catch visual regressions the stubbed smoke test can't.GO. For a private, desktop, single-day, source-shared timeline this renderer is ready: the public API and types are documented, the known-bug list is closed, the test suite is green, and the code is modular enough to hand over as a starting point. Adopt it behind the parallel-run flag to eyeball parity on the real data, then flip the default. Revisit §4 only if the deployment scope widens beyond the assumptions above.