Skip to content

refactor: Componentize docs indicator chart rendering#1981

Merged
DaveSkender merged 12 commits intov3from
codex/refactor-charts-component-for-docs-website
Feb 28, 2026
Merged

refactor: Componentize docs indicator chart rendering#1981
DaveSkender merged 12 commits intov3from
codex/refactor-charts-component-for-docs-website

Conversation

@DaveSkender
Copy link
Owner

Motivation

  • Reduce duplicated chart embedding markup in indicator pages and centralize chart logic for maintainability.
  • Enable optional live chart data, resilient local fallback, and client-side caching for docs chart rendering.

Description

  • Add shared chart contracts and helpers in docs/.vitepress/lib: chart-types.ts, chart-time.ts, and chart-data-provider.ts with support for VITE_CHART_DATA_BASE_URL, local /data/<indicator>.json fallback, in-memory + localStorage caching and TTL via VITE_CHART_CACHE_TTL_MS.
  • Update IndicatorChart.vue to consume getChartData(...) and createTimeContext(...), accept an optional indicatorKey prop, and watch both src and indicatorKey for reloads, removing inline type/logic duplication.
  • Add reusable docs web part IndicatorChartPanel.vue and register it in the VitePress theme (docs/.vitepress/theme/index.ts) to standardize embeds.
  • Replace per-page ClientOnly + IndicatorChart blocks across docs/indicators/*.md with <IndicatorChartPanel indicator-key="..." /> and update the migration script docs/scripts/update-indicator-charts.mjs to emit the new tag.
  • Extend markdownlint allow-list in .markdownlint-cli2.jsonc to include the new custom component tag names.

Testing

  • Ran pnpm install in docs/ successfully to restore dependencies.
  • Built the docs with pnpm run docs:build successfully, and ran vitepress preview to serve the site; a Playwright script visited /indicators/Rsi and captured a screenshot confirming the panel renders.
  • Ran markdown linting; initial lint run flagged MD033 (inline HTML) for the new component usages, the .markdownlint-cli2.jsonc allow-list was updated and committed, but depending on the exact CLI invocation some lint runs still reported MD033 (investigation: CLI invocation scope/args may need alignment).

Codex Task

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new IndicatorChartPanel Vue component and registers it globally; updates IndicatorChart to accept an optional indicatorKey and to load data via a new chart-data-provider with multi-layer caching; introduces chart-time utilities (parseTimestamp, createTimeContext) and chart-type declarations; replaces many docs pages’ ClientOnly+IndicatorChart blocks with IndicatorChartPanel; and adjusts Markdown lint configuration to permit the new custom element(s).

Possibly related PRs

  • PR #1908: Modifies docs/.vitepress/components/IndicatorChart.vue (props, marker/series handling) and overlaps the component refactor in this change.
  • PR #1944: Implements/changes time-resolution logic (time context and non-linear resolution) now factored into the new chart-time.ts utility.
  • PR #1885: Makes overlapping edits to IndicatorChart.vue (data loading/time resolution and indicator-related rendering), indicating direct code-level overlap.

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/indicators/StochRsi.md (1)

136-136: ⚠️ Potential issue | 🟡 Minor

Fix typo in comment.

The comment contains a typo: "huba" should be "hub" and the text appears duplicated.

📝 Proposed fix
-// does not create 2nd internal huba separate internal RSI hub
+// does not create a 2nd internal RSI hub
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/indicators/StochRsi.md` at line 136, Fix the duplicated/typo'd inline
comment next to the ToStochRsiHub() call: replace the existing comment with a
single, clear phrase such as "// does not create a 2nd internal hub" (ensure
"hub" is spelled correctly and any duplicated text is removed) in the line
containing ToStochRsiHub().
🧹 Nitpick comments (1)
docs/scripts/update-indicator-charts.mjs (1)

48-48: Remove redundant skip condition branch for clarity.

content.includes('IndicatorChartPanel') already implies content.includes('IndicatorChart'); simplifying this condition improves readability without behavior change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/scripts/update-indicator-charts.mjs` at line 48, The conditional
currently checks both content.includes('IndicatorChart') and
content.includes('IndicatorChartPanel'); remove the redundant second branch and
simplify the if to only check content.includes('IndicatorChart') so the
condition remains correct and more readable. Update the check where the symbol
content.includes('IndicatorChartPanel') appears (inside the if that also checks
content.includes('IndicatorChart')) to remove that redundant clause and leave
only content.includes('IndicatorChart').
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/lib/chart-data-provider.ts`:
- Around line 45-64: The readStorageCache function currently accesses
window.localStorage.getItem and removeItem directly and can throw in
restrictive/privacy browser modes; wrap all localStorage access in a try/catch
so any exceptions (e.g., SecurityError) result in removing/ignoring the cache
and returning null instead of bubbling the error; specifically, surround calls
to window.localStorage.getItem(cacheKey) and
window.localStorage.removeItem(cacheKey) plus the JSON.parse/Date.now validation
with a try/catch, ensure JSON.parse failures and storage access errors are
caught and lead to returning null (and attempting removeItem inside the try only
if safe), and keep the existing early return when typeof window === 'undefined'
in the readStorageCache function.
- Around line 80-93: The sliceData function mutates the passed ChartData (fields
candles and series), causing cached objects to be permanently shortened; change
sliceData to avoid in-place mutation by returning a new ChartData object: create
a new candles array using data.candles.slice(-maxBars) only when needed and map
series to new objects with data: s.data.slice(-maxBars) (preserve other series
properties), and copy any other top-level properties from the original data so
the cache remains unchanged and callers get a sliced copy instead of the
original.

In `@docs/.vitepress/lib/chart-time.ts`:
- Around line 54-79: The current resolveSeriesTime shares the timeIndexOffsets
Map across all mapping passes causing cross-series leakage; fix by turning the
resolver into a factory that creates a fresh offsets map per mapping pass:
implement a createResolveSeriesTime() that captures a new timeIndexOffsets Map
and returns the resolveSeriesTime(timestamp, index) closure (using existing
timeIndexLookup and indices logic), then replace the exported resolveSeriesTime
usage so callers invoke createResolveSeriesTime() at the start of each mapping
pass to get an isolated resolver; reference symbols: timeIndexOffsets,
resolveSeriesTime, createResolveSeriesTime, timeIndexLookup.

In `@docs/indicators/Slope.md`:
- Around line 11-12: Remove the duplicate IndicatorChartPanel: delete the second
occurrence of <IndicatorChartPanel indicator-key="Slope" /> so only one
IndicatorChartPanel with indicator-key="Slope" remains; locate the duplicate by
searching for the IndicatorChartPanel component with indicator-key="Slope" and
remove the extra instance to match other indicator pages and the single
Slope.json data file.

In `@docs/scripts/update-indicator-charts.mjs`:
- Line 62: The current replacement uses a single indicatorName for every image
(`replacement` variable) which causes distinct chart variants to be overwritten;
update the generator to compute a unique per-image key (e.g., derive an
identifier from the source image basename or append a per-match index) and use
that when constructing the `<IndicatorChartPanel indicator-key="...">` string
(replace the single-use `indicatorName` in `replacement` with a composed key
like `${indicatorName}-${imageBasename}` or `${indicatorName}-${index}` so each
replacement call produces a unique indicator-key).

---

Outside diff comments:
In `@docs/indicators/StochRsi.md`:
- Line 136: Fix the duplicated/typo'd inline comment next to the ToStochRsiHub()
call: replace the existing comment with a single, clear phrase such as "// does
not create a 2nd internal hub" (ensure "hub" is spelled correctly and any
duplicated text is removed) in the line containing ToStochRsiHub().

---

Nitpick comments:
In `@docs/scripts/update-indicator-charts.mjs`:
- Line 48: The conditional currently checks both
content.includes('IndicatorChart') and content.includes('IndicatorChartPanel');
remove the redundant second branch and simplify the if to only check
content.includes('IndicatorChart') so the condition remains correct and more
readable. Update the check where the symbol
content.includes('IndicatorChartPanel') appears (inside the if that also checks
content.includes('IndicatorChart')) to remove that redundant clause and leave
only content.includes('IndicatorChart').

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e32d09 and 9445df7.

📒 Files selected for processing (85)
  • .markdownlint-cli2.jsonc
  • docs/.vitepress/components/IndicatorChart.vue
  • docs/.vitepress/components/IndicatorChartPanel.vue
  • docs/.vitepress/lib/chart-data-provider.ts
  • docs/.vitepress/lib/chart-time.ts
  • docs/.vitepress/lib/chart-types.ts
  • docs/.vitepress/theme/index.ts
  • docs/indicators/Adl.md
  • docs/indicators/Adx.md
  • docs/indicators/Alligator.md
  • docs/indicators/Alma.md
  • docs/indicators/Aroon.md
  • docs/indicators/Atr.md
  • docs/indicators/AtrStop.md
  • docs/indicators/Awesome.md
  • docs/indicators/Beta.md
  • docs/indicators/BollingerBands.md
  • docs/indicators/Bop.md
  • docs/indicators/Cci.md
  • docs/indicators/ChaikinOsc.md
  • docs/indicators/Chandelier.md
  • docs/indicators/Chop.md
  • docs/indicators/Cmf.md
  • docs/indicators/Cmo.md
  • docs/indicators/ConnorsRsi.md
  • docs/indicators/Correlation.md
  • docs/indicators/Dema.md
  • docs/indicators/Donchian.md
  • docs/indicators/Dpo.md
  • docs/indicators/Dynamic.md
  • docs/indicators/ElderRay.md
  • docs/indicators/Ema.md
  • docs/indicators/Epma.md
  • docs/indicators/Fcb.md
  • docs/indicators/FisherTransform.md
  • docs/indicators/ForceIndex.md
  • docs/indicators/Fractal.md
  • docs/indicators/HeikinAshi.md
  • docs/indicators/Hma.md
  • docs/indicators/HtTrendline.md
  • docs/indicators/Hurst.md
  • docs/indicators/Ichimoku.md
  • docs/indicators/Kama.md
  • docs/indicators/Keltner.md
  • docs/indicators/Kvo.md
  • docs/indicators/MaEnvelopes.md
  • docs/indicators/Macd.md
  • docs/indicators/Mama.md
  • docs/indicators/Mfi.md
  • docs/indicators/Obv.md
  • docs/indicators/ParabolicSar.md
  • docs/indicators/Pivots.md
  • docs/indicators/Pmo.md
  • docs/indicators/Prs.md
  • docs/indicators/Pvo.md
  • docs/indicators/Renko.md
  • docs/indicators/Roc.md
  • docs/indicators/RocWb.md
  • docs/indicators/RollingPivots.md
  • docs/indicators/Rsi.md
  • docs/indicators/Slope.md
  • docs/indicators/Sma.md
  • docs/indicators/Smi.md
  • docs/indicators/Smma.md
  • docs/indicators/StarcBands.md
  • docs/indicators/Stc.md
  • docs/indicators/StdDev.md
  • docs/indicators/StdDevChannels.md
  • docs/indicators/Stoch.md
  • docs/indicators/StochRsi.md
  • docs/indicators/SuperTrend.md
  • docs/indicators/T3.md
  • docs/indicators/Tema.md
  • docs/indicators/Trix.md
  • docs/indicators/Tsi.md
  • docs/indicators/UlcerIndex.md
  • docs/indicators/Ultimate.md
  • docs/indicators/VolatilityStop.md
  • docs/indicators/Vortex.md
  • docs/indicators/Vwap.md
  • docs/indicators/Vwma.md
  • docs/indicators/WilliamsR.md
  • docs/indicators/Wma.md
  • docs/indicators/ZigZag.md
  • docs/scripts/update-indicator-charts.mjs

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Signed-off-by: Dave Skender <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
docs/.vitepress/lib/chart-data-provider.ts (1)

45-64: ⚠️ Potential issue | 🟠 Major

Guard localStorage access in readStorageCache to avoid runtime aborts in restricted browser modes.

At Line [50], getItem can throw; Line [58] and Line [63] removeItem calls can also throw. That can break loading before fallback logic runs.

Suggested hardening
 function readStorageCache(cacheKey: string): CachePayload | null {
   if (typeof window === 'undefined') {
     return null
   }

-  const raw = window.localStorage.getItem(cacheKey)
+  let raw: string | null = null
+  try {
+    raw = window.localStorage.getItem(cacheKey)
+  } catch {
+    return null
+  }
   if (!raw) {
     return null
   }

   try {
     const parsed = JSON.parse(raw) as CachePayload
     if (!parsed.expiresAt || !parsed.data || Date.now() > parsed.expiresAt) {
-      window.localStorage.removeItem(cacheKey)
+      try { window.localStorage.removeItem(cacheKey) } catch {}
       return null
     }
     return parsed
   } catch {
-    window.localStorage.removeItem(cacheKey)
+    try { window.localStorage.removeItem(cacheKey) } catch {}
     return null
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/lib/chart-data-provider.ts` around lines 45 - 64, The
readStorageCache function currently assumes
window.localStorage.getItem/removeItem won't throw; wrap all localStorage access
inside a safe feature-detection try/catch (or a small helper e.g.,
isLocalStorageAvailable) so failures in getItem or removeItem return null
instead of aborting; specifically, inside readStorageCache guard with typeof
window !== 'undefined' then attempt getItem in a try/catch, handle JSON.parse
errors as already done, and wrap any subsequent removeItem calls in try/catch
(or short-circuit them) so thrown DOMExceptions are swallowed and the function
returns null allowing fallback logic to proceed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/lib/chart-data-provider.ts`:
- Around line 100-102: The getChartData function currently reads options.maxBars
directly, which allows 0, negative, non-integer values to break slice()
behavior; normalize options.maxBars before using it: compute a normalizedMaxBars
by checking Number.isFinite(options.maxBars) and options.maxBars > 0, then set
normalizedMaxBars = Math.floor(options.maxBars) when valid, otherwise fall back
to the default 100; replace uses of maxBars (or the prior variable) with
normalizedMaxBars when slicing/truncating the dataset in getChartData to ensure
correct, positive integer behavior.

---

Duplicate comments:
In `@docs/.vitepress/lib/chart-data-provider.ts`:
- Around line 45-64: The readStorageCache function currently assumes
window.localStorage.getItem/removeItem won't throw; wrap all localStorage access
inside a safe feature-detection try/catch (or a small helper e.g.,
isLocalStorageAvailable) so failures in getItem or removeItem return null
instead of aborting; specifically, inside readStorageCache guard with typeof
window !== 'undefined' then attempt getItem in a try/catch, handle JSON.parse
errors as already done, and wrap any subsequent removeItem calls in try/catch
(or short-circuit them) so thrown DOMExceptions are swallowed and the function
returns null allowing fallback logic to proceed.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9445df7 and 2dd2a93.

📒 Files selected for processing (1)
  • docs/.vitepress/lib/chart-data-provider.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/.vitepress/components/IndicatorChart.vue (1)

454-590: ⚠️ Potential issue | 🟠 Major

Prevent stale async initChart() runs from overwriting newer renders.

With reloads now triggered by both src and indicatorKey, overlapping async inits can race; a slower earlier run can apply stale chart/state after a newer reload starts.

🔧 Suggested guard (generation token) to ignore stale runs
 let resizeObserver: ResizeObserver | null = null
 let resizeTimeout: ReturnType<typeof setTimeout> | null = null
+let initGeneration = 0

 async function initChart() {
+  const generation = ++initGeneration
   isLoading.value = true
   hasError.value = false

   const data = await loadChartData()
+  if (generation !== initGeneration) return
   if (!data) {
     isLoading.value = false
     return
   }

   // ...
   await new Promise(resolve => requestAnimationFrame(resolve))
   await new Promise(resolve => requestAnimationFrame(resolve))
+  if (generation !== initGeneration) return

   // continue chart creation only if still current generation
 }

 function destroyChart() {
+  initGeneration++
   if (resizeTimeout) {
     clearTimeout(resizeTimeout)
     resizeTimeout = null
   }
   // ...
 }

Also applies to: 630-633

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/components/IndicatorChart.vue` around lines 454 - 590, Add a
generation token to guard initChart: increment a shared numeric token (e.g.,
initGeneration) at the start of initChart, capture it into a local const (e.g.,
currentGeneration) and after every await (especially after loadChartData, the
double requestAnimationFrame waits, the container-width polling loop, and before
each section that mutates state or creates charts/resizeObserver) check that
currentGeneration === initGeneration and return early if it differs; ensure you
perform no DOM/chart mutations when stale and only call
overlayChart/oscillatorChart creation,
setupIndicatorSeries/setupCandlestickSeries/setupVolumeSeries, series.setData,
and resizeObserver.observe when the token still matches; apply the same
generation-guard pattern to the other async reload path that also runs on
src/indicatorKey changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.vscode/tasks.json:
- Line 760: Remove the unconditional "|| true" that forces success from the
Windows stop command containing "Stop-Process -Name Test.Simulation -Force
-ErrorAction SilentlyContinue" so the shell exit code can drive the conditional;
then update the other Windows stop command variant (the similar PowerShell
command further down) so it uses an OR fallback that prints the "[INFO] No
Simulation server processes found" message only when the Stop-Process call fails
(i.e., replace the current unconditional success pattern with a proper "&& echo
'[OK] ...' || echo '[INFO] ...'" flow).
- Line 431: The Test: Integration task currently appends " ; powershell.exe ...
|| true" which forces success; instead run the dotnet test commands, capture
their exit code into a variable, perform the cleanup (Stop-Process -Name
Test.SseServer -Force -ErrorAction SilentlyContinue) and then exit/passthrough
with the original test exit code so failures propagate to CI. Locate the task
command string that contains "dotnet test
tests/integration/Tests.Integration.csproj ... && dotnet test
tests/public-api/Tests.PublicApi.csproj ...; powershell.exe" and replace the
trailing `|| true` with logic that saves the tests' exit code and uses that code
as the final process exit after running the PowerShell cleanup.

---

Outside diff comments:
In `@docs/.vitepress/components/IndicatorChart.vue`:
- Around line 454-590: Add a generation token to guard initChart: increment a
shared numeric token (e.g., initGeneration) at the start of initChart, capture
it into a local const (e.g., currentGeneration) and after every await
(especially after loadChartData, the double requestAnimationFrame waits, the
container-width polling loop, and before each section that mutates state or
creates charts/resizeObserver) check that currentGeneration === initGeneration
and return early if it differs; ensure you perform no DOM/chart mutations when
stale and only call overlayChart/oscillatorChart creation,
setupIndicatorSeries/setupCandlestickSeries/setupVolumeSeries, series.setData,
and resizeObserver.observe when the token still matches; apply the same
generation-guard pattern to the other async reload path that also runs on
src/indicatorKey changes.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2dd2a93 and be9d650.

📒 Files selected for processing (4)
  • .markdownlint-cli2.jsonc
  • .vscode/tasks.json
  • docs/.vitepress/components/IndicatorChart.vue
  • docs/indicators/T3.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/indicators/T3.md

@DaveSkender DaveSkender temporarily deployed to stockindicators.dev February 28, 2026 16:32 — with GitHub Actions Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
docs/.vitepress/lib/chart-data-provider.ts (1)

100-102: ⚠️ Potential issue | 🟡 Minor

Normalize maxBars before slicing.

Line [101] still accepts 0, negative, NaN, and fractional values; this leads to unexpected slice() output. Please coerce to a finite positive integer with a safe default.

Proposed fix
 export async function getChartData(options: ChartProviderOptions): Promise<ChartData> {
-  const maxBars = options.maxBars ?? 100
+  const requestedBars = options.maxBars
+  const maxBars =
+    typeof requestedBars === 'number' &&
+    Number.isFinite(requestedBars) &&
+    requestedBars > 0
+      ? Math.floor(requestedBars)
+      : 100
   const cacheKey = getCacheKey(options.indicatorKey)
#!/bin/bash
# Verify current maxBars handling and whether normalization exists.
rg -n "const maxBars = options.maxBars|Number\\.isFinite\\(|Math\\.floor\\(" docs/.vitepress/lib/chart-data-provider.ts -C2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/lib/chart-data-provider.ts` around lines 100 - 102, Normalize
options.maxBars at the top of getChartData: coerce options.maxBars into a finite
positive integer before using it for slicing (replace the current const maxBars
= options.maxBars ?? 100). Use Number.isFinite to detect non-finite values, fall
back to the safe default (100), and apply Math.floor and Math.max to ensure a
positive integer (at least 1) so slice() receives a valid count; update any
downstream uses of maxBars (e.g., where slice(...) is called) to rely on this
normalized value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.markdownlint-cli2.jsonc`:
- Line 13: Re-enable MD033 by removing the global "MD033": false setting and
instead add an explicit allow-list under MD033.allowed_elements that includes
only the required VitePress/custom tags (e.g., "IndicatorChartPanel", "style",
"span", "h2", "h3" or any other specific tags used by the docs); update
docs/.markdownlint-cli2.jsonc to restore MD033 to true (or remove the false
entry) and add MD033.allowed_elements with the narrow set of permitted elements
so inline HTML remains blocked except for those approved tags.

In `@docs/.vitepress/components/IndicatorChart.vue`:
- Around line 632-635: Concurrent invocations of initChart (triggered by the
watchers) can interleave because initChart awaits loadChartData,
requestAnimationFrame and a polling loop, allowing stale runs to overwrite newer
state when globals are assigned; fix this by adding a run token (e.g.,
chartRunId) that is incremented in destroyChart and captured at the start of
initChart, then validate the captured token after each await point (before
assigning any global chart objects or completing render steps) and bail out if
it no longer matches so only the most recent initChart run proceeds to update
shared state (reference functions: destroyChart, initChart, loadChartData and
the polling loop).

In `@docs/.vitepress/lib/chart-data-provider.ts`:
- Around line 92-97: fetchJson currently awaits fetch(url) with no timeout, so
stalled requests never reject and prevent the fallback; add an AbortController
inside fetchJson, pass controller.signal to fetch(url, { signal }), start a
setTimeout that calls controller.abort() after ~8000ms, and clear that timeout
once fetch completes successfully; ensure the aborted fetch results in a thrown
error so the existing try/catch/fallback logic can run (clean up the timer in
both success and error paths).

In `@docs/indicators/StochRsi.md`:
- Around line 134-136: The example declares the same local variable name twice
(var stochRsiHub) causing a duplicate declaration error; update the second
declaration (the one assigning rsiHub.ToStochRsiHub()) by either removing the
var to reassign the existing stochRsiHub or renaming it (e.g., stochRsiHub2) so
it no longer conflicts with the earlier stochRsiHub declaration; locate the
second occurrence by the rsiHub and ToStochRsiHub() symbols and make the rename
or reassign change accordingly.

---

Duplicate comments:
In `@docs/.vitepress/lib/chart-data-provider.ts`:
- Around line 100-102: Normalize options.maxBars at the top of getChartData:
coerce options.maxBars into a finite positive integer before using it for
slicing (replace the current const maxBars = options.maxBars ?? 100). Use
Number.isFinite to detect non-finite values, fall back to the safe default
(100), and apply Math.floor and Math.max to ensure a positive integer (at least
1) so slice() receives a valid count; update any downstream uses of maxBars
(e.g., where slice(...) is called) to rely on this normalized value.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be9d650 and 24a3254.

📒 Files selected for processing (10)
  • .vscode/tasks.json
  • docs/.markdownlint-cli2.jsonc
  • docs/.vitepress/components/IndicatorChart.vue
  • docs/.vitepress/lib/chart-data-provider.ts
  • docs/.vitepress/lib/chart-time.ts
  • docs/.vitepress/lib/chart-types.ts
  • docs/indicators/Slope.md
  • docs/indicators/StochRsi.md
  • tests/indicators/Tests.Indicators.csproj
  • tools/simulate/Test.Simulation.csproj
✅ Files skipped from review due to trivial changes (1)
  • tools/simulate/Test.Simulation.csproj
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/.vitepress/lib/chart-time.ts

@DaveSkender DaveSkender temporarily deployed to stockindicators.dev February 28, 2026 16:59 — with GitHub Actions Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
docs/.vitepress/components/IndicatorChart.vue (1)

17-17: Remove unused import parseTimestamp.

parseTimestamp is imported but not used anywhere in this component.

🧹 Proposed fix
-import { createTimeContext, parseTimestamp } from '../lib/chart-time'
+import { createTimeContext } from '../lib/chart-time'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/components/IndicatorChart.vue` at line 17, The import
statement in IndicatorChart.vue includes an unused symbol parseTimestamp; remove
parseTimestamp from the import so only createTimeContext is imported (i.e.,
change the import from "{ createTimeContext, parseTimestamp }" to just "{
createTimeContext }") to eliminate the unused import warning and keep imports
minimal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/.vitepress/components/IndicatorChart.vue`:
- Line 17: The import statement in IndicatorChart.vue includes an unused symbol
parseTimestamp; remove parseTimestamp from the import so only createTimeContext
is imported (i.e., change the import from "{ createTimeContext, parseTimestamp
}" to just "{ createTimeContext }") to eliminate the unused import warning and
keep imports minimal.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24a3254 and a8ab0fc.

📒 Files selected for processing (5)
  • .vscode/tasks.json
  • docs/.markdownlint-cli2.jsonc
  • docs/.vitepress/components/IndicatorChart.vue
  • docs/.vitepress/lib/chart-data-provider.ts
  • docs/indicators/StochRsi.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/indicators/StochRsi.md

@DaveSkender
Copy link
Owner Author

@CodeRabbit resolve or renew any old feedback items if they are still valid

@DaveSkender DaveSkender temporarily deployed to stockindicators.dev February 28, 2026 17:20 — with GitHub Actions Inactive
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@DaveSkender, I'll check for any existing review comments and their status on this PR.


🏁 Script executed:

#!/bin/bash
# List all review comments from coderabbitai on PR `#1981`
gh pr view 1981 --json reviews,comments --jq '.reviews[] | select(.author.login == "coderabbitai") | {state: .state, body: .body, createdAt: .createdAt}'

# Also check for review comments (inline comments)
gh api repos/DaveSkender/Stock.Indicators/pulls/1981/comments --jq '.[] | select(.user.login == "coderabbitai") | {id: .id, path: .path, body: .body, created_at: .created_at, line: .line}'

Length of output: 33224


I'll check the current state of the code to see which feedback items are still valid.


🏁 Script executed:

#!/bin/bash

echo "=== 1. Check StochRsi.md line 136 for typo ==="
sed -n '135,137p' docs/indicators/StochRsi.md

echo -e "\n=== 2. Check Slope.md for duplicate IndicatorChartPanel ==="
grep -n "IndicatorChartPanel" docs/indicators/Slope.md

echo -e "\n=== 3. Check chart-data-provider.ts localStorage guards ==="
sed -n '45,64p' docs/.vitepress/lib/chart-data-provider.ts

echo -e "\n=== 4. Check chart-data-provider.ts maxBars normalization ==="
sed -n '100,102p' docs/.vitepress/lib/chart-data-provider.ts

echo -e "\n=== 5. Check chart-data-provider.ts fetchJson timeout ==="
sed -n '92,110p' docs/.vitepress/lib/chart-data-provider.ts

echo -e "\n=== 6. Check chart-time.ts for timeIndexOffsets issue ==="
sed -n '54,79p' docs/.vitepress/lib/chart-time.ts | head -20

echo -e "\n=== 7. Check IndicatorChart.vue unused import ==="
sed -n '17p' docs/.vitepress/components/IndicatorChart.vue

echo -e "\n=== 8. Check IndicatorChart.vue for race condition guards ==="
rg -n "initGeneration|chartRunId" docs/.vitepress/components/IndicatorChart.vue

echo -e "\n=== 9. Check tasks.json for forced success patterns ==="
rg -n "\|\| true" .vscode/tasks.json

echo -e "\n=== 10. Check .markdownlint-cli2.jsonc MD033 setting ==="
sed -n '10,15p' docs/.markdownlint-cli2.jsonc

echo -e "\n=== 11. Check update-indicator-charts.mjs for issues ==="
if [ -f docs/scripts/update-indicator-charts.mjs ]; then
  sed -n '45,50p' docs/scripts/update-indicator-charts.mjs
  echo "---"
  sed -n '60,65p' docs/scripts/update-indicator-charts.mjs
else
  echo "File not found"
fi

Length of output: 3608


Let me check a few more items that were mentioned in the reviews:


🏁 Script executed:

#!/bin/bash

echo "=== Check sliceData for mutation issue ==="
sed -n '80,93p' docs/.vitepress/lib/chart-data-provider.ts

echo -e "\n=== Check full maxBars normalization context ==="
sed -n '103,120p' docs/.vitepress/lib/chart-data-provider.ts

echo -e "\n=== Check StochRsi.md for duplicate variable declaration ==="
sed -n '125,140p' docs/indicators/StochRsi.md | grep -A5 -B5 "var stochRsiHub"

echo -e "\n=== Verify parseTimestamp is actually unused in IndicatorChart.vue ==="
rg -n "parseTimestamp" docs/.vitepress/components/IndicatorChart.vue

Length of output: 1481


@DaveSkender, I've reviewed all previous feedback items. Great progress—most issues have been addressed! Here's the status:

Resolved Issues (can be marked as addressed):

  1. StochRsi.md typo - Fixed: comment now correctly reads "// does not create a 2nd internal hub"
  2. Slope.md duplicate panel - Fixed: only one IndicatorChartPanel present
  3. localStorage guards - Fixed: wrapped in try-catch with proper error handling
  4. maxBars normalization - Fixed: uses Number.isFinite(), Math.floor(), validates > 0
  5. fetchJson timeout - Fixed: AbortController with 8-second timeout implemented
  6. timeIndexOffsets cross-series leakage - Fixed: createResolveSeriesTime() factory returns fresh Map per call
  7. IndicatorChart.vue race conditions - Fixed: chartRunId token guards async operations
  8. sliceData mutation - Fixed: returns new object with spread operator, doesn't mutate cache
  9. StochRsi duplicate variable - Fixed: proper usage shown
  10. update-indicator-charts.mjs issues - N/A: file removed from repo

⚠️ Remaining Valid Issues:

1. Unused import (Nitpick) - IndicatorChart.vue line 17

import { createTimeContext, parseTimestamp } from '../lib/chart-time'

parseTimestamp is imported but never used. Minor cleanup:

-import { createTimeContext, parseTimestamp } from '../lib/chart-time'
+import { createTimeContext } from '../lib/chart-time'

2. Tasks.json forced success - .vscode/tasks.json line 780

The SSE Server stop command forces success with || true, preventing proper error propagation to CI:

... || true) && echo '[OK] ...' || echo '[INFO] ...'

3. MD033 globally disabled - docs/.markdownlint-cli2.jsonc line 13

"MD033": false,  // accomodate vue components

This disables all inline HTML checks. The review suggested re-enabling MD033 and using allowed_elements instead to permit only specific custom components (IndicatorChartPanel, etc.) while still catching unintended HTML.

Would you like me to resolve these remaining three items, or do you prefer to handle them differently?


🧠 Learnings used
Learnt from: CR
Repo: DaveSkender/Stock.Indicators PR: 0
File: .agents/skills/indicator-series/SKILL.md:0-0
Timestamp: 2026-02-23T05:22:12.210Z
Learning: Applies to docs/indicators/*.md : Document indicators in `docs/indicators/{Indicator}.md`

Learnt from: CR
Repo: DaveSkender/Stock.Indicators PR: 0
File: docs/AGENTS.md:0-0
Timestamp: 2026-02-23T05:20:59.763Z
Learning: Applies to docs/indicators/**/*.md : Add indicator pages to the `indicators/` directory

Learnt from: CR
Repo: DaveSkender/Stock.Indicators PR: 0
File: .agents/skills/code-completion/SKILL.md:0-0
Timestamp: 2026-02-23T05:21:32.981Z
Learning: Applies to docs/indicators/**/*.md : Update docs/indicators/{IndicatorName}.md when changing indicators or public APIs

Learnt from: CR
Repo: DaveSkender/Stock.Indicators PR: 0
File: .agents/skills/indicator-stream/SKILL.md:0-0
Timestamp: 2026-02-23T05:22:24.575Z
Learning: Applies to docs/indicators/*.md : Update public documentation in docs/indicators/{IndicatorName}.md for StreamHub implementations

@DaveSkender DaveSkender temporarily deployed to stockindicators.dev February 28, 2026 17:28 — with GitHub Actions Inactive
@DaveSkender DaveSkender merged commit 4647208 into v3 Feb 28, 2026
14 checks passed
@DaveSkender DaveSkender deleted the codex/refactor-charts-component-for-docs-website branch February 28, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Development

Successfully merging this pull request may close these issues.

1 participant