@iccandle/vuejs-widget
| Install | |
|---|---|
npm install @iccandle/vuejs-widget |
|
| Latest Version: | 0.4.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 21, 2026 |
| Links: | npm |
@iccandle/vuejs-widget
Vue 3 scanner overlay for an existing TradingView Charting Library widget. It adds ICCandle’s draggable scanner popup, pattern search, replay of predicted candles, and a results embed.
Published as ESM and CommonJS with TypeScript declarations. Component styles are bundled and injected at runtime — no separate CSS import.
Features
- Pattern scanner — select a range on the chart and search for similar historical patterns across forex, commodities, indices, crypto, and stocks.
- AI simulation replay — predicted candles are revealed bar by bar on your chart, skipping closed sessions (e.g. the FX weekend) so 2H/4H bars stay on the chart grid.
- Correlated markets panel — while a simulation is on the chart, the popup expands to show how the move is expected to ripple into correlated markets (ρ, direction, % move).
- Pattern Tracker — subscribe to alerts when a selected pattern forms again.
- News / event overlays — event marks, vertical lines, and event-anchored simulations.
- Theming — light, dark, or system, CSS custom properties, and client styles from the ICCandle Plugin Portal via
apiKey. - 8 UI languages —
en,zh,vi,th,ko,ja,mn,ru.
Contents
Install
npm install @iccandle/vuejs-widget
# or
pnpm add @iccandle/vuejs-widget
vue ^3.5.0 is a peer dependency. Install it in your app. axios is a runtime dependency of this package.
Prerequisites
- Vue 3.5+
- TradingView Charting Library — obtain it under your own license, host the static assets (for example
/charting_library/), and load it at runtime vialibrary_path. This package does not ship the charting library. - A results iframe (or equivalent) that loads ICCandle’s embed app. Scan/tracker actions require the user to sign in inside that embed; the widget stores the session as
iccandle_tokeninlocalStorage.
Quick start
<script setup lang="ts">
import { ref, shallowRef, watch } from "vue";
import type { IChartingLibraryWidget } from "charting_library/charting_library";
import {
WidgetIccandle,
WIDGET_RESULT_URL,
handleResultIframeLoad,
} from "@iccandle/vuejs-widget";
const chartWidget = shallowRef<IChartingLibraryWidget | null>(null);
const language = "en" as const;
const theme = "dark" as const;
const iframeSrc = ref(`${WIDGET_RESULT_URL}/${language}?theme=${theme}&header=true`);
const iframeLoaded = ref(false);
const resultIframeRef = ref<HTMLIFrameElement | null>(null);
const resultIframeBox = { current: null as HTMLIFrameElement | null };
watch(
resultIframeRef,
(el) => {
resultIframeBox.current = el;
},
{ immediate: true },
);
function handleSubmit(src: string) {
iframeSrc.value = src;
}
function handleIframeLoad() {
iframeLoaded.value = true;
handleResultIframeLoad(resultIframeRef.value);
}
</script>
<template>
<div class="iccandle-selector-widget__container">
<div class="iccandle-selector-widget__chart-pane">
<WidgetIccandle
:chart-widget="chartWidget"
:submit-callback="handleSubmit"
:theme="theme"
:language="language"
:iframe-loaded="iframeLoaded"
:result-iframe-ref="resultIframeBox"
>
<!-- Bootstrap TradingView here and assign chartWidget when ready -->
<div id="tv_chart_container" style="height: 100%" />
</WidgetIccandle>
</div>
<iframe
ref="resultIframeRef"
:src="iframeSrc"
class="iccandle-selector-widget__iframe"
title="ICCandle results"
@load="handleIframeLoad"
/>
</div>
</template>
Replace the chart placeholder with your TradingView initialization and pass the IChartingLibraryWidget instance when the widget is created (typically right after new widget(options)).
Usage
1. Host the Charting Library
Copy the TradingView Charting Library build into a path your app can serve as static files (for example public/charting_library/ in Vite). Point library_path at that URL. The library is not bundled inside @iccandle/vuejs-widget.
2. Bootstrap TradingView with replay support
Wrap your datafeed with withPlayChart so the widget can inject predicted bars during replay. Register getCustomIndicators so those bars render as generated-candle studies:
import { onMounted, onBeforeUnmount, ref, shallowRef } from "vue";
import type {
ChartingLibraryWidgetOptions,
IChartingLibraryWidget,
ResolutionString,
} from "charting_library/charting_library";
import { widget } from "charting_library/charting_library";
import { withPlayChart, getCustomIndicators } from "@iccandle/vuejs-widget";
const LIBRARY_PATH = "/charting_library/";
const containerRef = ref<HTMLDivElement | null>(null);
const chartWidget = shallowRef<IChartingLibraryWidget | null>(null);
onMounted(() => {
const el = containerRef.value;
if (!el) return;
const options: ChartingLibraryWidgetOptions = {
container: el,
library_path: LIBRARY_PATH,
symbol: "EURUSD",
interval: "60" as ResolutionString,
datafeed: withPlayChart(yourDatafeed),
locale: "en",
autosize: true,
drawings_access: {
type: "black",
tools: [{ name: "Date Range" }],
},
custom_indicators_getter: () => getCustomIndicators("dark"),
};
const tv = new widget(options);
chartWidget.value = tv;
onBeforeUnmount(() => {
try {
tv.remove();
} catch {
/* no-op */
}
chartWidget.value = null;
});
});
withPlayChart also accepts a factory plus its arguments: withPlayChart(createDatafeed, arg1, arg2).
You must supply a valid datafeed, symbol, interval, locale, and any other options required by your TradingView license.
3. Wrap the chart with WidgetIccandle
The component must wrap the same subtree that contains the chart container so the scanner overlay positions correctly. Pass null for chartWidget until the instance is ready.
<WidgetIccandle
:chart-widget="chartWidget"
:submit-callback="handleSubmit"
theme="dark"
language="en"
:iframe-loaded="iframeLoaded"
:result-iframe-ref="resultIframeBox"
:available-intervals="['1', '5', '15', '30', '60']"
>
<div ref="containerRef" style="height: 100%; min-height: 400px" />
</WidgetIccandle>
4. Handle the results iframe URL
After a successful scan, submitCallback receives a full HTTPS URL for the ICCandle embed. Typical query parameters include symbol, reference resolution, scan resolution, candle cache id (cid), time window (from / to), theme, and optional filters (fs, period, model, temp).
Split pane (recommended) — keep an iframe beside the chart and update its src:
const submitCallback = (nextSrc: string) => {
iframeSrc.value = nextSrc;
};
Open in a new tab
submitCallback: (iframeSrc) => {
window.open(iframeSrc, "_blank", "noopener,noreferrer");
};
On iframe load, call handleResultIframeLoad(iframe) so the embed knows the parent origin (required for sign-in to write iccandle_token) and so a Stripe return of ?payment=success can refresh the subscription without a full reload.
result-iframe-ref is a mutable { current } box, not a Vue ref. Sync it from the iframe element:
const resultIframeRef = ref<HTMLIFrameElement | null>(null);
const resultIframeBox = { current: null as HTMLIFrameElement | null };
watch(resultIframeRef, (el) => {
resultIframeBox.current = el;
}, { immediate: true });
Sign-in
Scan and Pattern Tracker require localStorage.iccandle_token. The results iframe posts auth.signIn / auth.signOut; the widget listens and stores or clears the token. Until the user signs in, those actions show a login prompt.
Theme
theme |
Behavior |
|---|---|
"light" (default) |
Light scanner chrome and embed theme. |
"dark" |
Dark scanner chrome; adds .iccandle-dark on the root. |
"system" |
Follows prefers-color-scheme. |
The root uses CSS custom properties you can override: --iccandle-primary, --iccandle-border, --iccandle-text, --iccandle-background, --iccandle-primary-gradient-end, --iccandle-font. --iccandle-secondary is derived from the background.
Plugin Portal style (apiKey)
Clients manage their style in the ICCandle Plugin Portal and create an API key there. Pass it to the widget:
<WidgetIccandle api-key="<KEY>" :chart-widget="chartWidget" :submit-callback="handleSubmit">
<!-- chart -->
</WidgetIccandle>
- The widget calls
GET https://api.iccandle.ai/plugin-client/v1/plugin/stylewith theapi-keyheader once per key. The result is cached in memory and refetched only whenapiKeychanges (not on theme change). The request times out after 5 s and is aborted on unmount. - The key is also forwarded to every results-iframe URL as
&apiKey=, so the embed app uses the same style. - The key is publishable: it only reads style data and is meant to ship in client-side code. Restrict it in the portal with allowed origins (
scheme://host[:port], e.g.https://app.example.com). With no allowed origins any site can use the key (the portal warns about this). Requests from other origins get403and the widget falls back to the defaults.
Supported style fields, per theme (light and dark):
| Field | CSS variable | Accepted values |
|---|---|---|
iccandle_primary |
--iccandle-primary |
#rgb, #rgba, #rrggbb, #rrggbbaa, rgb(r, g, b), rgba(r, g, b, a) |
iccandle_primary_gradient_end |
--iccandle-primary-gradient-end |
same as above |
iccandle_background |
--iccandle-background |
same as above |
iccandle_border |
--iccandle-border |
same as above |
iccandle_text |
--iccandle-text |
same as above |
icc_font |
--iccandle-font |
Inter Tight, Inter, Roboto, Open Sans, Lato, Montserrat, Poppins, Nunito, Source Sans 3, IBM Plex Sans, system-ui |
The portal also sets default_theme (light | dark). The widget re-validates every value and ignores anything outside the formats above. Non-system fonts are loaded once from Google Fonts.
Priority (highest first):
- Theme:
themeprop → portaldefault_theme→light. - Colors: portal style → bundled defaults.
Fallback: with no apiKey, an invalid or revoked key, a disallowed origin, rate limiting, no saved style, or a network error, the widget uses the bundled defaults. The chart keeps working and nothing is thrown.
Chart mask colors
During replay the widget draws all predicted candles at once and hides the ones that have not been revealed yet by painting them in a solid "mask" color. The illusion only works if that color is identical to the chart pane background, otherwise the hidden bars show up as visible blocks ahead of the replay cursor.
Defaults are #ffffff for light and #0F0F0F for dark. If your TradingView paneProperties.background differs, pass chartMaskColors with the matching value:
<WidgetIccandle
:chart-widget="chartWidget"
:submit-callback="handleSubmit"
theme="dark"
:chart-mask-colors="{ dark: '#2F2F2F', light: '#ffffff' }"
>
<div ref="containerRef" style="height: 100%" />
</WidgetIccandle>
Only the entry for the active theme is used, and each key is optional — omit one to keep its default. Any CSS color string the charting library accepts works, but avoid transparency: a translucent mask lets the real series bleed through. Changing the prop recreates the generated-candle studies, so it can be updated reactively when the host switches themes.
Language
language accepts "en" | "zh" | "vi" | "th" | "ko" | "ja" | "mn" | "ru". Unknown values fall back to "en". The value is used for scanner copy and is forwarded into embed URLs.
Correlated markets panel
When the embed sends a chart.play message whose data.correlation holds a correlation payload (root symbol plus simulations[0].symbols), the scanner popup expands under its toolbar once the simulated candles finish drawing. The panel lists the first four markets, with a "Show all" toggle for the rest. Each row shows:
- the correlation coefficient
ρ(hover for a sentence such as "EUR/USD 82% correlated with XAU/USD"), - an
inversetag when the market moves against the root symbol, - the expected % move.
No host configuration is needed. The panel hides itself when replay stops, the result is closed, or the payload has no symbols. It switches to a compact layout when the popup is narrow.
Layout helpers
Injected CSS includes split-pane classes used in the demo:
| Class | Role |
|---|---|
.iccandle-selector-widget__container |
Flex row (stacks below 1024px). --chart-pane-size defaults to 60%. |
.iccandle-selector-widget__chart-pane |
Chart column. |
.iccandle-selector-widget__resize-handle |
Drag handle; add --stacked for the stacked layout. |
.iccandle-selector-widget__iframe |
Results pane. |
Full working example
See src/App.vue, src/tradingview/TradingviewChart.vue, and src/tradingview/TradingviewChartContainer.vue in this repository.
API
Exports
| Name | Kind | Description |
|---|---|---|
WidgetIccandle |
Component | Scanner overlay around your chart subtree. |
WidgetIccandleProps |
Type | Public props of WidgetIccandle. |
WidgetChartMaskColors |
Type | { light?: string; dark?: string } for the chartMaskColors prop. |
WidgetLanguage |
Type | Supported UI languages. |
withPlayChart |
Function | Wrap a TradingView datafeed (or factory) so replay can inject bars and block live ticks. |
getCustomIndicators |
Function | Returns custom studies used to draw generated / predicted candles. Pass "light" or "dark". |
GeneratedCandlesTheme |
Type | "light" | "dark" for getCustomIndicators. |
handleResultIframeLoad |
Function | Post parent origin (and optional payment-success) to the results iframe. |
WIDGET_RESULT_URL |
Constant | Default embed origin: https://embed-iccandle-app.iccandle.ai. |
OPEN_PRICING_MESSAGE_TYPE |
Constant | "open-pricing" postMessage type. |
postOpenPricingToEmbed |
Function | Ask the results iframe to open pricing. Returns false if no contentWindow. |
postOpenPricingToParent |
Function | Forward pricing to window.parent when this widget is itself embedded. |
postOpenPricingToWindow |
Function | Post an open-pricing message to an arbitrary Window. |
OpenPricingMessage |
Type | { type, theme?, language? }. |
WidgetIccandle props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
chartWidget |
IChartingLibraryWidget | null |
Yes | — | Live TradingView widget (null until ready). |
submitCallback |
(iframeSrc: string) => void |
Yes | — | Called with the embed URL after a scan (and for some pricing / news navigations). |
theme |
"light" | "dark" | "system" |
No | portal default_theme, else "light" |
Scanner and forwarded embed theme. |
apiKey |
string |
No | — | Publishable Plugin Portal key (pk_live_…). Loads the client's portal style and forwards &apiKey= to the results iframe. See Plugin Portal style. |
chartMaskColors |
WidgetChartMaskColors |
No | { light: "#ffffff", dark: "#0F0F0F" } |
Per-theme color used to mask not-yet-revealed predicted candles. Must match your chart pane background. |
language |
WidgetLanguage |
No | "en" |
Scanner UI language. |
onCloseResult |
() => void |
No | — | Called when the embed posts selector.closeResult. |
iframeLoaded |
boolean |
No | true |
When false, Scan and Pattern Tracker are disabled. Set true after the results iframe has loaded. |
resultIframeRef |
{ current: HTMLIFrameElement | null } | null |
No | — | Results iframe box. Receives open-pricing and chart-resolution postMessages. |
isShowScanButton |
boolean |
No | true |
Show Scan inside the popup. |
isShowTrackerButton |
boolean |
No | true |
Show Pattern Tracker inside the popup. |
isShowScannerPopup |
boolean |
No | true |
Show the scanner popup. |
availableIntervals |
string[] |
No | — | Resolutions allowed when drawing a pattern date range from embed selection. |
onScanClick |
() => void |
No | — | Fired when the user clicks Scan, before login / config / scan logic. |
Default slot receives { chartRefs } (highlightBarsRef, generated-candle study ids). The same object is exposed on the component instance as chartRefs.
Behavior
- Subscribes to chart readiness, resolution, symbol, visible range, drawings, and timescale-mark clicks.
- Draws a
date_rangeshape for the scan window; bar count and exported candles drive the scanner. - Posts selected candles to ICCandle’s cache, then calls
submitCallbackwith the embed URL. - Listens for
windowmessageevents from the embed (replay, auth, news, pattern selection, navigation, loading). - Clears persisted news mark keys (
tv:selected-news-events,tv:clicked-news-event) when a scan starts.
Embed postMessage names
The widget handles JSON messages with a name (and optional data) from the results iframe:
| Name | Effect |
|---|---|
chart.play |
Replay / predicted candles on the chart. Optional data.correlation feeds the correlated markets panel. |
chart.stop |
Stop replay and restore live ticks. |
chart.requestResolution |
Re-post the current chart resolution to the iframe. |
selector.closeResult |
Invoke onCloseResult. |
selector.loading |
Toggle scanner loading state. |
auth.signIn |
Store data.idToken as iccandle_token. |
auth.signOut |
Remove iccandle_token. |
news.eventClicked |
Draw event marks / target window on the chart. |
news.back / news.backToSimilarEvents |
Clear event overlay / replay. |
pattern.classicPatternSelected / pattern.custom_pattern_selected |
Draw the compared pattern’s date range. |
pattern.clearClassicPatternSelected / pattern.clear_custom_pattern_selected |
Clear pattern overlay. |
nav.click |
Handle embed navigation (clears date range on /news routes). |
The host → embed helpers use a different shape: { type: "parent-origin" | "payment-success" | "open-pricing" | "chart-resolution", ... }.
Optional: timescale marks (news / events)
If your datafeed implements getTimescaleMarks, you can surface stored events from localStorage (tv:selected-news-events, tv:clicked-news-event) as marks on the time axis. See src/lib/data-feed.ts in this repo.
Troubleshooting
| Symptom | Check |
|---|---|
| Scanner popup never appears | chartWidget is still null, isShowScannerPopup is false, or the current resolution is not in availableIntervals. |
| Scan / Pattern Tracker are greyed out | iframeLoaded is false or a scan is still loading. Set iframeLoaded to true from the iframe's load event. |
| "Please log in first" on every click | localStorage.iccandle_token is missing. Make sure handleResultIframeLoad(iframe) runs on load so the embed can post auth.signIn. |
| Hidden predicted candles show up as blocks | chartMaskColors does not match your chart pane background. See Chart mask colors. |
| Messages from the embed are ignored | The widget only accepts postMessage from WIDGET_RESULT_URL. Don't proxy the embed through another origin. |
Changelog
0.4.0
- Breaking: removed the
bg_dark/bg_lightprops. Use a Plugin Portal style viaapiKeyinstead. - New
apiKeyprop loads the client's Plugin Portal style (colors, font, default theme) and forwards&apiKey=to the results iframe. - Without an explicit
theme, the portal'sdefault_themeis used, thenlight. - Buttons and inputs inherit the widget font.
0.3.4
- Correlated markets panel under the scanner popup during AI simulation replay.
- Simulated bars past the last real candle stay on the chart's resolution grid and skip closed sessions.
- Removed the paid custom window-size dropdown from the popup. The toolbar now uses equal-width controls.
- Tooltips are positioned in the viewport, so they no longer get clipped by the popup or chart.
- Scanner symbol selection ignores tickers that are not in the catalog and keeps a stable order.
- Symbol search trigger has a fixed height, and chips no longer wrap.
Development (this repo)
| Script | Command | Purpose |
|---|---|---|
| Dev demo | pnpm dev |
Vite app with a local charting library. Copy .env.example to .env for demo sign-in. |
| Library build | pnpm build |
Emits dist/ (ESM, CJS, injected CSS, declarations). |
| App build | pnpm build:app |
Full demo app build. |
prepublishOnly runs build before publish.
License
MIT. TradingView Charting Library is subject to its own license from TradingView.
Related Packages
Version History
| Version | Released | License |
|---|---|---|
| 0.4.0 | MIT | |
| 0.3.4 | MIT | |
| 0.3.3 | MIT | |
| 0.3.2 | MIT | |
| 0.3.1 | MIT | |
| 0.3.0 | MIT | |
| 0.2.8 | MIT | |
| 0.2.7 | MIT | |
| 0.2.6 | MIT | |
| 0.2.5 | MIT | |
| 0.2.4 | MIT | |
| 0.2.3 | MIT | |
| 0.2.2 | MIT | |
| 0.2.1 | MIT | |
| 0.2.0 | MIT | |
| 0.1.5 | MIT | |
| 0.1.4 | MIT | |
| 0.1.3 | MIT | |
| 0.1.2 | MIT | |
| 0.1.1 | MIT | |
| 0.1.0 | MIT | |
| 0.0.8 | MIT | |
| 0.0.7 | MIT | |
| 0.0.6 | MIT | |
| 0.0.5 | MIT | |
| 0.0.4 | MIT | |
| 0.0.3 | MIT | |
| 0.0.2 | MIT | |
| 0.0.1 | MIT |