Skip to content

Migrate to uplot#3295

Open
fakemonster wants to merge 6 commits into
mainfrom
migrate-to-uplot
Open

Migrate to uplot#3295
fakemonster wants to merge 6 commits into
mainfrom
migrate-to-uplot

Conversation

@fakemonster

Copy link
Copy Markdown
Contributor

This PR replaces recharts with uPlot. The core of the decision here is really just "stop using an svg-based chart", but uPlot is a fast option even among canvas-based renderers, and (with a bit of patience) still quite flexible when it comes to styling.

Setting the code itself aside, this should not be that noticeable a change. Charts will render faster, but the smaller datasets we're using right now weren't really a problem for recharts; it would be much more noticeable once we started work on #3270. The three most noticeable changes today:

  1. a switch from svg to canvas will, in lower dpr screens (like, 1x), make the linework more stark. I consider this a benefit for those of us with pitiful 1x displays; we get some small slice of the Apple experience.
  2. the ticks are picked by uplot now. Now charts with very low numbers (say, a max of 1.5) won't get squished to the bottom, and we'll have generally "nice" numbers (5/15 minutes marks, y-axis multiples of 100, 1000, etc).
  3. the top grid line has been removed. Since we give uplot control over ticks now, it seems a bit odd to stick a line there willy-nilly.

As an example of difference 2 in particular, here's the visual comparison test for instance network metrics:

Recharts uPlot
instance-metrics-network-expected instance-metrics-network-actual

Getting these in puts us in a spot where we can pretty easily see the
differences between uplot and recharts.
This is just a touch more portable.
Verbose, but fairly straightforward. The only meaningful changes you
should see are the lack of a top grid line (since uplot does tick
calculation, this would often be quite tight with the line below) and
uplot's tick selection logic replacing ours (which is for the greater
good).
Any canvas drawing element is going to need themes this way. I
considered doing something more involved, like computing the style
within the subscription, or actually making a the subscription an effect
hook, but the thing is, you kind of want to only use getComputedStyle
consciously.
If you passed an anonymous function as the yAxisTickFormatter, that
would cause rerender cycles that screwed with hovering, because the
graph was being constantly re-created. You could say the caller is just
responsible for not doing that, but I was feeling generous.
@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
console Ready Ready Preview Jul 21, 2026 8:39pm

Request Review

@david-crespo

david-crespo commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Those are some good ticks. Does this also fix the missing x-axis ticks when there’s a big gap in the data?

Comment thread app/ui/styles/index.css
@import '@oxide/design-system/styles/light.css';
@import '@oxide/design-system/styles/preflight.css' layer(base);
@import 'simplebar-react/dist/simplebar.min.css' layer(components);
@import 'uplot/dist/uPlot.min.css' layer(components);

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.

This seems fine but worth checking it's not overriding something we don't want it to. Like:

.u-legend th {
	font-weight: 600;
}

We're not using title or legend here but we might elsewhere.

const v = (name: string) => style.getPropertyValue(name)
return {
fontFamily: v('--font-mono'),
stroke: v('--content-accent-tertiary'),

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.

Was already like this, but we should probably switch to the equivalent semantic stroke token --stroke-accent-secondary

@benjaminleonard

Copy link
Copy Markdown
Contributor

Currently not working in the vercel preview, maybe related error:

validate.ts:79 Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.

image

@benjaminleonard

Copy link
Copy Markdown
Contributor

Unfortunately wide gamut / display-p3 colour does not work. It looks like we can patch it (untested on regular displays) but we might want to try and upstream up a PR to uPlot if possible.

// uPlot's canvas defaults to sRGB, which clips our oklch theme colors, so charts look washed out.
// uPlot exposes no way to pass context attributes, so we patch the canvas default globally.
function defaultCanvasToDisplayP3() {
  try {
    const attrs = document
      .createElement('canvas')
      .getContext('2d', { colorSpace: 'display-p3' })
      ?.getContextAttributes?.()
    if (attrs?.colorSpace !== 'display-p3') return // browser doesn't support it
  } catch {
    return
  }
  const original = HTMLCanvasElement.prototype.getContext
  HTMLCanvasElement.prototype.getContext = function (
    this: HTMLCanvasElement,
    contextId: string,
    options?: object
  ) {
    return original.call(this, contextId, { ...options, colorSpace: 'display-p3' })
    // cast needed because a plain function can't satisfy getContext's overload set
  } as typeof original
}
defaultCanvasToDisplayP3()

@david-crespo

david-crespo commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

I don't think the CSP warning is related, it's there on main too. I think it's from this, which would only affect the preview because I don't think we use zod in production: colinhacks/zod#4461. Still probably worth fixing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants