@iccandle/reactjs-widget 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,181 +1,40 @@
1
1
  # @iccandle/reactjs-widget
2
2
 
3
- React component that wraps an existing [TradingView Charting Library](https://www.tradingview.com/charting-library-docs/) widget and adds ICCandle’s **scanner UI**: a draggable popup to run pattern search over a user-selected bar range, with theming loaded from ICCandle’s API.
3
+ React overlay for the [TradingView Charting Library](https://www.tradingview.com/charting-library-docs/) that adds ICCandle’s **pattern scanner**, **results iframe**, **pattern tracker**, and **news/event** integration.
4
4
 
5
- Published as ESM and CommonJS; component styles are bundled and injected at runtime (no separate CSS import).
5
+ The package ships ESM + CommonJS. Component styles are injected at runtime (no separate CSS import).
6
6
 
7
- ## Install
8
-
9
- ```bash
10
- npm install @iccandle/reactjs-widget
11
- ```
12
-
13
- ### How to get an API key
14
-
15
- 1. Register or sign in at the [ICCandle corporate portal](https://corporate-iccandle.vercel.app/).
16
- 2. Use **Create API key** in the dashboard.
17
- 3. When selecting a service, choose **search** so the key is valid for this widget (remote theming, candle cache, and scanner validation).
18
-
19
- Issued keys use the format `icc_search_` followed by 48 hexadecimal characters.
20
-
21
- ### Prerequisites
22
-
23
- - **React 18+** — `react` and `react-dom` are **peer dependencies** (install them in your app).
24
- - **TradingView Charting Library** — obtain it under your own license from TradingView, host the static assets (e.g. under `/charting_library/` in your public folder), and load the library at runtime. **This package does not ship the charting library.**
25
- - **ICCandle API key** — required for theme, candle cache, and scanner validation; format `icc_search_` plus 48 hex characters. See [How to get an API key](#how-to-get-an-api-key) above.
26
-
27
- ## Quick start
28
-
29
- ```tsx
30
- import { useState } from "react";
31
- import type { IChartingLibraryWidget } from "charting_library/charting_library";
32
- import { WidgetIccandle } from "@iccandle/reactjs-widget";
7
+ ## What it does
33
8
 
34
- function App() {
35
- const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
36
- null,
37
- );
38
- const widgetKey = "icc_search_..."; // ICCandle-issued key (48 hex chars after prefix)
9
+ `WidgetIccandle` wraps your chart and renders:
39
10
 
40
- return (
41
- <WidgetIccandle
42
- chartWidget={chartWidget}
43
- widgetKey={widgetKey}
44
- theme="system"
45
- submitCallback={(iframeSrc) => {
46
- // Full URL for the ICCandle plugin iframe (open in modal, new window, etc.)
47
- console.log(iframeSrc);
48
- }}
49
- >
50
- {/* Your chart container + TradingView bootstrap; call setChartWidget when ready */}
51
- <div id="tv_chart_container" style={{ height: "100%" }} />
52
- </WidgetIccandle>
53
- );
54
- }
55
- ```
11
+ | Surface | Role |
12
+ | -------- | ---- |
13
+ | **Scanner popup** | Draggable overlay over the chart. Select a bar window with TradingView’s Date Range tool, then run a scan or subscribe to pattern alerts. |
14
+ | **Results iframe** | Side-by-side panel hosting the ICCandle embed app (`embed-iccandle-app.iccandle.ai`) — search results, similar events, auth, billing. |
15
+ | **Chart sync** | Bidirectional `postMessage` bridge: pattern selection, chart replay, news marks, sign-in tokens, loading state. |
56
16
 
57
- Replace the chart placeholder with your TradingView initialization and pass the `IChartingLibraryWidget` instance when `onChartReady` (or equivalent) fires.
17
+ Typical flow:
58
18
 
59
- ## Usage guide
19
+ 1. User draws a **Date Range** on the chart (or activates the scanner).
20
+ 2. Scanner caches the selected candles and opens the results iframe with search parameters.
21
+ 3. The embed app returns matches; selecting a pattern redraws the range on the chart. Replay / news flows inject predicted candles via custom indicators.
60
22
 
61
- ### Step 1 — Install the package
23
+ ## Install
62
24
 
63
25
  ```bash
64
26
  npm install @iccandle/reactjs-widget
27
+ # or
28
+ pnpm add @iccandle/reactjs-widget
65
29
  ```
66
30
 
67
- Ensure `react` and `react-dom` are installed and meet the peer version range.
68
-
69
- ### Step 2 — Host the Charting Library
70
-
71
- 1. Copy the TradingView Charting Library build into a path your app can serve as static files (e.g. `public/charting_library/` in Vite or Create React App).
72
- 2. Import the library constructor from that path in your bundler setup (see TradingView’s integration docs for your framework). The library is **not** bundled inside `@iccandle/reactjs-widget`; it loads at runtime via `library_path` (or equivalent) on the widget options.
73
-
74
- ### Step 3 — Bootstrap TradingView and capture the widget instance
75
-
76
- Create a ref for the chart DOM node, instantiate the widget in `useEffect`, store the instance in React state, and remove it on cleanup:
77
-
78
- ```tsx
79
- import { useEffect, useRef, useState } from "react";
80
- import type {
81
- ChartingLibraryWidgetOptions,
82
- IChartingLibraryWidget,
83
- ResolutionString,
84
- } from "charting_library/charting_library";
85
- import { widget } from "charting_library/charting_library";
86
-
87
- const LIBRARY_PATH = "/charting_library/"; // must match your hosted assets
88
-
89
- // Inside your component:
90
- const containerRef = useRef<HTMLDivElement>(null);
91
- const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
92
- null,
93
- );
94
-
95
- useEffect(() => {
96
- const el = containerRef.current;
97
- if (!el) return;
98
-
99
- const options: ChartingLibraryWidgetOptions = {
100
- container: el,
101
- library_path: LIBRARY_PATH,
102
- symbol: "EURUSD",
103
- interval: "60" as ResolutionString,
104
- datafeed: yourDatafeed,
105
- locale: "en",
106
- autosize: true,
107
- };
108
-
109
- const tv = new widget(options);
110
- setChartWidget(tv);
111
-
112
- return () => {
113
- try {
114
- tv.remove();
115
- } catch {
116
- /* no-op */
117
- }
118
- setChartWidget(null);
119
- };
120
- }, [/* library_path, datafeed identity, or other inputs that should recreate the chart */]);
121
- ```
122
-
123
- You must supply a valid `datafeed`, `symbol`, `interval`, `locale`, and any other options required by your TradingView license and app. Import paths for `widget` and types differ by setup (`charting_library/charting_library`, `./public/charting_library`, etc.—follow TradingView’s docs for your bundler). If your integration only exposes the instance after `onChartReady`, call `setChartWidget` inside that callback instead of immediately after `new widget(...)`.
124
-
125
- ### Step 4 — Wrap the chart with `WidgetIccandle`
126
-
127
- `WidgetIccandle` must wrap the same subtree that contains the chart container so the scanner overlay positions correctly. Pass the live widget instance (or `null` while mounting):
128
-
129
- ```tsx
130
- import { WidgetIccandle } from "@iccandle/reactjs-widget";
131
-
132
- const widgetKey = "icc_search_..."; // your ICCandle key
133
-
134
- <WidgetIccandle
135
- chartWidget={chartWidget}
136
- widgetKey={widgetKey}
137
- theme="system"
138
- submitCallback={(iframeSrc) => {
139
- /* see Step 5 */
140
- }}
141
- >
142
- <div ref={containerRef} style={{ height: "100%", minHeight: 400 }} />
143
- </WidgetIccandle>
144
- ```
145
-
146
- ### Step 5 — Handle the plugin iframe URL
147
-
148
- After a successful scan setup, `submitCallback` receives a **full HTTPS URL** for the ICCandle plugin iframe. The query string typically includes the bar window (timestamps / size), symbol, `candle_id`, `apiKey` (your widget key), resolved theme, and any active filters—use it as-is in an iframe `src` or deep link.
149
-
150
- **Open in a new tab**
151
-
152
- ```ts
153
- submitCallback={(iframeSrc) => {
154
- window.open(iframeSrc, "_blank", "noopener,noreferrer");
155
- }}
156
- ```
157
-
158
- **Show in a modal or side panel**
159
-
160
- Store the URL in state and render:
161
-
162
- ```tsx
163
- {iframeSrc ? (
164
- <iframe title="ICCandle pattern search" src={iframeSrc} className="..." />
165
- ) : null}
166
- ```
167
-
168
- **TypeScript:** import `WidgetIccandleProps` if you wrap `WidgetIccandle` in your own component and want explicit prop typing. Import `IChartingLibraryWidget` from **your** Charting Library typings path (`charting_library/charting_library` or the path your project uses)—this package does not re-export TradingView types.
169
-
170
- ### Theme and remote branding
171
-
172
- - **`theme="light"` / `"dark"`** — forces that palette for the scanner chrome and for values forwarded into the plugin URL.
173
- - **`theme="system"`** (recommended when you don’t control parent theme) — follows `prefers-color-scheme` for light/dark resolution.
174
- - On mount, the widget fetches your org’s tokens from ICCandle (see [Widget key and remote theming](#widget-key-and-remote-theming)) and sets CSS custom properties on the widget root, e.g. `--iccandle-primary`, `--iccandle-border`, so the in-chart scanner matches your configured light/dark branding. The same resolved theme is reflected in the iframe URL so the plugin UI stays consistent.
31
+ ### Prerequisites
175
32
 
176
- ### Full working example
33
+ - **React 18+** — `react` and `react-dom` are peer dependencies.
34
+ - **TradingView Charting Library** — obtain under your own license, host the static assets (e.g. `/charting_library/`), and bootstrap the widget yourself. **This package does not ship the charting library.**
35
+ - **Auth token** — API calls (candle cache, pattern tracker) use `Bearer` auth from `localStorage` key `iccandle_token`. The embedded results app sets this via a `sign-in-success` `postMessage` after login.
177
36
 
178
- Minimal end-to-end pattern: chart container ref, widget lifecycle, `WidgetIccandle`, and iframe after submit. Replace `yourDatafeed` and widget options with your real datafeed and TradingView settings.
37
+ ## Quick start
179
38
 
180
39
  ```tsx
181
40
  import { useEffect, useRef, useState } from "react";
@@ -185,18 +44,44 @@ import type {
185
44
  ResolutionString,
186
45
  } from "charting_library/charting_library";
187
46
  import { widget } from "charting_library/charting_library";
188
- import { WidgetIccandle } from "@iccandle/reactjs-widget";
47
+ import {
48
+ WidgetIccandle,
49
+ withPlayChart,
50
+ getCustomIndicators,
51
+ } from "@iccandle/reactjs-widget";
189
52
 
190
53
  const LIBRARY_PATH = "/charting_library/";
191
- const WIDGET_KEY = "icc_search_your48hexcharactershere............";
192
54
 
193
- export function ChartWithIccandleScanner() {
55
+ function App() {
194
56
  const containerRef = useRef<HTMLDivElement>(null);
195
57
  const [chartWidget, setChartWidget] = useState<IChartingLibraryWidget | null>(
196
58
  null,
197
59
  );
198
- const [pluginSrc, setPluginSrc] = useState("");
199
60
 
61
+ return (
62
+ <div style={{ height: "100vh", width: "100%" }}>
63
+ <WidgetIccandle chartWidget={chartWidget} theme="light" language="en">
64
+ {(chartRefs) => (
65
+ <ChartHost
66
+ containerRef={containerRef}
67
+ chartRefs={chartRefs}
68
+ onReady={setChartWidget}
69
+ />
70
+ )}
71
+ </WidgetIccandle>
72
+ </div>
73
+ );
74
+ }
75
+
76
+ function ChartHost({
77
+ containerRef,
78
+ chartRefs,
79
+ onReady,
80
+ }: {
81
+ containerRef: React.RefObject<HTMLDivElement | null>;
82
+ chartRefs: import("@iccandle/reactjs-widget").WidgetIccandleChartRefs;
83
+ onReady: (w: IChartingLibraryWidget | null) => void;
84
+ }) {
200
85
  useEffect(() => {
201
86
  const el = containerRef.current;
202
87
  if (!el) return;
@@ -206,18 +91,18 @@ export function ChartWithIccandleScanner() {
206
91
  library_path: LIBRARY_PATH,
207
92
  symbol: "EURUSD",
208
93
  interval: "60" as ResolutionString,
209
- datafeed: yourDatafeed,
94
+ datafeed: withPlayChart(yourDatafeed), // enables chart replay injection
210
95
  locale: "en",
211
96
  autosize: true,
212
- fullscreen: false,
213
97
  drawings_access: {
214
98
  type: "black",
215
99
  tools: [{ name: "Date Range" }],
216
100
  },
101
+ custom_indicators_getter: () => getCustomIndicators("light"),
217
102
  };
218
103
 
219
104
  const tv = new widget(options);
220
- setChartWidget(tv);
105
+ onReady(tv);
221
106
 
222
107
  return () => {
223
108
  try {
@@ -225,108 +110,139 @@ export function ChartWithIccandleScanner() {
225
110
  } catch {
226
111
  /* no-op */
227
112
  }
228
- setChartWidget(null);
113
+ onReady(null);
229
114
  };
230
- }, []);
115
+ }, [containerRef, chartRefs, onReady]);
231
116
 
232
- return (
233
- <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
234
- <div style={{ height: 500, width: "100%" }}>
235
- <WidgetIccandle
236
- chartWidget={chartWidget}
237
- widgetKey={WIDGET_KEY}
238
- theme="system"
239
- submitCallback={(iframeSrc) => setPluginSrc(iframeSrc)}
240
- >
241
- <div ref={containerRef} style={{ height: "100%", width: "100%" }} />
242
- </WidgetIccandle>
243
- </div>
244
- {pluginSrc ? (
245
- <iframe
246
- title="ICCandle search"
247
- src={pluginSrc}
248
- style={{ width: "100%", height: 600, border: "1px solid #ccc" }}
249
- />
250
- ) : null}
251
- </div>
252
- );
117
+ return <div ref={containerRef} style={{ height: "100%", width: "100%" }} />;
253
118
  }
254
119
  ```
255
120
 
256
- For a concrete in-repo reference (custom datafeed, timezone, visibility handling), see [`src/tradingview/TradingviewChart.tsx`](src/tradingview/TradingviewChart.tsx) in this repository’s dev app.
121
+ Use a **render-prop `children`** when you need `chartRefs` for generated-candle studies (replay / predict). A plain React node also works if you do not need those refs.
257
122
 
258
- ### Common patterns
123
+ For a full in-repo reference, see [`src/tradingview/TradingviewChart.tsx`](src/tradingview/TradingviewChart.tsx).
259
124
 
260
- | Pattern | Approach |
261
- | -------- | -------- |
262
- | **Modal iframe** | On `submitCallback`, set state and render `<iframe src={url} />` inside a `<dialog>`, Radix Dialog, MUI Modal, etc. |
263
- | **New tab** | `window.open(iframeSrc, "_blank", "noopener,noreferrer")`. |
264
- | **Split layout** | Keep the chart in one column and mount the iframe in another when `pluginSrc` is set (as in the example above). |
265
- | **News / events on the time axis** | If your datafeed implements `getTimescaleMarks`, you can sync marks with `localStorage` — see [Optional: timescale marks (news/events)](#optional-timescale-marks-newsevents). |
125
+ ## Features
266
126
 
267
- ### Troubleshooting
127
+ ### Pattern scanner
268
128
 
269
- | Issue | What to check |
270
- | ----- | --------------- |
271
- | Chart area stays blank | `library_path` must point to the folder URL where TradingView static files are served; container ref must be mounted before `new widget(...)`. |
272
- | `chartWidget` is always `null` | Ensure you call `setChartWidget` after the widget is created (or inside `onChartReady` if your integration requires it). |
273
- | Scanner or theme looks wrong | Confirm `widgetKey` is valid; theme fetch uses `api-key: <widgetKey>`. Network or auth failures may leave defaults. |
274
- | CORS / network errors on theme API | In local dev, browser calls to `api.iccandle.ai` may be blocked by CORS unless you proxy; the widget should still function with fallback styling—verify in production or behind your proxy. |
275
- | Date range tool missing | Enable the Date Range drawing in `drawings_access` (see full example) so users can select the bar window the scanner uses. |
129
+ - Subscribes to chart readiness, symbol, resolution, and drawing events.
130
+ - Manages a `date_range` multipoint drawing so the user can adjust the bar window (default window size: **25** bars).
131
+ - On scan: posts candles to `https://scan-service.iccandle.ai/cacheCandle`, then navigates the results iframe with query params (`ws`, `tk`, `et`, `symbol`, `tf`, `cid`, filters, theme).
132
+ - Optional **advanced filters** (symbols, top-k, lookback period, probability window) stored in `localStorage` under `search-filter`.
133
+
134
+ ### Results iframe
135
+
136
+ - Loads `https://embed-iccandle-app.iccandle.ai/{language}?theme=...` by default.
137
+ - After scan / news navigation, the `src` updates with search or similar-events paths.
138
+ - On iframe load, the parent posts `{ type: "parent-origin", origin }` so Stripe checkout can return to the host domain. If the host URL has `?payment=success`, it also posts `{ type: "payment-success" }` so the embed can refresh subscription state.
139
+
140
+ ### Pattern tracker
141
+
142
+ From the scanner popup, users can open a subscription modal to track a custom pattern (name + timeframes + symbols). Requires a valid `iccandle_token`.
143
+
144
+ ### News / economic events
145
+
146
+ - Clicking a timescale mark can open an **event info** modal (currency calendar events; skips holidays / early / sentiment-only marks).
147
+ - “Go to detail” loads similar events in the iframe and draws a vertical line on the chart.
148
+ - Marks can be driven from `localStorage` keys `tv:selected-news-events` and `tv:clicked-news-event` if your datafeed implements `getTimescaleMarks` (see below).
149
+
150
+ ### Chart replay (play candles)
151
+
152
+ Wrap your datafeed with `withPlayChart` and register custom indicators via `getCustomIndicators` so the embed can inject predicted / replay bars without live ticks fighting the playback.
276
153
 
277
154
  ## API
278
155
 
279
156
  ### Exports
280
157
 
281
- | Name | Kind | Description |
282
- | -------------------- | ------ | -------------------------------------------- |
283
- | `WidgetIccandle` | Component | Scanner overlay around your chart subtree |
284
- | `WidgetIccandleProps`| Type | Props for `WidgetIccandle` |
158
+ | Name | Kind | Description |
159
+ | ---- | ---- | ----------- |
160
+ | `WidgetIccandle` | Component | Chart overlay + results iframe |
161
+ | `WidgetIccandleProps` | Type | Props for `WidgetIccandle` |
162
+ | `WidgetIccandleChartRefs` | Type | Refs for highlight bars / generated-candle studies |
163
+ | `WidgetLanguage` | Type | Supported locale codes |
164
+ | `withPlayChart` | Function | Wraps a TradingView datafeed for replay bar injection |
165
+ | `getCustomIndicators` | Function | Returns ICCandle custom indicators (generated candles) |
166
+ | `getGeneratedCandlesMaskColor` | Function | Theme-aware mask color for generated candles |
285
167
 
286
168
  ### `WidgetIccandle` props
287
169
 
288
- | Prop | Type | Required | Description |
289
- | ----------------- | --------------------------------------- | -------- | ----------- |
290
- | `chartWidget` | `IChartingLibraryWidget \| null` | Yes | Live TradingView widget instance (`null` until ready). |
291
- | `children` | `ReactNode` | Yes | Your chart UI (e.g. container + library bootstrap). |
292
- | `widgetKey` | `string` | Yes | ICCandle API key (`icc_search_` + 48 hex chars). Used for theme fetch, candle cache, and scanner validation. |
293
- | `submitCallback` | `(iframeSrc: string) => void` | Yes | Called after a successful scan setup with the **plugin iframe URL** (query string includes window size, timestamps, symbol, `candle_id`, `apiKey`, theme, optional filters). |
294
- | `theme` | `"light" \| "dark" \| "system"` | No | Defaults to sensible behavior; `"system"` follows `prefers-color-scheme`. Affects resolved theme tokens and the plugin URL. |
170
+ | Prop | Type | Required | Description |
171
+ | ---- | ---- | -------- | ----------- |
172
+ | `chartWidget` | `IChartingLibraryWidget \| null` | Yes | Live TradingView widget (`null` until ready). |
173
+ | `children` | `ReactNode \| ((chartRefs) => ReactNode)` | Yes | Chart UI, or render prop receiving `WidgetIccandleChartRefs`. |
174
+ | `theme` | `"light" \| "dark" \| "system"` | No | Scanner chrome + iframe theme. `"system"` follows `prefers-color-scheme`. |
175
+ | `language` | `WidgetLanguage` | No | UI + iframe locale. One of: `en`, `zh`, `vi`, `th`, `ko`, `ja`, `mn`, `ru`. Defaults to `en`. |
176
+ | `onCloseResult` | `() => void` | No | Called when the embed posts `close-result`. |
177
+ | `iframeLoaded` | `boolean` | No | When `false`, disables scan / track actions until the results iframe is ready. Defaults to enabled behavior in the scanner. |
295
178
 
296
- ### Widget key and remote theming
179
+ `IChartingLibraryWidget` must be imported from **your** Charting Library typings — this package does not re-export TradingView types.
297
180
 
298
- On mount, the component loads branding colors from ICCandle:
181
+ ### `withPlayChart(datafeed)`
299
182
 
300
- - **URL:** `https://api.iccandle.ai/corporate-client/v1/widgetStyle/search/user/?service_type=search`
301
- - **Header:** `api-key: <widgetKey>`
183
+ Wraps `subscribeBars` so ICCandle can push replay bars and optionally block live ticks during playback.
302
184
 
303
- CSS custom properties (`--iccandle-primary`, `--iccandle-border`, etc.) are applied on the widget root so the scanner matches your configured light/dark tokens.
185
+ ```ts
186
+ import { withPlayChart } from "@iccandle/reactjs-widget";
304
187
 
305
- **Theme prop vs. API tokens:** The `theme` prop (`"light"` \| `"dark"` \| `"system"`) chooses which variant of those tokens to apply and is also forwarded in the plugin iframe URL. `"system"` tracks `prefers-color-scheme` so the scanner and plugin stay aligned with the user’s OS preference when you don’t pass an explicit light/dark mode from your app shell.
188
+ datafeed: withPlayChart(myDatafeed),
189
+ // or factory + args:
190
+ datafeed: withPlayChart(createDatafeed, arg1, arg2),
191
+ ```
306
192
 
307
- ### Behavior summary
193
+ ### `getCustomIndicators(theme?)`
308
194
 
309
- - Subscribes to chart readiness, resolution, symbol changes, and drawing events.
310
- - Manages a `date_range` multipoint drawing so the user can adjust the bar window; window size and exported candles drive the scanner.
311
- - Posts candles to ICCandle’s cache endpoint before opening the plugin URL (see scanner implementation for details).
312
- - Listens for `window` `message` events for chart/news integration and can clear persisted news mark selections (`localStorage` key `tv:selected-news-events`) when starting a scan.
195
+ Returns a Promise of TradingView `CustomIndicator[]` used for generated / predicted candle overlays. Pass `"light"` or `"dark"` to match chart theme. Wire it into widget options:
196
+
197
+ ```ts
198
+ custom_indicators_getter: () => getCustomIndicators("light"),
199
+ ```
200
+
201
+ ## Auth and storage
202
+
203
+ | Key | Purpose |
204
+ | --- | ------- |
205
+ | `iccandle_token` | Bearer token for scan cache, pattern tracker, and related APIs. Set by the embed via `sign-in-success`. |
206
+ | `search-filter` | Scanner advanced options (symbols, top_k, period, probability). |
207
+ | `tv:selected-news-events` | JSON array of news events used as timescale marks. |
208
+ | `tv:clicked-news-event` | Last clicked calendar event for mark highlighting. |
209
+ | `tv:latest-symbol` | Last chart symbol (used by the demo app / fallbacks). |
210
+
211
+ ## postMessage bridge
212
+
213
+ Messages are accepted only from the results origin (`https://embed-iccandle-app.iccandle.ai`).
214
+
215
+ | Direction | Type / action | Effect |
216
+ | --------- | ------------- | ------ |
217
+ | Embed → parent | `play-chart` | Inject / clear replay candles on the chart |
218
+ | Embed → parent | `loading` | Toggle scanner loading UI |
219
+ | Embed → parent | `sign-in-success` | Persist `iccandle_token` from `payload.id_token` |
220
+ | Embed → parent | `close-result` | Clears play state; calls `onCloseResult` |
221
+ | Embed → parent | `pattern_selected` / `custom_pattern_selected` | Draw date range for the compared pattern |
222
+ | Embed → parent | `clear_pattern_selected` / `clear_custom_pattern_selected` | Remove pattern date range |
223
+ | Embed → parent | `eventClicked` | Center chart, draw event line, show mark |
224
+ | Embed → parent | `replay` | Start / stop event replay candles |
225
+ | Embed → parent | `back` / `back-to-similar-events` | Clear event line / replay |
226
+ | Embed → parent | `nav-click` | Hide scanner on `/news`; restore otherwise |
227
+ | Parent → embed | `parent-origin` | Host origin for Stripe return |
228
+ | Parent → embed | `payment-success` | Refresh subscription after checkout |
313
229
 
314
230
  ## Optional: timescale marks (news/events)
315
231
 
316
- If your data feed implements `getTimescaleMarks`, you can surface stored events (e.g. from `localStorage` under `tv:selected-news-events`) as marks on the time axis. Example shape:
232
+ If your datafeed implements `getTimescaleMarks`, surface stored events as marks:
317
233
 
318
234
  ```ts
319
235
  getTimescaleMarks: async (
320
- symbolInfo: LibrarySymbolInfo,
321
- from: number,
322
- to: number,
323
- onResult: GetMarksCallback<TimescaleMark>,
236
+ symbolInfo,
237
+ from,
238
+ to,
239
+ onResult,
324
240
  ) => {
325
- const marks: TimescaleMark[] = [];
241
+ const marks = [];
326
242
  try {
327
243
  const allNewsEvents = JSON.parse(
328
244
  localStorage.getItem("tv:selected-news-events") || "[]",
329
- ) as NewsEventType[];
245
+ );
330
246
 
331
247
  allNewsEvents?.forEach(({ id, timestamp, event_name, currency }) => {
332
248
  if (!id || !Number.isFinite(timestamp) || timestamp <= 0) return;
@@ -349,17 +265,43 @@ getTimescaleMarks: async (
349
265
  };
350
266
  ```
351
267
 
352
- Adjust paths and types to match your app and TradingView typings.
268
+ ## Theming
269
+
270
+ Scanner UI uses CSS variables on `.iccandle-selector-widget`:
271
+
272
+ - `--iccandle-primary`
273
+ - `--iccandle-primary-gradient-end`
274
+ - `--iccandle-background`
275
+ - `--iccandle-border`
276
+ - `--iccandle-text`
277
+ - `--iccandle-secondary`
278
+ - `--iccandle-font`
279
+
280
+ Light and dark defaults live in the bundled stylesheet. The `theme` prop toggles the `.iccandle-dark` class and is forwarded to the iframe.
353
281
 
354
282
  ## Development (this repo)
355
283
 
356
- | Script | Command | Purpose |
357
- | ------------- | ------------ | ------- |
358
- | Dev demo | `npm run dev` | Vite app with local charting library (see `vite.config.ts`). |
359
- | Library build | `npm run build` | Emits `dist/` (JS, CJS, bundled CSS injection, declarations). |
360
- | Lint | `npm run lint` | ESLint. |
284
+ | Script | Command | Purpose |
285
+ | ------ | ------- | ------- |
286
+ | Dev demo | `pnpm run dev` | Vite app with local charting library |
287
+ | Library build | `pnpm run build` | Emits `dist/` (ESM, CJS, injected CSS, `.d.ts`) |
288
+ | Web build | `pnpm run build:web` | SPA build to `dist-web/` |
289
+ | Deploy web | `pnpm run deploy:web` | Build + Vercel production deploy |
290
+ | Lint | `pnpm run lint` | ESLint |
291
+ | Preview web | `pnpm run preview` | Preview the web build |
292
+
293
+ `prepublishOnly` runs `build` before npm publish.
294
+
295
+ ## Troubleshooting
361
296
 
362
- `prepublishOnly` runs `build` before publish.
297
+ | Issue | What to check |
298
+ | ----- | --------------- |
299
+ | Chart stays blank | `library_path` must serve TradingView static files; container must be mounted before `new widget(...)`. |
300
+ | `chartWidget` is always `null` | Call `setChartWidget` after create (or inside `onChartReady` if required). |
301
+ | Scan fails / 401 | Ensure the user signed in via the results iframe so `iccandle_token` is set. |
302
+ | Date Range tool missing | Enable it in `drawings_access` (see Quick start). |
303
+ | Replay bars do not appear | Wrap the datafeed with `withPlayChart` and register `getCustomIndicators`. |
304
+ | Scanner actions disabled | Pass `iframeLoaded={true}` once the results iframe has loaded, or omit the prop if you do not gate on load. |
363
305
 
364
306
  ## License
365
307