Skip to content

feat(web): rebuild analytics charts on bklit-ui and fix avg/day#1093

Merged
AmanVarshney01 merged 3 commits into
mainfrom
aman/analytics-bklit-charts
Jul 1, 2026
Merged

feat(web): rebuild analytics charts on bklit-ui and fix avg/day#1093
AmanVarshney01 merged 3 commits into
mainfrom
aman/analytics-bklit-charts

Conversation

@AmanVarshney01

@AmanVarshney01 AmanVarshney01 commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes inaccurate analytics numbers and rebuilds every chart on the analytics page using bklit-ui, themed to match the site (Catppuccin, minimal terminal look, auto light/dark).

Analytics accuracy

  • avg/day bug fixed. The home stats card and the analytics page divided the all-time project total by a fixed 30-day window (50017 / 30 ≈ 1667). It now divides by the actual tracked calendar span (firstDate→lastDate ≈ 209 days) → ~239/day, and both pages now agree. Verified against live prod Convex data.
  • Removed the pre-Convex "legacy" archive callout (hardcoded total: 55434, avgPerDay: 326.1) from the analytics header, and threaded the prop removal through analytics-client → analytics-page → analytics-header.
  • Audited the rest of the page against live data — totals, tracked span, momentum, hourly/weekday, and distributions all compute correctly.

Charts → bklit-ui

  • Added the @bklit shadcn registry and installed the area + bar chart families (composable, built on @visx/* + motion). Vendored under apps/web/src/components/charts.
  • Migrated all ~24 chart instances (metrics, timeline, stack, dev-environment, preference cards) behind two thin wrappers in analytics/_components/bklit-charts.tsx: TrendAreaChart (date-x time series) and CategoryBarChart (categorical, with a configurable labelWidth gutter).
  • Themed via --chart-* CSS vars mapped onto site tokens (--primary, --border, --muted-foreground, --popover) so charts adapt to light/dark automatically. Also fixed a shadcn cssVars injection bug (var(----chart-*)var(--chart-*)).
  • Fixed horizontal-bar category labels that bklit truncated to 1–2 characters — they now get a proper left gutter and read fully (tanstack-router, postgres, drizzle, Frontend · next, …).
  • Removed evilcharts entirely (12 files + evil-chart-utils.ts + registry entry).

Notes / deps

  • Adds @visx/*@4.0.1-alpha.0 (curve, event, gradient, grid, pattern, responsive, scale, shape), d3-array, and @types/d3-array — bklit-ui is visx-based.
  • Two small install fixes: a doubled ../components/shimmering-text import path and missing @types/d3-array.

Verification

  • bun run check (oxfmt + oxlint) passes — 0 warnings, 0 errors.
  • tsc --noEmit clean for the web app (only a pre-existing bun:test test-file error remains).
  • Rendered locally and screenshot-verified every section in both light and dark modes; no console errors.

Summary by CodeRabbit

  • New Features
    • Upgraded analytics charts with smoother line/area/bar rendering, richer hover highlights, improved tooltips, and new loading skeletons/transitions.
    • Simplified analytics telemetry cards for a cleaner, more consistent header experience.
    • Introduced a shimmering text effect for highlighted UI copy.
  • Bug Fixes
    • Improved “Avg/Day” to calculate based on actual tracking duration (more accurate metrics).
  • Chores
    • Updated chart registry endpoint and expanded visualization-related dependencies.

Fix the home + analytics "avg/day" stat, which divided the all-time
total by a fixed 30-day window (~1667); it now divides by the actual
tracked span so both pages agree (~239/day). Remove the pre-Convex
"legacy" archive callout from the analytics header.

Migrate every analytics chart from evilcharts to bklit-ui (shadcn
@bklit registry, composable visx + motion) behind two thin wrappers
(TrendAreaChart, CategoryBarChart). Charts are themed through the
--chart-* CSS vars mapped onto site tokens (primary / border /
muted-foreground / popover) for a minimal terminal look that adapts
to light and dark automatically.

Horizontal bars get a configurable left gutter so category names stay
readable. Also fixes a vendored shimmering-text import path and adds
@types/d3-array. Removes the evilcharts library and evil-chart-utils.
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
create-better-t-stack-web Ready Ready Preview, Comment Jul 1, 2026 1:46pm

Request Review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5adca81252

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/src/components/charts/area-chart.tsx Outdated
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8ed74b79-90e9-4932-8422-78ec0d81e442

📥 Commits

Reviewing files that changed from the base of the PR and between c91c59e and 92d6793.

📒 Files selected for processing (1)
  • apps/web/src/components/charts/area-chart.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/components/charts/area-chart.tsx

Walkthrough

This PR replaces the legacy EvilCharts analytics stack with new bklit chart primitives, wrappers, and shared utilities. It also simplifies analytics stats/header wiring, updates chart theme/dependency setup, and removes the old EvilCharts modules.

Changes

Analytics Dashboard & Chart Migration

Layer / File(s) Summary
Dependencies and analytics stats
apps/web/components.json, apps/web/package.json, apps/web/src/app/global.css, apps/web/src/app/(home)/_components/stats-section.tsx, apps/web/src/app/(home)/analytics/_components/analytics-header.tsx, apps/web/src/app/(home)/analytics/_components/analytics-page.tsx, apps/web/src/app/(home)/analytics/analytics-client.tsx
Switches the registry and chart dependencies, adds chart theme variables, removes the legacy analytics prop flow, and recomputes Avg/Day from monthly date-span tracking.
Shared chart context and utilities
apps/web/src/components/charts/chart-context.tsx, chart-config-context.tsx, chart-phase.ts, chart-child-passthrough.ts, chart-defs.ts, chart-formatters.ts, chart-legend-hover.tsx, animation.ts, motion-utils.ts, fade-edges.ts, indicator-fade.ts, y-axis-scales.ts, y-axis-ticks.ts, y-domain-utils.ts, decimate-time-series.ts, filter-data-by-x-domain.ts, series-bar-layout.ts, bar-depth-geometry.ts, projection-utils.ts, projection-config.ts, reference-area-config.ts, reference-area-registration-context.tsx, generate-chart-skeleton-data.ts
Adds shared chart context, lifecycle, classification, formatting, animation, fade, scale, domain, projection, and skeleton-data utilities.
Loading, interaction, and phase hooks
apps/web/src/components/charts/use-chart-interaction.ts, use-scheduled-tooltip.ts, use-chart-phase-orchestrator.ts, use-enter-complete.ts, use-mount-progress.ts, use-highlight-segment.ts, highlight-segment-bounds.ts, highlight-segment.tsx, series-highlight-layer.tsx, loading-sweep.tsx, line-loading-pulse.tsx, line-loading-timing.ts, chart-loading-label.tsx, area-chart-loading.tsx, bar-chart-loading.tsx, apps/web/src/components/shimmering-text.tsx, use-grid-shimmer.ts, static-chart-preview-context.tsx
Adds loading visuals, hover/selection handling, tooltip scheduling, highlight segments, mount progress, and chart phase orchestration hooks.
Area, bar, grid, and tooltip components
apps/web/src/components/charts/area.tsx, area-chart.tsx, area-gradient-defs.tsx, pattern-area.tsx, dash-tail-stroke.tsx, path-stroke-utils.ts, series-dash-tail-overlay.tsx, series-hover-dim.tsx, series-markers.tsx, series-point-marker.tsx, bar.tsx, bar-chart.tsx, bar-x-axis.tsx, bar-y-axis.tsx, grid.tsx, x-axis.tsx, tooltip/*, time-series-chart-shell.tsx
Adds the chart renderers and supporting grid, axis, and tooltip components used across the new stack.
bklit wrappers and analytics dashboard rewiring
apps/web/src/app/(home)/analytics/_components/bklit-charts.tsx, dev-environment-charts.tsx, metrics-cards.tsx, preference-chart-card.tsx, timeline-charts.tsx, stack-configuration-charts.tsx
Adds TrendAreaChart and CategoryBarChart wrappers and rewires analytics dashboard charts to them, including chart-data and layout prop changes.
Legacy EvilCharts removal
apps/web/src/components/evilcharts/charts/*, apps/web/src/components/evilcharts/ui/*, apps/web/src/app/(home)/analytics/_components/evil-chart-utils.ts
Removes the old EvilCharts chart components, shared UI modules, and chart helper utilities replaced by the new bklit stack.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: rebuilding analytics charts on bklit-ui and fixing avg/day.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 13

♻️ Duplicate comments (1)
apps/web/src/components/charts/tooltip/date-ticker.tsx (1)

6-8: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

COMPACT_TICKER_THRESHOLD duplicated in chart-tooltip.tsx.

See companion comment in chart-tooltip.tsx (line 117) — dateLabels.length > 60 there hardcodes the same value independently of this constant. Exporting this constant and importing it in chart-tooltip.tsx would keep the two behaviors in sync.

🧹 Nitpick comments (18)
apps/web/src/app/(home)/analytics/_components/bklit-charts.tsx (1)

55-60: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate color-fallback logic between tooltip and render paths.

s.color ?? (s.line ? MUTED : ACCENT) is computed independently in rows (Line 57) and again in the series.map render (Line 74). Any future change to one branch risks silently desyncing tooltip colors from rendered series colors.

♻️ Proposed dedup
+  const colorFor = (s: ChartSeries) => s.color ?? (s.line ? MUTED : ACCENT);
+
   const rows = (point: Row): TooltipRow[] =>
     series.map((s) => ({
-      color: s.color ?? (s.line ? MUTED : ACCENT),
+      color: colorFor(s),
       label: s.label,
       value: valueFormat(num(point[s.key])),
     }));
 ...
         {series.map((s) => {
-          const color = s.color ?? (s.line ? MUTED : ACCENT);
+          const color = colorFor(s);

Also applies to: 73-87

apps/web/src/components/charts/x-axis.tsx (1)

618-625: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep label keys stable across position changes.

Line 624 includes item.x, so labels remount when the scale changes and the left transition on XAxisLabel cannot animate.

Proposed fix
-          key={`${item.date.getTime()}-${item.x}`}
+          key={`${item.date.getTime()}-${item.label}`}
apps/web/src/components/charts/tooltip/date-ticker.tsx (1)

23-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Hardcoded zinc palette bypasses the chart theming CSS variables.

TooltipBox, TooltipIndicator, and TooltipDot all theme via chartCssVars (mapped to site tokens per the PR description), but the ticker pill uses hardcoded bg-zinc-900/dark:bg-zinc-100/text-white classes. This is inconsistent with the rest of the tooltip subsystem's theming strategy and won't automatically pick up custom site tokens.

Also applies to: 96-96

apps/web/src/components/charts/tooltip/tooltip-box.tsx (1)

96-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Flip/position formula duplicated in render body and useLayoutEffect.

The same shouldFlipX/targetX/targetY computation is written twice (once against the stale ref defaults, once against freshly-measured values). Extracting a small helper reduces the risk of the two copies drifting apart on a future edit.

♻️ Suggested extraction
+function computeFlipPosition(x: number, y: number, w: number, h: number, containerWidth: number, containerHeight: number, offset: number) {
+  const flip = x + w + offset > containerWidth;
+  const targetX = flip ? x - offset - w : x + offset;
+  const targetY = Math.max(offset, Math.min(y - h / 2, containerHeight - h - offset));
+  return { flip, targetX, targetY };
+}

Also applies to: 126-130

apps/web/src/components/charts/tooltip/tooltip-indicator.tsx (1)

97-97: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Default gradientId isn't guaranteed unique across chart instances.

gradientId defaults to a fixed string and chart-tooltip.tsx never overrides it per chart. With multiple charts mounted on the analytics page, any scenario where two indicators are visible simultaneously (multi-touch, or future simultaneous-hover UX) will collide on the same SVG id, causing one gradient to silently override the other.

🛡️ Suggested fix: use React's `useId()` for a guaranteed-unique default
+import { useId } from "react";
...
-  gradientId = "tooltip-indicator-gradient",
+  gradientId,
...
+  const generatedId = useId();
+  const resolvedGradientId = gradientId ?? `tooltip-indicator-gradient-${generatedId}`;

(then use resolvedGradientId in place of gradientId for the <linearGradient id> / fill="url(#...)" references)

apps/web/src/components/shimmering-text.tsx (1)

39-61: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Minor: createCharVariants result isn't actually memoized per character.

useCallback only stabilizes the function identity; calling createCharVariants(index) inside the render loop still allocates a fresh Variants object per character on every render. For long strings, consider useMemo over the full text to build the array once. Given typical short shimmer labels, this is unlikely to matter in practice.

apps/web/src/components/charts/static-chart-preview-context.tsx (1)

8-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Provider hardcodes value={true} — no way to toggle off within nested subtree.

If a future consumer needs to conditionally re-enable the reveal clip-path inside a static-preview subtree, there's no prop escape hatch. Given the current single-purpose usage (always-on wrapper), this is acceptable, but consider accepting an optional enabled prop for flexibility.

apps/web/src/components/charts/area-chart.tsx (1)

74-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Component-detection heuristic is fragile for wrapped components.

extractAreaConfigs relies on child.type === Area / displayName checks that assume Area/PatternArea are plain function components. If either is ever wrapped in memo() or forwardRef(), typeof child.type === "function" becomes false and componentName resolution silently breaks, falling back only to the dataKey duck-typing check (which also mis-classifies any other child exposing a string dataKey prop as an Area).

apps/web/src/components/charts/bar-y-axis.tsx (1)

93-101: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Label decimation via fixed step can hide the last label / produce uneven spacing.

allLabels.filter((_, i) => i % step === 0) always keeps index 0 but may drop the final label if (length - 1) % step !== 0, which can look inconsistent for a bar-axis where the boundary categories are often most relevant to show.

apps/web/src/components/charts/chart-defs.ts (1)

51-61: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

collectChartDefsChildren doesn't flatten Fragments, unlike similar traversal helpers.

Other chart-child traversal utilities in this PR (forEachChartChild in chart-child-passthrough.ts, extractProjectionLineConfigs in projection-config.ts) explicitly recurse into Fragment children because "studio often groups layers in <>...</>". This function only does a flat Children.forEach, so pattern/gradient defs wrapped in a Fragment will be silently dropped, breaking their fills.

♻️ Proposed fix to flatten Fragments
 export function collectChartDefsChildren(children: ReactNode): ReactElement[] {
   const defNodes: ReactElement[] = [];

-  Children.forEach(children, (child) => {
-    if (isValidElement(child) && isChartDefsComponent(child)) {
-      defNodes.push(child);
-    }
-  });
+  const visit = (nodes: ReactNode) => {
+    Children.forEach(nodes, (child) => {
+      if (!isValidElement(child)) return;
+      if (child.type === Fragment) {
+        visit((child.props as { children?: ReactNode }).children);
+        return;
+      }
+      if (isChartDefsComponent(child)) {
+        defNodes.push(child);
+      }
+    });
+  };
+  visit(children);

   return defNodes;
 }
apps/web/src/components/charts/chart-child-passthrough.ts (1)

67-103: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Component-name resolution logic duplicated across files.

The typeof child.type === "function" ? childType.displayName || childType.name || "" : "" pattern is repeated here (3 times) and reimplemented again in chart-defs.ts (getChartChildComponentName) and projection-config.ts (getChildComponentName). Consider extracting a single shared helper (e.g. exporting getChartChildComponentName from a common module) that all three files import, so any future fix (e.g. memo/forwardRef support) only needs to land once.

apps/web/src/components/charts/chart-config-context.tsx (1)

50-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No unit tests for the interpolation branches.

resolveTooltipBoxMotion has non-trivial piecewise interpolation (three branches: damping === 0, < default, > default). This is a good candidate for small unit tests covering each branch and the clamp boundary, similar to how highlight-segment-bounds.ts is split out for testability.

apps/web/src/components/charts/chart-context.tsx (1)

236-350: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Manually duplicated field lists (object + deps, ×2) are fragile to drift.

Every field in ChartContextValue must be kept in sync across 4 places: the interface, the stable object literal, the stable deps array, and (for hover fields) the hover object/deps. Today they match, but adding a new field to ChartContextValue without updating all four spots silently produces a stale stable/hover slice (a value present in the type but missing from stable's object or deps will just be undefined for consumers, with no compile error). Consider a small helper (e.g. picking keys via a shared array, or a shallow-equal-based memo utility) to reduce the number of places that must be kept in sync.

apps/web/src/components/charts/use-chart-phase-orchestrator.ts (1)

19-174: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider splitting the forward/reverse transition branches into named helpers.

The main transition useEffect (lines 42-78) already carries a biome-ignore for cognitive complexity. Extracting the "ready"<-"loading" and "loading"<-"ready" branches into two small named functions (e.g. enterReady(...), enterLoading(...)) would make the state machine easier to follow without changing behavior.

apps/web/src/components/charts/use-highlight-segment.ts (1)

48-56: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Ref mutation + imperative motion-value updates during render is fragile under concurrent/Strict-Mode re-renders.

wasActive.current is both read and written directly in the render body, and used to decide whether to call .jump() vs .set(). Under React Strict Mode dev double-invocation (or any interrupted/replayed render), the ref write from a discarded render pass persists, so the second invocation can see wasActive.current already flipped and take the wrong branch — e.g. using .set() (eases in) instead of .jump() (snaps instantly) on the very first hover after mount. React's own guidance is to avoid mutating refs during render except for lazy state init; prefer deriving "previous value" via the sanctioned useState-based pattern (compare against state, call setState conditionally during render) instead of a raw ref mutation.

♻️ Suggested pattern
-  const wasActive = useRef(false);
-  if (bounds.isActive && !wasActive.current) {
-    xSpring.jump(bounds.x);
-    widthSpring.jump(bounds.width);
-  } else {
-    xSpring.set(bounds.x);
-    widthSpring.set(bounds.width);
-  }
-  wasActive.current = bounds.isActive;
+  const [prevActive, setPrevActive] = useState(bounds.isActive);
+  if (prevActive !== bounds.isActive) {
+    setPrevActive(bounds.isActive);
+  }
+  if (bounds.isActive && !prevActive) {
+    xSpring.jump(bounds.x);
+    widthSpring.jump(bounds.width);
+  } else {
+    xSpring.set(bounds.x);
+    widthSpring.set(bounds.width);
+  }
apps/web/src/components/charts/use-animated-y-domains.ts (1)

194-225: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Prefer domainsEqual over JSON.stringify signature for change detection.

JSON.stringify is sensitive to object key insertion order, so equal-value domain maps built with a different key order would be spuriously treated as "changed" (or vice versa). y-domain-utils.ts already exports domainsEqual() for order-independent comparison.

♻️ Proposed refactor
-  const targetSignature = JSON.stringify(targetByAxis);
-  const prevTargetSignatureRef = useRef(targetSignature);
+  const prevTargetRef = useRef(targetByAxis);

   useEffect(() => {
     const inLivePhase = chartPhase === "ready" || chartPhase === "revealing";

     if (!inLivePhase) {
-      prevTargetSignatureRef.current = targetSignature;
+      prevTargetRef.current = targetByAxis;
       return;
     }

-    if (prevTargetSignatureRef.current === targetSignature) {
+    if (domainsEqual(prevTargetRef.current, targetByAxis)) {
       return;
     }
-    prevTargetSignatureRef.current = targetSignature;
+    prevTargetRef.current = targetByAxis;
     ...
-  }, [chartPhase, durationMs, enabled, reducedMotion, targetSignature, tweenOnTargetChange]);
+  }, [chartPhase, durationMs, enabled, reducedMotion, targetByAxis, tweenOnTargetChange]);
apps/web/src/components/charts/use-chart-interaction.ts (2)

70-130: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated nearest-point resolution logic.

resolveTooltipFromX (lines 72-90) and resolveIndexFromX (lines 113-127) implement the identical bisect + nearest-neighbor-by-time comparison. Consider extracting a shared resolveNearestIndex(pixelX) helper that both build on, to avoid the two implementations drifting apart.

♻️ Sketch
const resolveNearestIndex = useCallback(
  (pixelX: number): number => {
    const x0 = xScale.invert(pixelX);
    const index = bisectDate(data, x0, 1);
    const d0 = data[index - 1];
    const d1 = data[index];
    if (!d0) return 0;
    if (d1) {
      const d0Time = xAccessor(d0).getTime();
      const d1Time = xAccessor(d1).getTime();
      if (x0.getTime() - d0Time > d1Time - x0.getTime()) return index;
    }
    return index - 1;
  },
  [xScale, data, xAccessor, bisectDate],
);

Both resolveTooltipFromX and resolveIndexFromX can then call this.


220-296: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Two-touch pinch-selection block duplicated between handleTouchStart and handleTouchMove.

Lines 233-251 and 276-292 are nearly identical (compute x0/x1, derive startX/endX, call resolveIndexFromX twice, setSelection). Worth extracting a small buildSelectionFromTouches(event) helper to keep the two handlers in sync going forward.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b2e5db0-e22e-40cb-a90f-ae79ab2765a1

📥 Commits

Reviewing files that changed from the base of the PR and between 10b6d55 and 5adca81.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (92)
  • apps/web/components.json
  • apps/web/package.json
  • apps/web/src/app/(home)/_components/stats-section.tsx
  • apps/web/src/app/(home)/analytics/_components/analytics-header.tsx
  • apps/web/src/app/(home)/analytics/_components/analytics-page.tsx
  • apps/web/src/app/(home)/analytics/_components/bklit-charts.tsx
  • apps/web/src/app/(home)/analytics/_components/dev-environment-charts.tsx
  • apps/web/src/app/(home)/analytics/_components/evil-chart-utils.ts
  • apps/web/src/app/(home)/analytics/_components/metrics-cards.tsx
  • apps/web/src/app/(home)/analytics/_components/preference-chart-card.tsx
  • apps/web/src/app/(home)/analytics/_components/timeline-charts.tsx
  • apps/web/src/app/(home)/analytics/analytics-client.tsx
  • apps/web/src/app/global.css
  • apps/web/src/components/charts/animation.ts
  • apps/web/src/components/charts/area-chart-loading.tsx
  • apps/web/src/components/charts/area-chart.tsx
  • apps/web/src/components/charts/area-gradient-defs.tsx
  • apps/web/src/components/charts/area.tsx
  • apps/web/src/components/charts/bar-chart-loading.tsx
  • apps/web/src/components/charts/bar-chart.tsx
  • apps/web/src/components/charts/bar-depth-geometry.ts
  • apps/web/src/components/charts/bar-x-axis.tsx
  • apps/web/src/components/charts/bar-y-axis.tsx
  • apps/web/src/components/charts/bar.tsx
  • apps/web/src/components/charts/chart-child-passthrough.ts
  • apps/web/src/components/charts/chart-config-context.tsx
  • apps/web/src/components/charts/chart-context.tsx
  • apps/web/src/components/charts/chart-defs.ts
  • apps/web/src/components/charts/chart-formatters.ts
  • apps/web/src/components/charts/chart-legend-hover.tsx
  • apps/web/src/components/charts/chart-loading-label.tsx
  • apps/web/src/components/charts/chart-phase.ts
  • apps/web/src/components/charts/chart-reveal-clip.tsx
  • apps/web/src/components/charts/dash-tail-stroke.tsx
  • apps/web/src/components/charts/decimate-time-series.ts
  • apps/web/src/components/charts/fade-edges.ts
  • apps/web/src/components/charts/filter-data-by-x-domain.ts
  • apps/web/src/components/charts/generate-chart-skeleton-data.ts
  • apps/web/src/components/charts/grid.tsx
  • apps/web/src/components/charts/highlight-segment-bounds.ts
  • apps/web/src/components/charts/highlight-segment.tsx
  • apps/web/src/components/charts/indicator-fade.ts
  • apps/web/src/components/charts/line-loading-pulse.tsx
  • apps/web/src/components/charts/line-loading-timing.ts
  • apps/web/src/components/charts/loading-sweep.tsx
  • apps/web/src/components/charts/motion-utils.ts
  • apps/web/src/components/charts/path-stroke-utils.ts
  • apps/web/src/components/charts/pattern-area.tsx
  • apps/web/src/components/charts/projection-config.ts
  • apps/web/src/components/charts/projection-utils.ts
  • apps/web/src/components/charts/reference-area-config.ts
  • apps/web/src/components/charts/reference-area-registration-context.tsx
  • apps/web/src/components/charts/series-bar-layout.ts
  • apps/web/src/components/charts/series-dash-tail-overlay.tsx
  • apps/web/src/components/charts/series-highlight-layer.tsx
  • apps/web/src/components/charts/series-hover-dim.tsx
  • apps/web/src/components/charts/series-markers.tsx
  • apps/web/src/components/charts/series-point-marker.tsx
  • apps/web/src/components/charts/static-chart-preview-context.tsx
  • apps/web/src/components/charts/time-series-chart-shell.tsx
  • apps/web/src/components/charts/tooltip/chart-tooltip.tsx
  • apps/web/src/components/charts/tooltip/date-ticker.tsx
  • apps/web/src/components/charts/tooltip/index.ts
  • apps/web/src/components/charts/tooltip/tooltip-box.tsx
  • apps/web/src/components/charts/tooltip/tooltip-content.tsx
  • apps/web/src/components/charts/tooltip/tooltip-dot.tsx
  • apps/web/src/components/charts/tooltip/tooltip-indicator.tsx
  • apps/web/src/components/charts/use-animated-y-domains.ts
  • apps/web/src/components/charts/use-chart-interaction.ts
  • apps/web/src/components/charts/use-chart-phase-orchestrator.ts
  • apps/web/src/components/charts/use-enter-complete.ts
  • apps/web/src/components/charts/use-grid-shimmer.ts
  • apps/web/src/components/charts/use-highlight-segment.ts
  • apps/web/src/components/charts/use-mount-progress.ts
  • apps/web/src/components/charts/use-scheduled-tooltip.ts
  • apps/web/src/components/charts/x-axis.tsx
  • apps/web/src/components/charts/y-axis-scales.ts
  • apps/web/src/components/charts/y-axis-ticks.ts
  • apps/web/src/components/charts/y-domain-utils.ts
  • apps/web/src/components/evilcharts/charts/area-chart.tsx
  • apps/web/src/components/evilcharts/charts/bar-chart.tsx
  • apps/web/src/components/evilcharts/charts/line-chart.tsx
  • apps/web/src/components/evilcharts/charts/pie-chart.tsx
  • apps/web/src/components/evilcharts/charts/radar-chart.tsx
  • apps/web/src/components/evilcharts/charts/radial-chart.tsx
  • apps/web/src/components/evilcharts/ui/background.tsx
  • apps/web/src/components/evilcharts/ui/chart.tsx
  • apps/web/src/components/evilcharts/ui/dot.tsx
  • apps/web/src/components/evilcharts/ui/evil-brush.tsx
  • apps/web/src/components/evilcharts/ui/legend.tsx
  • apps/web/src/components/evilcharts/ui/tooltip.tsx
  • apps/web/src/components/shimmering-text.tsx
💤 Files with no reviewable changes (14)
  • apps/web/src/components/evilcharts/ui/dot.tsx
  • apps/web/src/components/evilcharts/ui/legend.tsx
  • apps/web/src/components/evilcharts/ui/evil-brush.tsx
  • apps/web/src/components/evilcharts/ui/chart.tsx
  • apps/web/src/components/evilcharts/charts/line-chart.tsx
  • apps/web/src/components/evilcharts/ui/tooltip.tsx
  • apps/web/src/components/evilcharts/ui/background.tsx
  • apps/web/src/components/evilcharts/charts/radar-chart.tsx
  • apps/web/src/components/evilcharts/charts/radial-chart.tsx
  • apps/web/src/components/evilcharts/charts/area-chart.tsx
  • apps/web/src/app/(home)/analytics/_components/evil-chart-utils.ts
  • apps/web/src/components/evilcharts/charts/pie-chart.tsx
  • apps/web/src/components/evilcharts/charts/bar-chart.tsx
  • apps/web/src/app/(home)/analytics/_components/analytics-page.tsx

Comment thread apps/web/package.json
Comment thread apps/web/src/components/charts/animation.ts
Comment thread apps/web/src/components/charts/area-chart.tsx
Comment thread apps/web/src/components/charts/area.tsx
Comment thread apps/web/src/components/charts/bar-chart.tsx
Comment thread apps/web/src/components/charts/grid.tsx
Comment thread apps/web/src/components/charts/projection-utils.ts
Comment thread apps/web/src/components/charts/tooltip/chart-tooltip.tsx
Comment thread apps/web/src/components/charts/tooltip/chart-tooltip.tsx
Comment thread apps/web/src/components/charts/x-axis.tsx
- denser horizontal preference bars (30px/row instead of 38)
- drop leftover min-h wrappers that padded empty space below short charts
- lay CLI versions in a single 4-column row instead of a 2x2 wall
bklit's AreaChart hardcoded clipPathId="chart-area-grow-clip", so the
two area charts on the analytics page (metrics sparkline + daily
timeline) emitted duplicate document-scoped SVG ids and could resolve
each other's reveal clip. Derive a per-instance id from useId, matching
how the rest of the chart library already generates ids.
@AmanVarshney01 AmanVarshney01 merged commit a5201d9 into main Jul 1, 2026
3 checks passed
@AmanVarshney01 AmanVarshney01 deleted the aman/analytics-bklit-charts branch July 1, 2026 13:57
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.

1 participant