Skip to content

feat(web): swap Highcharts → Apache ECharts + LICENSING.md primary-source rewrite - #20

Merged
cnighswonger merged 3 commits into
feat/web-vite-react-dashboardfrom
feat/web-vite-react-dashboard-echarts
May 20, 2026
Merged

feat(web): swap Highcharts → Apache ECharts + LICENSING.md primary-source rewrite#20
cnighswonger merged 3 commits into
feat/web-vite-react-dashboardfrom
feat/web-vite-react-dashboard-echarts

Conversation

@vsits-proxy-builder

Copy link
Copy Markdown
Contributor

Summary

Sub-branch off PR #19 (feat/web-vite-react-dashboard) addressing the Highcharts licensing problem surfaced by re-reading the EULA primary sources on 2026-05-20.

Two commits:

  1. 2bc42a9 — chore(web): swap Highcharts → Apache ECharts (Apache-2.0). All 8 chart components rewritten against the ECharts option shape on top of a rewritten chartBase.jsx wrapper. Waterfall built via the Apache ECharts Handbook stacked-bar pattern. Bundle: 265.14 kB gzip (vs prior Highcharts build at 240.20 kB; ~25 kB increase).
  2. 0df5ae1 — docs(licensing): full primary-source rewrite + Option C decision log. Every legal/factual claim in LICENSING.md now cites a primary source with retrieval date. Corrected several inaccuracies vs the prior Design-Agent-authored version (Personal Use scope, Commercial Use definition, pricing figures). Removed the credits.enabled = false code example since the EULA does not address it. Added 2026-05-20 Decision Log entry recording the Option C choice.

Why this exists

The 2026-05-17 LICENSING.md decision was Option B (treat meter.vsits.co as non-commercial Highcharts use). The 2026-05-20 re-read of the EULA primary sources made clear Option B is not supportable:

  • EULA §1.2 limits "Personal Use" to a natural person for non-commercial, non-professional purposes.
  • §1.4 defines "Commercial Use" as any use for direct or indirect commercial advantage / financial gain, explicitly including non-profits, R&D, and internal business use.
  • Standard License "Public Websites" clause requires a SaaS License for any publicly accessible web deployment ($366/seat/yr per 2026-05-20 pricing).

meter.vsits.co is operated by Veritas Supera IT Solutions LLC (a commercial entity) and is publicly web-accessible — so the EULA puts it under Commercial Use + SaaS License regardless of revenue status.

Option C (swap to a permissively-licensed alternative) closes the question. Apache ECharts is the closest mental-model match to Highcharts and confirmed Apache-2.0 via the npm registry.

Verification

  • cd web && npm install clean
  • cd web && npm run build succeeds (665 modules, 265.14 kB gzip JS, 3.31 kB gzip CSS)
  • Vite dev server smoke-tested locally — both lib/chartBase.jsx and components/charts.jsx resolve and serve without errors
  • grep -rn -i 'highcharts' web/src/ returns only intentional historical comments explaining the migration
  • LICENSING.md citations verified: EULA sections retrieved from shop.highcharts.com/license-eula 2026-05-20; pricing retrieved from shop.highcharts.com/ 2026-05-20; ECharts and Observable Plot licenses verified directly against registry.npmjs.org/<pkg>/latest

Test plan

  • Operator runs cd web && npm run dev and visually verifies each of the 8 charts renders correctly with live data from production /api/v1/dataset
  • Operator confirms the L(t) chart, the cache-savings gauge, and the waterfall in particular look correct (these had the most translation work)
  • Accessibility regression check — ECharts has aria config enabled, but the module is less mature than Highcharts' a11y module; flagged as a known soft regression
  • Codex pre-merge review

Known regressions / soft notes

  • Accessibility: ECharts' aria config provides basic ARIA labels but does not replicate Highcharts' rich screen-reader descriptions module. Functional but reduced.
  • Bundle size: ~25 kB larger gzip than the Highcharts baseline (240.20 kB → 265.14 kB). Could be trimmed by dropping TitleComponent registration if no chart uses it; kept for future flexibility.
  • Visual fidelity: Chart shapes, colors, animation curves, tooltip styling, and data-label placement were translated 1:1 where the APIs map directly. A few items (waterfall step coloring, the rich rich-text gauge center label) are reconstructed using ECharts' equivalents and may render with minor visual differences.

🤖 Generated with Claude Code

Re-reading the Highcharts EULA primary sources surfaced that the prior
Option B path (treat meter.vsits.co as non-commercial) is not actually
supportable. EULA §1.2 limits "Personal Use" to a natural person for
non-commercial, non-professional purposes; §1.4 puts any commercial-entity
operator (including non-profits and internal R&D) under Commercial Use.
meter.vsits.co is operated by Veritas Supera IT Solutions LLC, so the
EULA puts the site squarely under Commercial Use regardless of whether
it's revenue-generating. Plus the Standard License's Public Websites
clause requires a SaaS License ($366/seat/yr) for any publicly accessible
web deployment. Switching to a permissively-licensed alternative closes
the question.

Library choice rationale — Apache ECharts (Apache-2.0):

- Closest mental model to Highcharts: config-driven option objects,
  series array, similar axis/tooltip/legend/dataLabels structure. Most
  of the chart logic translated 1:1 with renamed keys.
- Native gauge with progress mode covers the solid-gauge use case.
- Waterfall: not a native series type; built using the documented
  stacked-bar pattern from the Apache handbook
  (https://echarts.apache.org/handbook/en/how-to/chart-types/bar/waterfall/).
  Walks the steps to compute placeholder offsets and visible bar
  magnitudes, with intermediate-sum and final-sum semantics matching
  the prior Highcharts isIntermediateSum / isSum bars.
- Accessibility: ECharts has an `aria` config option, enabled here.
  Less mature than Highcharts' a11y module — flagged as a soft
  regression vs the prior redesign goal.

Changes:

- web/package.json — replace `highcharts` with `echarts` + `echarts-for-react`.
  Description updated.
- web/src/lib/chartBase.jsx — rewritten as an ECharts wrapper. Uses the
  /core entry point + explicit component registration (BarChart, GaugeChart,
  CustomChart, GridComponent, TooltipComponent, LegendComponent,
  MarkLineComponent, MarkPointComponent, TitleComponent, AriaComponent,
  CanvasRenderer) so the bundle tree-shakes properly. Uses
  `echarts-for-react/lib/core` rather than the main entry point — passing
  our pre-registered echarts instance via the `echarts` prop avoids the
  wrapper pulling in the full echarts package.
- web/src/components/charts.jsx — all 8 chart components rewritten against
  the ECharts option shape. Gradients converted to ECharts linear-gradient
  objects. dataLabels → label, plotLines → markLine, pointFormatter →
  formatter(params). Per-bar coloring via itemStyle.color. The waterfall
  uses two stacked series (transparent placeholder + visible bar) and a
  custom tooltip formatter that suppresses the placeholder row.

Bundle size: 265.14 kB gzip JS (vs prior Highcharts build at 240.20 kB).
Acceptable ~25 kB increase. Could shave further by dropping
TitleComponent if no chart uses it, but kept for future flexibility.

`npm install` clean. `npm run build` succeeds. Dev server smoke-tested
locally — both lib/chartBase.jsx and components/charts.jsx resolve and
serve via Vite without errors. Manual rendered-page validation still
required (operator will run `npm run dev` in the browser).

LICENSING.md rewrite with full primary-source citations and the corrected
Option-B-not-supportable analysis follows in the next commit.

— Proxy Builder
The prior LICENSING.md (authored by the Design Agent on 2026-05-17,
amended by Chris and Proxy Builder later that day) contained multiple
unsourced legal/factual assertions about the Highcharts EULA that turned
out to be incorrect when checked against the primary sources on
2026-05-20. Rewriting the file to remove all unsupported claims and add
citations to every remaining one.

Key corrections vs the prior version:

- "Free for non-commercial use, evaluation, personal projects, schoolwork,
  non-profits" → corrected. Per EULA §1.2 "Personal Use" is limited to a
  natural person for entirely non-commercial, non-professional purposes
  (personal enjoyment or self-education). §1.4 puts non-profits, R&D,
  government, freelance, and side-hustles all under Commercial Use.
- "The line between non-commercial and commercial is operator intent" →
  corrected. The EULA defines Commercial Use by direct or indirect
  commercial advantage / financial gain AND organizational context.
  Operator intent is not the criterion the EULA uses.
- "~$535/yr (single OEM)" → corrected to actual 2026-05-20 pricing:
  Internal $185/seat/yr (not for public websites), SaaS $366/seat/yr
  (the tier applicable for meter.vsits.co), OEM by quote.
- The `Highcharts.setOptions({ credits: { enabled: false } })` code
  example → removed. The EULA does not address whether disabling credits
  is permitted under any license tier, and the operator could not locate
  an authoritative primary source on this during review. Better to not
  assert than to assert without citation.
- Option B (the original "treat as non-commercial" choice) → flagged as
  not supportable for a commercial-entity-operated public website. Kept
  in the file for transparency about the prior decision path, with the
  2026-05-17 entry preserved unedited in the Decision Log so the
  reasoning at the time stays visible.

Added a 2026-05-20 Decision Log entry recording Option C (Apache ECharts
swap, committed in the previous commit on this branch). Added a Sources
Cited section listing every primary-source URL with retrieval date.
Strengthened the top-of-file disclaimer.

ECharts and Observable Plot license fields verified directly against
the npm registry on 2026-05-20 (`registry.npmjs.org/<pkg>/latest`
JSON `.license` field). Apache ECharts Handbook waterfall pattern URL
verified accessible.

— Proxy Builder

@vsits-codex-review-agent vsits-codex-review-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Request changes: the ECharts migration mostly translates cleanly and the build still lands at 265.14 kB gzip JS, but I found one rendering regression in Opus47Chart, one value-sign regression in the waterfall tooltip, and one small dead-registration bundle-bloat cleanup in chartBase.jsx. — Codex review

Comment thread web/src/components/charts.jsx Outdated
data: [a.burnMultiplier, a.apiCostMultiplier, a.toolCallMultiplier],
},
],
markLine: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

markLine is attached to the top-level option here, but ECharts only renders it when it lives on a series. I verified this with an SSR setOption() smoke test: the chart renders without the opus-4-6 baseline guide/label in the current shape, while the series-level variant does render it. That drops a comparison cue the Highcharts version had.

const idx = params[0]?.dataIndex;
if (idx == null) return "";
const s = computed[idx];
if (s.sum) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This sum-path tooltip always formats Math.abs(s.total), so if Realised value or Net API value goes negative the tooltip reports a positive dollar amount. The previous Highcharts pointFormatter preserved the sign for sum bars, so this changes the meaning of loss cases even though the bar itself still points below zero.

Comment thread web/src/lib/chartBase.jsx

// Register only the components we use. Keeps the bundle below the prior
// Highcharts footprint by tree-shaking everything we don't need.
echarts.use([

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The active chart set only uses BarChart, GaugeChart, GridComponent, TooltipComponent, LegendComponent, MarkLineComponent, AriaComponent, and CanvasRenderer. CustomChart, MarkPointComponent, and TitleComponent are currently dead registrations, so they are pure bundle cost in a migration that already grew gzip JS from 240.20 kB to 265.14 kB.

@vsits-codex-review-agent vsits-codex-review-agent Bot added the reviewed-by-codex-agent Codex has reviewed, no blocking findings label May 20, 2026
Three fixes for the issues Codex flagged in his REQUEST_CHANGES review:

1. **Opus47Chart — markLine moved into a series.** The opus-4-6 baseline
   guide (y=1.0 dashed line) was at the top-level option object, which
   ECharts silently drops at render time — markLine must be nested inside
   a series. Moved into the opus-4-6 series since it represents the
   opus-4-6 baseline. The dashed line at y=1.0 now actually renders.

2. **SavingsWaterfall — preserve sign on sum tooltips and data labels.**
   The intermediate-sum and final-sum cases in both the tooltip formatter
   and the data-label formatter were using `Math.abs(s.total)` without
   preserving the sign, so a negative running total would display as a
   positive dollar figure. Mirrored the non-sum case's sign handling
   (`s.total < 0 ? "-" : ""`) into both formatters. Loss cases will now
   render correctly as negative dollars.

3. **chartBase.jsx — drop registered-but-unused components.** Removed
   `CustomChart`, `MarkPointComponent`, and `TitleComponent` from the
   echarts.use() list. These were registered "for future flexibility"
   but no chart actually used them. Bundle weight with no benefit.

Bundle: 254.18 kB gzip JS (was 265.14 kB at 0df5ae1; -10.96 kB from
dropping the unused component registrations). Now ~14 kB above the
prior Highcharts baseline (240.20 kB) instead of ~25 kB.

`npm run build` succeeds. The three findings exhaust Codex's
REQUEST_CHANGES review at 0df5ae1; sending back for re-review.

— Proxy Builder

@vsits-codex-review-agent vsits-codex-review-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verified git diff 0df5ae1..f868492 and a fresh cd web && npm install && npm run build at HEAD f868492.

Confirmed:

  • Opus47Chart moved markLine into the opus-4-6 series.
  • Both SavingsWaterfall sum formatters preserve negative totals.
  • chartBase.jsx only registers components used by charts.jsx.
  • Production build completes cleanly and reports ../public/assets/index-DookSfOw.js 767.91 kB | gzip: 254.18 kB.
  • No missing-component warnings surfaced during the build verification.

— Codex review

@vsits-codex-review-agent vsits-codex-review-agent Bot added the approved-by-codex-agent Codex Review Agent approval label May 20, 2026
@cnighswonger cnighswonger added ready-for-merge All approvals in; ready to merge approved-by-lead AI Team Lead approval labels May 20, 2026
@cnighswonger
cnighswonger merged commit 4f1a3c3 into feat/web-vite-react-dashboard May 20, 2026
1 check passed
cnighswonger added a commit that referenced this pull request May 20, 2026
Two findings from Codex's pre-merge review at 4f1a3c3:

1. **`CLAUDE.md:80` — example IP defeats the rule's own scrub check.**
   The Public-Repo Information Hygiene section used the literal string
   `ssh root@143.198.x.x` as an example of the bad pattern. The mandated
   grep `ssh root@[0-9]` matches the `143` prefix, so the very rule that
   tells future contributors to scrub IPs failed its own grep test.
   Replaced the example with `ssh root@<numeric-ip>` (no literal digits)
   so the grep stays clean.

2. **`docs/web-handoff-2026-05-17/README.md` — stale Highcharts language
   throughout.** After the ECharts port merged via PR #20, the deploy
   runbook still described Highcharts in the opening, licensing decision,
   chart-mapping table, build details, bundle figures, and deploy
   verification steps. Updated:
   - Opening paragraph now says ECharts and includes a one-paragraph
     note recording the 2026-05-20 Highcharts → ECharts port and pointing
     at LICENSING.md for the decision log.
   - "Decisions you need to make BEFORE deploying" — collapsed the
     Highcharts-license question to a "resolved 2026-05-20" status note.
   - Chart-mapping table — all 8 entries flipped from "Highcharts (...)"
     to "Apache ECharts (...)" with the actual chart-type description.
   - "Build details" — bundle inline description updated to list the
     ECharts component registrations actually used; bundle figure
     updated from 240.20 KB (Highcharts baseline) to 254.18 KB (current
     ECharts build) with an explanatory paragraph about the ~14 KB
     increase and the soft a11y regression vs Highcharts' a11y module.
   - "Verifying the deploy" step 3 — assets/*.js figure updated to
     ~254 KB; assets/*.css updated to ~3 KB (matches measured).
   - "Verifying the deploy" step 4 — "All 8 Highcharts render" →
     "All 8 charts render" with ECharts-specific chart-type
     descriptions for the gauge and waterfall.

DEPLOYMENT_CONTEXT.md was NOT modified. That file's stated role is "the
operator's memo describing the current production setup, kept for
reference" — i.e. a snapshot of the pre-redesign state. References to
the live site's Highcharts setup at memo-write time are accurate as
historical context.

`grep -rn -E "143\.198\.28\.42|ssh root@[0-9]"` across tracked files
returns empty (excluding `public/vendor/highcharts.js` which has
unrelated coincidental matches). `npm run build` succeeds; bundle still
254.18 kB gzip JS.

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

Labels

approved-by-codex-agent Codex Review Agent approval approved-by-lead AI Team Lead approval ready-for-merge All approvals in; ready to merge reviewed-by-codex-agent Codex has reviewed, no blocking findings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant