npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@0k/time-chart

v0.2.0

Published

Framework-free linked time-series charts with local calendar guides.

Readme

@0k/time-chart

Linked, zoomable time-series charts for the browser, built on Apache ECharts. No framework required.

  • Day, month and year guides that fade in with zoom, weekends hatched, and a "now" line; all in local time, correct across DST changes.
  • Wheel zoom and drag pan on the time axis; charts sharing a link key zoom together.
  • Stacked areas, min/max bands and gaps for missing values.
  • Light and dark themes, following the OS by default.

Install

npm install @0k/time-chart

Usage

import { createTimeChart } from '@0k/time-chart';

const now = Date.now();
const chart = createTimeChart(document.getElementById('chart')!, {
  series: [{
    id: 'sensor-a',
    label: 'Sensor A',
    points: [[now - 3_600_000, 2], [now, 3]],
  }],
  formatValue: value => value.toFixed(2),
  onWindowChange: (start, end) => { /* fetch data for this window */ },
});

chart.setSeries(newSeries);   // keeps the current window and hidden series
chart.setWindow(start, end);
chart.destroy();

The element must have a nonzero size. Times are Unix milliseconds.

Series

| Field | Description | |--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------| | id | Unique identifier | | label | Name shown in the legend and tooltip | | points | [time, value] pairs sorted by time; null value is a gap | | min, max | Optional band bounds, one per point | | group | Series of a group get close colors | | stack | Stack with the other stacked series | | color | Explicit CSS color instead of the generated one | | dash | 'solid' (default), 'dashed' or 'dotted' line style | | width | Finite positive line width in pixels (default 1); highlighting doubles it, dimming halves it | | fill | Opacity in (0,1]; fill to the bottom of the y axis, not stacked; cannot be combined with stack: true | | band | {pattern, opacity} for min / max bounds; pattern is 'solid' (default) or diagonal 'hatch' in the series color; opacity is in (0,1] (default 0.18) | | line | Draw the line (default true); false keeps its band/fill and legend entry but excludes it from the tooltip |

Options

| Option | Default | Description | |-----------------------|---------------------|------------------------------------------------------------------------------------------------------------------------------------------| | series | required | Series to draw | | window | data extent | Initial {start, end} shown | | formatValue | localized number | Formats y-axis labels and tooltip values | | formatTime | localized date/time | Formats the tooltip time | | yAxis | | {min, max, name, interval, minorInterval}; max: 'data' fixes it at 1.2 × data maximum (default) | | yAxis.interval | automatic | Finite positive major tick and label step | | yAxis.minorInterval | disabled | Finite positive minor tick step with dashed minor split lines; requires interval and must divide it into an integer count from 2 to 99 | | tooltip | 'axis' | 'axis' (all series) or 'item' (one) | | legend | | {show, collapsed, width, maxWidth}, scrolls, on right | | legend.width | 340 | Column width in pixels, or 'auto' to fit the widest label | | legend.maxWidth | 240 for 'auto' | Caps the column; when set, also caps a numeric width | | link | | Charts with the same key zoom, pan and hover together by time; their 'auto' legends share the widest column | | onWindowChange | | Called after the user zooms or pans | | onLegendToggle | | Called when the user toggles a series | | locale | browser locale | Locale for labels and default formatters | | theme | 'auto' | 'auto', 'light' or 'dark' |

With legend: { width: 'auto' } the legend column is as wide as its widest label: at least 80 pixels (less only if maxWidth is smaller), at most maxWidth; longer labels are ellipsized and shown in full on hover. Labels are measured again when setSeries changes them. Charts sharing a link key and an 'auto' legend all take the widest column of the group, each within its own maxWidth, so with the same maxWidth their plots line up; the group adjusts when one of them changes labels or is destroyed. A chart with a numeric width keeps it.

Charts sharing a link key are linked by time: after a user zoom or pan they all show the same time window, and hovering one puts the others' pointers at the same time, even when their data covers different ranges or different timestamps. With tooltip: 'axis' each linked chart also shows its tooltip, with the values of its own points nearest to that time; with 'item' only the pointer is linked. A chart hides its pointer when the time is outside the span of its points or outside its window. onWindowChange fires on every affected chart after 150 ms without another zoom or pan, with that chart's current window. setWindow changes only the receiving chart and never fires the callback; set it on each linked chart when choosing a range programmatically.

Methods

| Method | Description | |----------------------------|--------------------------------------------| | setSeries(series) | Replace the data | | setWindow(start, end) | Show this time range (no onWindowChange) | | getWindow() | Current {start, end} | | setHidden(ids) | Hide these series | | highlight(ids) | Emphasize these series; null resets | | setLegendCollapsed(bool) | Hide or show the legend | | resize() | Redraw after a size change | | destroy() | Release the chart; safe to call twice |

calendarGuides(start, end, widthPx, now, locale?) is also exported: it returns the guide lines and weekend bands for a time range, without drawing anything.

Development

npm install
npm run check   # typecheck, tests, build
npm run demo    # http://127.0.0.1:5187/demo/

To use a working copy from another project without changing its committed files:

npm run watch                                # here
npm install --no-save /path/to/time-chart    # in the other project

License

MIT

Changelog

0.2.0 (2026-10-11)

New

  • Fit the legend to its labels, keeping linked charts aligned. [Valentin Lab]

    With legend: { width: 'auto' } the legend column fits its widest label, measured in the legend's font, between 80 pixels and legend.maxWidth (240 by default; an explicit maxWidth below 80 wins); longer labels are ellipsized. Charts sharing a link key with auto legends all take the group's widest column, each within its own maxWidth, so with the same maxWidth their plots line up; they adjust when one of them changes labels or is destroyed.

    maxWidth also caps a numeric width; an invalid value for either now throws. The demo charts switch to auto legends.

  • Choose the step between y-axis labels, with dashed minor lines. [Valentin Lab]

    yAxis.interval sets the step between y-axis labels, and yAxis.minorInterval subdivides it with dashed minor lines. Invalid values throw: minorInterval requires interval and must divide it into an integer count from 2 to 99 (ECharts ignores more). Minor ticks are drawn in the palette's axis color, so they follow the light and dark themes.

  • Draw dashed, dotted, thick, filled, hatched and line-less series. [Valentin Lab]

    Each series can now set its line style with dash ('solid', 'dashed' or 'dotted') and width in pixels, which highlighting doubles and dimming halves. fill shades the area down to the bottom of the y axis at the given opacity, without stacking. band: {pattern, opacity} draws the min/max band solid or hatched diagonally in the series color, at a chosen opacity. line: false draws only a series' fill or band: it keeps its legend entry but is left out of the tooltip.

    Invalid widths and opacities throw, as do fill with stack: true and band on a series without min and max.

Changes

  • Depend on ECharts 6, which npm audit no longer flags. [Valentin Lab]

    ECharts 5 has a security advisory (GHSA-fgmj-fm8m-jvvx, an XSS in the tooltip of lines series) that only 6.1.0 fixes, and 5.x gets no more fixes. The charts drawn here never used a lines series, but every consumer's npm audit reported it, with no way to resolve it.

    Two ECharts 6 defaults are pinned back so the charts look as before:

    • grid.outerBoundsMode: "none": ECharts 6 shrinks the plot to fit wide axis labels, which would move the plot away from the calendar guides, the legend and the alignment of linked charts.
    • yAxis.nameMoveOverlap: false: ECharts 6 pushes the y-axis name past its labels, off the canvas; it stays 46 px from the axis line.

    AGENTS.md now names ~/dev/ts/rrd-graph as the consumer that uses this working copy.

    Contract:

    • echarts is ^6.1.0; the public API and options are unchanged.
    • The plot fills exactly the grid insets, whatever the label widths.
    • The y-axis name is drawn 46 px left of the axis line.
    • Tests: test/option.test.ts; demo/verify.py passes in Chrome, and the demo renders as with ECharts 5.6 apart from ECharts' own time-axis labelling.

Fix

  • Link charts by time, so zoom, pan and hover line up when their data differs. [Valentin Lab]

    echarts.connect relays a zoom as a percentage of each chart's own time axis, and a hover as a data index of the hovered chart. Linked charts whose data covered different ranges therefore showed different windows, and hovering one put the other charts' pointers and tooltips at other times, sometimes 600 px away, or nowhere.

    The link registry now relays by time instead of using connect:

    • a user zoom or pan sends its time window to every linked chart, which moves only its zoom (its axis grows if the window falls outside it);
    • a hover sends the hovered time; each linked chart draws its pointer there, with its axis tooltip showing its own nearest values, or nothing when the time is outside the span of its points or its window. The pointer does not snap to the nearest point (that would differ between charts), and it also links with tooltip: "item", without a tooltip.

    Contract:

    • After a user zoom or pan, every chart of a link group shows the same window and fires onWindowChange once, after 150 ms; no echo.
    • setWindow and setSeries never move another chart's window or pointer; only a change of the shared auto-legend width redraws them.
    • A hover points every linked chart exactly at the hovered time, or hides its pointer outside its data's span or window; leaving hides them all.
    • Tests: test/link.test.ts (real ECharts), test/chart.test.ts, demo/verify.py in Chrome.
  • Let a long legend scroll, aligned with the top of the plot. [Valentin Lab]

    The legend was the canvas legend of echarts, which can only page with small arrow buttons when its entries do not fit: the mouse wheel over it zoomed the chart instead, and its first entry sat a few pixels below the plot's top edge.

    The legend is now an HTML list over the chart's right margin. It spans exactly the plot's height, scrolls with the wheel without zooming the chart or scrolling the page, and keeps its scroll position across redraws. Long labels are truncated clear of overlay scrollbars and show their full text on hover. Clicking an entry, or pressing Space or Enter on it, still hides or shows its series and calls onLegendToggle; hidden series are greyed out. The options and API are unchanged.

    A host element with position: static is switched to relative so the legend can be placed inside it.

0.1.2 (2026-10-08)

New

  • Draw linked, zoomable time-series charts with calendar guides. [Valentin Lab]

    Framework-free TypeScript library over Apache ECharts: day, month and year guides fading with zoom density, hatched weekends, a "now" line, time-axis wheel zoom and drag pan past the data, a y-axis fixed from the full data, stacked areas, minimum and maximum bands, light and dark palettes following the OS, and charts sharing a link key zooming together.

Fix

  • Draw the hatching of a weekend already in progress at the window start. [Valentin Lab]

    A window starting on a Saturday or Sunday drew no hatching for that weekend, because bands were only emitted for Saturday midnights inside the window. The band of a weekend overlapping the window start is now drawn too.

  • Keep day guides on local midnight after a DST jump at midnight. [Valentin Lab]

    In zones whose clocks jump at midnight (Chile, Azores, Cuba), the jump day starts at 01:00. Stepping one Date forward with setDate carried that 01:00 onto every following day guide and weekend band, until the end of the window. Each day is now built from its date parts, so only the jump day itself starts at 01:00.

  • Draw legend icons as solid swatches readable in dark mode. [Valentin Lab]

    ECharts' default legend icon is an empty circle filled white, a glaring white dot on a dark background.