@funnest/charts-ui 0.0.0-stage → 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,3005 @@
1
+ // src/trading-chart.tsx
2
+ import {
3
+ createChart,
4
+ darkTheme,
5
+ lightTheme
6
+ } from "@funnest/charts";
7
+
8
+ // ../../node_modules/.pnpm/preact@11.2.0/node_modules/preact/dist/preact.mjs
9
+ var n;
10
+ var t;
11
+ var r;
12
+ var u;
13
+ var i;
14
+ var f;
15
+ var o;
16
+ var e;
17
+ var l;
18
+ var c;
19
+ var a;
20
+ var s;
21
+ var h = /* @__PURE__ */ Symbol.for("pv");
22
+ var p = {};
23
+ var y = [];
24
+ var v = /^m(i|n|o|s|text|space)$/;
25
+ var w = Array.isArray;
26
+ var d = y.slice;
27
+ var _ = Object.assign;
28
+ function g(n2) {
29
+ n2 && n2.parentNode && n2.remove();
30
+ }
31
+ function m(r3, u3, i3, f4, o4) {
32
+ var e3 = { type: r3, props: u3, key: i3, ref: f4, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: h, __v: o4 || ++t, __i: -1, __u: 0 };
33
+ return !o4 && n.vnode && n.vnode(e3), e3;
34
+ }
35
+ function $(n2) {
36
+ return n2.children;
37
+ }
38
+ function x(n2, t3) {
39
+ this.props = n2, this.context = t3, this.__g = 0;
40
+ }
41
+ function S(n2, t3) {
42
+ if (null == t3) return n2.__ && S(n2.__, n2.__i + 1);
43
+ for (var r3; t3 < n2.__k.length; t3++) if ((r3 = n2.__k[t3]) && r3.__e) return r3.__e;
44
+ return "function" != typeof n2.type || n2.props.__P ? null : S(n2);
45
+ }
46
+ function C(t3) {
47
+ var r3, u3 = t3.__v, i3 = u3.__e, f4 = [], o4 = [], e3 = t3.__P;
48
+ e3 && ((r3 = _({ constructor: h }, u3)).__v = u3.__v + 1, n.vnode && n.vnode(r3), z(e3, r3, u3, t3.__n, e3.namespaceURI, 32 & u3.__u ? [i3] : null, f4, i3 || S(u3), 32 & u3.__u, o4), r3.__v = u3.__v, r3.__.__k[r3.__i] = r3, E(f4, r3, o4), u3.__ = u3.__e = null, r3.__e != i3 && j(r3));
49
+ }
50
+ function j(n2) {
51
+ if ((n2 = n2.__) && n2.__c && !n2.props.__P) return n2.__e = null, n2.__k.some(function(t3) {
52
+ return t3 && (n2.__e = t3.__e);
53
+ }), j(n2);
54
+ }
55
+ function L(t3) {
56
+ (8 & t3.__g || (t3.__g |= 8, u.push(t3), f++)) && i == n.debounceRendering || ((i = n.debounceRendering) || queueMicrotask)(H);
57
+ }
58
+ function H() {
59
+ try {
60
+ for (var n2, t3 = 1; u.length; ) u.length > t3 && u.sort(o), n2 = u.shift(), t3 = u.length, 8 & n2.__g && C(n2);
61
+ } finally {
62
+ (f = u.length) && (i || queueMicrotask)(H);
63
+ }
64
+ }
65
+ function I(n2, t3, r3, u3, i3, f4, o4, e3, l3, c3, a4) {
66
+ var s3, h3, v3, w3, d3, _3, g3 = u3.__k || y, k2 = t3.length;
67
+ for (l3 = q(r3, t3, g3, l3, k2), s3 = 0; s3 < k2; s3++) null != (v3 = r3.__k[s3]) && (h3 = ~v3.__i && g3[v3.__i] || p, v3.__i = s3, _3 = z(n2, v3, h3, i3, f4, o4, e3, l3, c3, a4), w3 = v3.__e, (h3.ref != v3.ref || 8 & h3.__u) && (h3.ref && G(h3.ref, null, v3, h3), v3.ref && a4.push(v3.ref, v3.__c || w3, v3)), d3 = d3 || w3, 4 & v3.__u ? (l3 = A(v3, l3, n2, !h3.__v), h3.__e && (h3.__e = null)) : "function" == typeof v3.type && void 0 !== _3 ? l3 = _3 : w3 && (l3 = w3.nextSibling), v3.__u &= -21);
68
+ return r3.__e = d3, l3;
69
+ }
70
+ function q(n2, t3, r3, u3, i3) {
71
+ var f4, o4, e3, l3, c3, a4, s3, p3, y3, v3 = r3.length, d3 = v3, _3 = 0, g3 = 0, k2 = n2.__k = Array(i3);
72
+ for (f4 = 0; f4 < i3; f4++) null != (o4 = t3[f4]) && "boolean" != typeof o4 && "function" != typeof o4 ? ("object" != typeof o4 || o4.constructor === String ? o4 = k2[f4] = m(null, o4) : w(o4) ? o4 = k2[f4] = m($, { children: o4 }) : o4.constructor === h && o4.__b ? o4 = k2[f4] = m(o4.type, o4.props, o4.key, o4.ref, o4.__v) : k2[f4] = o4, l3 = f4 + _3, o4.__ = n2, o4.__b = n2.__b + 1, e3 = null, ~(c3 = o4.__i = P(o4, r3, l3, d3)) && (d3--, (e3 = r3[c3]) && (e3.__u |= 2)), e3 && e3.__v ? (o4.__u |= 16, c3 == l3 - 1 ? _3-- : c3 == l3 + 1 ? _3++ : c3 != l3 && (c3 > l3 ? _3-- : _3++, g3 = 1)) : (~c3 || (i3 > v3 ? _3-- : i3 < v3 && _3++), "function" != typeof o4.type && (o4.__u |= 4))) : k2[f4] = null;
73
+ if (g3) {
74
+ for (a4 = [], s3 = [], f4 = 0; f4 < i3; f4++) if ((o4 = k2[f4]) && 16 & o4.__u) {
75
+ for (_3 = 0, p3 = a4.length; _3 < p3; ) a4[y3 = _3 + p3 >> 1] < o4.__i ? _3 = y3 + 1 : p3 = y3;
76
+ a4[_3] = o4.__i, s3[f4] = _3 + 1;
77
+ }
78
+ for (_3 = a4.length; f4--; ) s3[f4] && (s3[f4] == _3 ? _3-- : k2[f4].__u |= 4);
79
+ }
80
+ if (d3) for (f4 = 0; f4 < v3; f4++) !(e3 = r3[f4]) || 2 & e3.__u || (e3.__e == u3 && (u3 = S(e3)), J(e3, e3));
81
+ return u3;
82
+ }
83
+ function A(n2, t3, r3, u3) {
84
+ var i3, f4, o4, e3;
85
+ if ("function" == typeof n2.type) {
86
+ if (n2.props.__P) return t3;
87
+ if (i3 = n2.__k) for (f4 = 0; f4 < i3.length; f4++) i3[f4] && (i3[f4].__ = n2, t3 = A(i3[f4], t3, r3, false));
88
+ return t3;
89
+ }
90
+ for (t3 && !t3.parentNode && (t3 = S(n2)) && !t3.parentNode && (t3 = null), o4 = n2.__e, e3 = t3; e3 && 8 == e3.nodeType; ) e3 = e3.nextSibling;
91
+ for (o4 != e3 && (!u3 && r3.moveBefore && o4.parentNode ? r3.moveBefore(o4, t3) : r3.insertBefore(o4, t3 || null)), t3 = o4; (t3 = t3 && t3.nextSibling) && 8 == t3.nodeType && !t3.data.startsWith("$s"); ) ;
92
+ return t3;
93
+ }
94
+ function P(n2, t3, r3, u3) {
95
+ var i3, f4 = n2.key, o4 = n2.type, e3 = t3[r3], l3 = e3 && !(2 & e3.__u);
96
+ if (null === e3 && null == f4 || l3 && f4 == e3.key && o4 == e3.type) return r3;
97
+ if (u3 > (l3 ? 1 : 0)) {
98
+ for (i3 = r3 - 1; i3 >= 0; i3--) if ((e3 = t3[i3]) && !(2 & e3.__u) && f4 == e3.key && o4 == e3.type) return i3;
99
+ for (i3 = r3 + 1; i3 < t3.length; i3++) if ((e3 = t3[i3]) && !(2 & e3.__u) && f4 == e3.key && o4 == e3.type) return i3;
100
+ }
101
+ return -1;
102
+ }
103
+ function T(n2, t3, r3) {
104
+ null == r3 && (r3 = ""), "-" == t3[0] ? n2.setProperty(t3, r3) : n2[t3] = r3;
105
+ }
106
+ function N(n2, t3, r3, u3, i3) {
107
+ var f4, o4;
108
+ if ("style" == t3) if ("string" == typeof r3) n2.style.cssText = r3;
109
+ else {
110
+ if ("string" == typeof u3 && (n2.style.cssText = u3 = ""), u3) for (t3 in u3) r3 && t3 in r3 || T(n2.style, t3);
111
+ if (r3) for (t3 in r3) u3 && r3[t3] == u3[t3] || T(n2.style, t3, r3[t3]);
112
+ }
113
+ else if ("o" == t3[0] && "n" == t3[1]) "function" != typeof r3 && (r3 = null), "function" != typeof u3 && (u3 = null), (n2.__e || (n2.__e = {}))[t3] = r3, r3 && u3 || (o4 = a[t3] || (a[t3] = V(t3)), (n2.__a || (n2.__a = {}))[t3] = c, f4 = t3 != (t3 = t3.replace(l, "$1")), (t3 = t3.slice(2))[0] < "a" && (t3 = t3.toLowerCase()), r3 ? n2.addEventListener(t3, o4, f4) : n2.removeEventListener(t3, o4, f4));
114
+ else {
115
+ if ("http://www.w3.org/2000/svg" == i3) t3 = t3.replace(/xlink(H|:h)/, "h").replace(/sName$/, "s");
116
+ else if ("width" != t3 && "height" != t3 && "href" != t3 && "list" != t3 && "form" != t3 && "tabIndex" != t3 && "download" != t3 && "rowSpan" != t3 && "colSpan" != t3 && "role" != t3 && "popover" != t3 && t3 in n2) try {
117
+ return void (n2[t3] = null == r3 ? "" : r3);
118
+ } catch (n3) {
119
+ }
120
+ if ("function" == typeof r3) return;
121
+ null == r3 || false === r3 && "-" != t3[4] ? n2.removeAttribute(t3) : n2.setAttribute(t3, "popover" == t3 && 1 == r3 ? "" : r3);
122
+ }
123
+ }
124
+ function V(t3) {
125
+ return function(r3) {
126
+ var u3 = this.__e;
127
+ if (u3) {
128
+ if (u3 = u3[t3], null == r3[e]) r3[e] = c++;
129
+ else if (r3[e] < this.__a[t3]) return;
130
+ return u3(n.event ? n.event(r3) : r3);
131
+ }
132
+ };
133
+ }
134
+ function z(t3, r3, u3, i3, f4, o4, e3, l3, c3, a4) {
135
+ var s3, p3, v3, d3, k2, b, m3, M, C3, j2, L2, H2, q2, A3, O, P2, T3, N2, V2 = r3.type;
136
+ if (r3.constructor !== h) return null;
137
+ 128 & u3.__u && (c3 = 32 & u3.__u, s3 = u3.__c.__z) && (r3.__u |= c3, p3 = o4 = [], 8 == s3.nodeType ? l3 = B(s3, o4) : o4.push(s3), l3 = o4[0] || l3), (s3 = n.__b) && s3(r3);
138
+ n: if ("function" == typeof V2) {
139
+ v3 = e3.length;
140
+ try {
141
+ if (M = r3.props, C3 = (s3 = V2.prototype) && s3.render, j2 = (s3 = V2.contextType) && i3[s3.__c], L2 = s3 ? j2 ? j2.props.value : s3.__ : i3, (d3 = u3.__c) ? (r3.__c = d3, 2 & d3.__g && (d3.__g |= 1)) : (C3 ? r3.__c = d3 = new V2(M, L2) : (r3.__c = d3 = new x(M, L2), d3.constructor = V2, d3.render = K), j2 && j2.sub(d3), d3.state || (d3.state = {}), d3.__n = i3, d3.__g |= 8, d3.__h = [], d3.__k = []), C3 && (d3.__s || (d3.__s = d3.state), V2.getDerivedStateFromProps && (d3.__s == d3.state && (d3.__s = _({}, d3.__s)), _(d3.__s, V2.getDerivedStateFromProps(M, d3.__s)))), k2 = d3.props, b = d3.state, d3.__v = r3, u3.__c) {
142
+ if (M !== k2 && C3 && !V2.getDerivedStateFromProps && d3.componentWillReceiveProps && d3.componentWillReceiveProps(M, L2), r3.__v == u3.__v && !(8 & d3.__g) || !(4 & d3.__g) && d3.shouldComponentUpdate && false === d3.shouldComponentUpdate(M, d3.__s, L2)) {
143
+ r3.__v != u3.__v && (d3.props = M, d3.state = d3.__s, d3.__g &= -9), r3.__e = u3.__e, r3.__k = u3.__k, r3.__k.some(function(n2) {
144
+ n2 && (n2.__ = r3);
145
+ }), y.push.apply(d3.__h, d3.__k), d3.__k = [], d3.__h.length && e3.push(d3), l3 = S(u3);
146
+ break n;
147
+ }
148
+ d3.componentWillUpdate && d3.componentWillUpdate(M, d3.__s, L2), C3 && d3.componentDidUpdate && d3.__h.push(function() {
149
+ d3.componentDidUpdate(k2, b, m3);
150
+ });
151
+ } else C3 && !V2.getDerivedStateFromProps && d3.componentWillMount && d3.componentWillMount(), C3 && d3.componentDidMount && d3.__h.push(d3.componentDidMount);
152
+ d3.context = L2, d3.props = M, d3.__P = t3, d3.__g &= -5, H2 = n.__r, q2 = C3 ? 25 : 0;
153
+ do {
154
+ d3.__g &= -9, H2 && H2(r3), s3 = d3.render(d3.props, d3.state = d3.__s, d3.context);
155
+ } while (8 & d3.__g && ++q2 < 25);
156
+ C3 && (y.push.apply(d3.__h, d3.__k), d3.__k = []), d3.state = d3.__s, d3.getChildContext && (i3 = _({}, i3, d3.getChildContext())), C3 && u3.__c && d3.getSnapshotBeforeUpdate && (m3 = d3.getSnapshotBeforeUpdate(k2, b)), A3 = s3 && s3.type === $ && null == s3.key ? s3.props.children : s3, M.__P && (s3 = l3, f4 = (t3 = M.__P).namespaceURI, c3 = o4 = null, u3.props && u3.props.__P != t3 && (u3.__k.some(function(n2) {
157
+ n2 && J(n2, n2);
158
+ }), u3.__k = null), l3 = u3.__k ? S(u3, 0) : null), l3 = I(t3, w(A3) ? A3 : [A3], r3, u3, i3, f4, o4, e3, l3, c3, a4), M.__P && (r3.__e = null, l3 = s3), r3.__u &= -161, p3 && p3.some(g), d3.__h.length && e3.push(d3), 1 & d3.__g && (d3.__g &= -4);
159
+ } catch (t4) {
160
+ if (e3.length = v3, r3.__v = null, c3 || o4) if (t4 && t4.then) {
161
+ if (r3.__u |= 128 | c3, o4) {
162
+ for (~(T3 = o4.indexOf(l3 || void 0)) || (T3 = o4.length); (N2 = o4[T3 - 1]) && 8 == N2.nodeType; ) T3--;
163
+ for (; T3 < o4.length; T3++) if (N2 = o4[T3]) if (o4[T3] = null, O) {
164
+ if (N2 == P2) break;
165
+ } else {
166
+ if (8 != N2.nodeType) break;
167
+ N2.data.startsWith("$s") && (P2 = B(O = N2));
168
+ }
169
+ }
170
+ if (!O) {
171
+ for (; l3 && 8 == l3.nodeType && l3.nextSibling; ) l3 = l3.nextSibling;
172
+ O = l3;
173
+ }
174
+ (s3 = B(O = r3.__c.__z || (r3.__c.__z = O))) ? (r3.__e = O, l3 = s3) : r3.__e = l3, l3 = l3 && l3.nextSibling;
175
+ } else o4 && o4.some(g);
176
+ else r3.__e = u3.__e;
177
+ r3.__k || (r3.__k = u3.__k || []), t4 && t4.then || D(r3), n.__e(t4, r3, u3);
178
+ }
179
+ } else l3 = r3.__e = F(u3.__e, r3, u3, i3, f4, o4, e3, c3, a4, t3);
180
+ return (s3 = n.diffed) && s3(r3), l3;
181
+ }
182
+ function B(n2, t3) {
183
+ if (n2 && 8 == n2.nodeType && n2.data.startsWith("$s")) for (var r3 = 1; n2 = n2.nextSibling; ) {
184
+ if (8 == n2.nodeType) {
185
+ if (n2.data.startsWith("$s")) r3++;
186
+ else if (n2.data.startsWith("/$s") && !--r3) return n2;
187
+ }
188
+ t3 && t3.push(n2);
189
+ }
190
+ }
191
+ function D(n2) {
192
+ n2 && (n2.__c && (n2.__c.__g |= 4), n2.__k && n2.__k.some(D));
193
+ }
194
+ function E(t3, r3, u3) {
195
+ for (var i3 = 0; i3 < u3.length; ) G(u3[i3++], u3[i3++], u3[i3++]);
196
+ n.__c && n.__c(r3, t3), t3.some(function(r4) {
197
+ try {
198
+ t3 = r4.__h, r4.__h = [], t3.some(function(n2) {
199
+ n2.call(r4);
200
+ });
201
+ } catch (t4) {
202
+ n.__e(t4, r4.__v);
203
+ }
204
+ });
205
+ }
206
+ function F(t3, r3, u3, i3, f4, o4, e3, l3, c3, a4) {
207
+ var s3, h3, y3, _3, k2, b, m3, M, $2 = u3.props || p, x2 = r3.props, C3 = r3.type;
208
+ if ("svg" == C3 ? f4 = "http://www.w3.org/2000/svg" : "math" == C3 ? f4 = "http://www.w3.org/1998/Math/MathML" : f4 || (f4 = "http://www.w3.org/1999/xhtml"), o4) {
209
+ for (s3 = 0; s3 < o4.length; s3++) if ((k2 = o4[s3]) && (C3 ? k2.localName == C3 : 3 == k2.nodeType)) {
210
+ t3 = k2, o4[s3] = null;
211
+ break;
212
+ }
213
+ }
214
+ if (!t3) {
215
+ if (M = a4.ownerDocument || document, !C3) return M.createTextNode(x2);
216
+ t3 = M.createElementNS(f4, C3, x2.is ? x2 : a4), l3 && (n.__m && n.__m(r3, o4), l3 = false), o4 = null;
217
+ }
218
+ if (C3) {
219
+ if (a4 = "template" == C3 && t3.content || t3, o4 = "textarea" == C3 && null != x2.defaultValue ? null : o4 && d.call(a4.childNodes), !l3 && o4) for ($2 = {}, s3 = 0; s3 < t3.attributes.length; s3++) $2[(k2 = t3.attributes[s3]).name] = k2.value;
220
+ for (s3 in $2) k2 = $2[s3], "dangerouslySetInnerHTML" == s3 ? y3 = k2 : "children" == s3 || s3 in x2 || "value" == s3 && "defaultValue" in x2 || "checked" == s3 && "defaultChecked" in x2 || N(t3, s3, null, k2, f4);
221
+ for (s3 in x2) k2 = x2[s3], "children" == s3 ? _3 = k2 : "dangerouslySetInnerHTML" == s3 ? h3 = k2 : "value" == s3 ? b = k2 : "checked" == s3 ? m3 = k2 : l3 && "function" != typeof k2 || $2[s3] === k2 || N(t3, s3, k2, $2[s3], f4);
222
+ h3 ? (l3 || y3 && (h3.__html == y3.__html || h3.__html == t3.innerHTML) || (t3.innerHTML = h3.__html), r3.__k = []) : (y3 && (t3.textContent = ""), ("foreignObject" == C3 || "http://www.w3.org/1998/Math/MathML" == f4 && v.test(C3)) && (f4 = "http://www.w3.org/1999/xhtml"), I(a4, w(_3) ? _3 : [_3], r3, u3, i3, f4, o4, e3, o4 ? o4[0] : u3.__k && S(u3, 0), l3, c3), o4 && o4.some(g)), l3 && "textarea" != C3 || (s3 = "value", "progress" == C3 && null == b ? t3.removeAttribute(s3) : null == b || b === t3[s3] && ("progress" != C3 || b) || N(t3, s3, b, $2[s3], f4), s3 = "checked", null != m3 && m3 != t3[s3] && N(t3, s3, m3, $2[s3], f4));
223
+ } else $2 === x2 || l3 && t3.data == x2 || (t3.data = x2);
224
+ return t3;
225
+ }
226
+ function G(t3, r3, u3, i3) {
227
+ try {
228
+ "function" == typeof t3 ? r3 ? r3.__x = t3(r3) || 1 : i3 && (i3 = i3.__c || i3.__e) && (r3 = i3.__x) && (i3.__x = null, "function" == typeof r3 ? r3() : t3(null)) : t3.current = r3;
229
+ } catch (t4) {
230
+ n.__e(t4, u3);
231
+ }
232
+ }
233
+ function J(t3, r3, u3) {
234
+ var i3, f4, o4, e3;
235
+ if (n.unmount && n.unmount(t3), !(i3 = t3.ref) || i3.current && i3.current != t3.__e || G(i3, null, r3, t3), i3 = t3.__c) {
236
+ if (i3.componentWillUnmount) try {
237
+ i3.componentWillUnmount();
238
+ } catch (t4) {
239
+ n.__e(t4, r3);
240
+ }
241
+ i3.__P = i3.__n = null;
242
+ }
243
+ if (i3 = t3.__k) for (e3 = 0; e3 < i3.length; e3++) i3[e3] && J(i3[e3], r3, "function" != typeof t3.type || u3 && !t3.props.__P);
244
+ !u3 && (i3 = t3.__c) && (i3 = i3.__z) && (f4 = B(i3, o4 = [])) && (i3 == t3.__e && o4.some(g), g(f4), g(i3)), u3 || g(t3.__e), t3.__e = t3.__c = t3.__ = null;
245
+ }
246
+ function K(n2, t3, r3) {
247
+ return this.constructor(n2, r3);
248
+ }
249
+ function Q(t3, r3) {
250
+ var u3, i3, f4, o4;
251
+ n.__ && n.__(t3, r3), 9 == r3.nodeType && (r3 = r3.documentElement), i3 = (u3 = t3 && 32 & t3.__u) ? null : r3.__k, r3.__k = m($, { children: [t3] }), f4 = [], o4 = [], z(r3, r3.__k, i3 || p, p, r3.namespaceURI, i3 ? null : r3.firstChild ? d.call(r3.childNodes) : null, f4, i3 ? i3.__e : r3.firstChild, u3, o4), E(f4, r3.__k, o4), r3.__k.props.children = null;
252
+ }
253
+ n = { __e: function(n2, t3, r3, u3) {
254
+ for (var i3, o4, e3; t3 = t3.__; ) if ((i3 = t3.__c) && !(1 & i3.__g)) {
255
+ i3.__g |= 4;
256
+ try {
257
+ if ((e3 = (o4 = i3.constructor) && o4.getDerivedStateFromError) && i3.setState(o4.getDerivedStateFromError(n2)), i3.componentDidCatch && (i3.componentDidCatch(n2, u3 || {}), e3 = 1), e3 && 8 & i3.__g) return void (i3.__g |= 2);
258
+ } catch (t4) {
259
+ n2 = t4;
260
+ }
261
+ }
262
+ throw f = 0, n2;
263
+ } }, t = 0, r = function(n2) {
264
+ return null != n2 && n2.constructor === h;
265
+ }, x.prototype.setState = function(n2, t3) {
266
+ var r3 = this.__s;
267
+ r3 && r3 != this.state || (r3 = this.__s = _({}, this.state)), "function" == typeof n2 && (n2 = n2(_({}, r3), this.props)), n2 && (_(r3, n2), this.__v && (t3 && this.__k.push(t3), L(this)));
268
+ }, x.prototype.forceUpdate = function(n2) {
269
+ this.__v && (this.__g |= 4, n2 && this.__h.push(n2), L(this));
270
+ }, x.prototype.render = $, u = [], f = 0, o = function(n2, t3) {
271
+ return n2.__v.__b - t3.__v.__b;
272
+ }, e = /* @__PURE__ */ Symbol(), l = /(PointerCapture)$|Capture$/i, c = 0, a = {}, s = 0;
273
+
274
+ // src/i18n.ts
275
+ var STRINGS = {
276
+ ko: {
277
+ searchSymbol: "\uC885\uBAA9 \uAC80\uC0C9",
278
+ searchPlaceholder: "\uC885\uBAA9\uBA85 \uB610\uB294 \uCF54\uB4DC \uAC80\uC0C9",
279
+ noResults: "\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC5B4\uC694",
280
+ close: "\uB2EB\uAE30",
281
+ zoomIn: "\uD655\uB300",
282
+ zoomOut: "\uCD95\uC18C",
283
+ scrollBack: "\uC774\uC804 \uAD6C\uAC04",
284
+ scrollForward: "\uB2E4\uC74C \uAD6C\uAC04",
285
+ resetView: "\uCD08\uAE30\uD654",
286
+ toRealtime: "\uCD5C\uC2E0\uC73C\uB85C \uC774\uB3D9",
287
+ screenshot: "\uC2A4\uD06C\uB9B0\uC0F7 \uC800\uC7A5",
288
+ fullscreen: "\uC804\uCCB4\uD654\uBA74",
289
+ exitFullscreen: "\uC804\uCCB4\uD654\uBA74 \uC885\uB8CC",
290
+ open: "\uC2DC",
291
+ high: "\uACE0",
292
+ low: "\uC800",
293
+ close_: "\uC885",
294
+ volume: "\uAC70\uB798\uB7C9",
295
+ chartType: "\uCC28\uD2B8 \uC885\uB958",
296
+ candles: "\uCE94\uB4E4",
297
+ line: "\uB77C\uC778",
298
+ area: "\uC601\uC5ED",
299
+ drawingTools: "\uADF8\uB9AC\uAE30 \uB3C4\uAD6C",
300
+ cursor: "\uCEE4\uC11C",
301
+ trendline: "\uCD94\uC138\uC120",
302
+ horizontal: "\uC218\uD3C9\uC120",
303
+ fibonacci: "\uD53C\uBCF4\uB098\uCE58 \uB418\uB3CC\uB9BC",
304
+ removeAll: "\uADF8\uB9AC\uAE30 \uBAA8\uB450 \uC0AD\uC81C",
305
+ collapseDrawbar: "\uADF8\uB9AC\uAE30 \uB3C4\uAD6C \uC811\uAE30",
306
+ chartSettings: "\uCC28\uD2B8 \uC124\uC815",
307
+ settingsCandles: "\uCE94\uB4E4",
308
+ settingsDisplay: "\uD45C\uC2DC",
309
+ settingsGrid: "\uACA9\uC790",
310
+ settingUpColor: "\uC0C1\uC2B9 \uC0C9",
311
+ settingDownColor: "\uD558\uB77D \uC0C9",
312
+ followTheme: "\uD14C\uB9C8 \uC0C9 \uC0AC\uC6A9",
313
+ settingHighLow: "\uCD5C\uACE0\xB7\uCD5C\uC800 \uD45C\uC2DC",
314
+ settingLastPrice: "\uD604\uC7AC\uAC00 \uC120",
315
+ settingVolume: "\uAC70\uB798\uB7C9",
316
+ settingGridH: "\uAC00\uB85C \uACA9\uC790",
317
+ settingGridV: "\uC138\uB85C \uACA9\uC790",
318
+ resetDefaults: "\uAE30\uBCF8\uAC12\uC73C\uB85C",
319
+ done: "\uC644\uB8CC",
320
+ expandDrawbar: "\uADF8\uB9AC\uAE30 \uB3C4\uAD6C \uD3BC\uCE58\uAE30",
321
+ magnet: "\uC790\uC11D \uBAA8\uB4DC (\uC2DC\xB7\uACE0\xB7\uC800\xB7\uC885\uAC00\uC5D0 \uBD99\uC774\uAE30)",
322
+ remove: "\uC0AD\uC81C",
323
+ color: "\uC0C9\uC0C1",
324
+ indicators: "\uC9C0\uD45C",
325
+ addIndicator: "\uC9C0\uD45C \uCD94\uAC00",
326
+ indicatorSettings: "\uC9C0\uD45C \uC124\uC815",
327
+ removeIndicator: "\uC9C0\uD45C \uC0AD\uC81C",
328
+ apply: "\uC801\uC6A9",
329
+ cancel: "\uCDE8\uC18C",
330
+ numbersHint: "\uC27C\uD45C\uB85C \uAD6C\uBD84 (\uC608: 5, 20, 60)",
331
+ show: "\uBCF4\uC774\uAE30",
332
+ hide: "\uC228\uAE30\uAE30",
333
+ collapse: "\uC9C0\uD45C \uC811\uAE30",
334
+ expand: "\uC9C0\uD45C \uD3BC\uCE58\uAE30",
335
+ indicatorCount: "\uC9C0\uD45C",
336
+ addPeriod: "\uAE30\uAC04 \uCD94\uAC00",
337
+ removePeriod: "\uAE30\uAC04 \uC0AD\uC81C",
338
+ period: "\uAE30\uAC04",
339
+ source: "\uAC00\uACA9 \uAE30\uC900",
340
+ lineWidth: "\uB450\uAED8",
341
+ inputs: "\uC785\uB825\uAC12",
342
+ style: "\uC2A4\uD0C0\uC77C",
343
+ // intervals
344
+ intervalMinutes: "\uBD84",
345
+ intervalHours: "\uC2DC\uAC04",
346
+ intervalDays: "\uC77C",
347
+ moreIntervals: "\uC2DC\uAC04 \uB2E8\uC704 \uB354\uBCF4\uAE30",
348
+ addFavorite: "\uC990\uACA8\uCC3E\uAE30\uC5D0 \uCD94\uAC00",
349
+ removeFavorite: "\uC990\uACA8\uCC3E\uAE30\uC5D0\uC11C \uC81C\uAC70",
350
+ unsupportedInterval: "\uC774 \uC885\uBAA9\uC740 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uB2E8\uC704",
351
+ // search
352
+ catAll: "\uC804\uCCB4",
353
+ catKrStock: "\uAD6D\uB0B4\uC8FC\uC2DD",
354
+ catUsStock: "\uD574\uC678\uC8FC\uC2DD",
355
+ catCrypto: "\uCF54\uC778",
356
+ catFutures: "\uC120\uBB3C",
357
+ recentSearches: "\uCD5C\uADFC \uAC80\uC0C9",
358
+ browse: "\uB458\uB7EC\uBCF4\uAE30",
359
+ allSymbols: "\uC804\uCCB4",
360
+ favorites: "\uC990\uACA8\uCC3E\uAE30",
361
+ exchanges: "\uAC70\uB798\uC18C",
362
+ noFavorites: "\uC990\uACA8\uCC3E\uAE30\uD55C \uC885\uBAA9\uC774 \uC5C6\uC5B4\uC694. \u2606\uB97C \uB20C\uB7EC \uCD94\uAC00\uD574 \uBCF4\uC138\uC694",
363
+ navigate: "\uC774\uB3D9",
364
+ openAction: "\uC5F4\uAE30",
365
+ clearRecent: "\uC804\uCCB4 \uC0AD\uC81C",
366
+ removeRecent: "\uCD5C\uADFC \uAC80\uC0C9\uC5D0\uC11C \uC0AD\uC81C",
367
+ symbols: "\uC885\uBAA9",
368
+ searching: "\uAC80\uC0C9 \uC911",
369
+ searchHint: "\u2191\u2193 \uC774\uB3D9 \xB7 Enter \uC120\uD0DD \xB7 Esc \uB2EB\uAE30",
370
+ current: "\uD604\uC7AC"
371
+ },
372
+ en: {
373
+ searchSymbol: "Symbol search",
374
+ searchPlaceholder: "Search by name or code",
375
+ noResults: "No results",
376
+ close: "Close",
377
+ zoomIn: "Zoom in",
378
+ zoomOut: "Zoom out",
379
+ scrollBack: "Scroll back",
380
+ scrollForward: "Scroll forward",
381
+ resetView: "Reset",
382
+ toRealtime: "Go to realtime",
383
+ screenshot: "Save screenshot",
384
+ fullscreen: "Fullscreen",
385
+ exitFullscreen: "Exit fullscreen",
386
+ open: "O",
387
+ high: "H",
388
+ low: "L",
389
+ close_: "C",
390
+ volume: "Vol",
391
+ chartType: "Chart type",
392
+ candles: "Candles",
393
+ line: "Line",
394
+ area: "Area",
395
+ drawingTools: "Drawing tools",
396
+ cursor: "Cursor",
397
+ trendline: "Trend line",
398
+ horizontal: "Horizontal line",
399
+ fibonacci: "Fib retracement",
400
+ removeAll: "Remove all drawings",
401
+ collapseDrawbar: "Hide drawing tools",
402
+ chartSettings: "Chart settings",
403
+ settingsCandles: "Candles",
404
+ settingsDisplay: "Display",
405
+ settingsGrid: "Grid",
406
+ settingUpColor: "Up color",
407
+ settingDownColor: "Down color",
408
+ followTheme: "Use theme color",
409
+ settingHighLow: "High/low markers",
410
+ settingLastPrice: "Last price line",
411
+ settingVolume: "Volume",
412
+ settingGridH: "Horizontal grid",
413
+ settingGridV: "Vertical grid",
414
+ resetDefaults: "Reset",
415
+ done: "Done",
416
+ expandDrawbar: "Show drawing tools",
417
+ magnet: "Magnet (snap to OHLC)",
418
+ remove: "Remove",
419
+ color: "Color",
420
+ indicators: "Indicators",
421
+ addIndicator: "Add indicator",
422
+ indicatorSettings: "Indicator settings",
423
+ removeIndicator: "Remove indicator",
424
+ apply: "Apply",
425
+ cancel: "Cancel",
426
+ numbersHint: "Comma separated (e.g. 5, 20, 60)",
427
+ show: "Show",
428
+ hide: "Hide",
429
+ collapse: "Collapse indicators",
430
+ expand: "Expand indicators",
431
+ indicatorCount: "Indicators",
432
+ addPeriod: "Add period",
433
+ removePeriod: "Remove period",
434
+ period: "Period",
435
+ source: "Source",
436
+ lineWidth: "Width",
437
+ inputs: "Inputs",
438
+ style: "Style",
439
+ intervalMinutes: "Minutes",
440
+ intervalHours: "Hours",
441
+ intervalDays: "Days",
442
+ moreIntervals: "More intervals",
443
+ addFavorite: "Add to favorites",
444
+ removeFavorite: "Remove from favorites",
445
+ unsupportedInterval: "Not available for this symbol",
446
+ catAll: "All",
447
+ catKrStock: "KR stocks",
448
+ catUsStock: "Global stocks",
449
+ catCrypto: "Crypto",
450
+ catFutures: "Futures",
451
+ recentSearches: "Recent",
452
+ browse: "Browse",
453
+ allSymbols: "All",
454
+ favorites: "Favorites",
455
+ exchanges: "Exchanges",
456
+ noFavorites: "No favorites yet \u2014 tap \u2606 to add one",
457
+ navigate: "navigate",
458
+ openAction: "open",
459
+ clearRecent: "Clear",
460
+ removeRecent: "Remove from recent",
461
+ symbols: "Symbols",
462
+ searching: "Searching",
463
+ searchHint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc close",
464
+ current: "Current"
465
+ }
466
+ };
467
+ function resolutionLabel(resolution, locale, form = "short") {
468
+ const match = /^(\d*)([SDWM]?)$/i.exec(resolution.trim());
469
+ if (!match) return resolution;
470
+ const n2 = match[1] ? Number(match[1]) : 1;
471
+ const unit = (match[2] ?? "").toUpperCase();
472
+ const ko = locale === "ko";
473
+ const long = form === "long";
474
+ switch (unit) {
475
+ case "S":
476
+ return ko ? `${n2}\uCD08` : `${n2}s`;
477
+ case "D":
478
+ return ko ? n2 === 1 && !long ? "\uC77C" : `${n2}\uC77C` : `${n2}D`;
479
+ case "W":
480
+ return ko ? n2 === 1 && !long ? "\uC8FC" : `${n2}\uC8FC` : `${n2}W`;
481
+ case "M":
482
+ if (n2 % 12 === 0) return ko ? `${n2 / 12}\uB144` : `${n2 / 12}Y`;
483
+ return ko ? n2 === 1 && !long ? "\uC6D4" : `${n2}\uAC1C\uC6D4` : `${n2}M`;
484
+ default:
485
+ if (n2 >= 60 && n2 % 60 === 0) return ko ? `${n2 / 60}\uC2DC\uAC04` : `${n2 / 60}h`;
486
+ return ko ? `${n2}\uBD84` : `${n2}m`;
487
+ }
488
+ }
489
+
490
+ // ../../node_modules/.pnpm/preact@11.2.0/node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs
491
+ var f2 = /* @__PURE__ */ Symbol.for("pv");
492
+ var o2 = 0;
493
+ function a2(t3, e3, n2, u3, a4, i3) {
494
+ e3 || (e3 = {});
495
+ var c3, l3, p3 = e3;
496
+ if ("ref" in p3 && "function" != typeof t3) for (l3 in p3 = {}, e3) "ref" == l3 ? c3 = e3[l3] : p3[l3] = e3[l3];
497
+ var s3 = { type: t3, props: p3, key: n2, ref: c3, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: f2, __v: --o2, __i: -1, __u: 0 };
498
+ return (a4 || i3) && (s3.__source = a4, s3.__self = i3), n.vnode && n.vnode(s3), s3;
499
+ }
500
+
501
+ // src/icons.tsx
502
+ function Icon({ children, size = 20 }) {
503
+ return /* @__PURE__ */ a2(
504
+ "svg",
505
+ {
506
+ width: size,
507
+ height: size,
508
+ viewBox: "0 0 20 20",
509
+ fill: "none",
510
+ stroke: "currentColor",
511
+ "stroke-width": "1.6",
512
+ "stroke-linecap": "round",
513
+ "stroke-linejoin": "round",
514
+ "aria-hidden": "true",
515
+ children
516
+ }
517
+ );
518
+ }
519
+ var SearchIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: [
520
+ /* @__PURE__ */ a2("circle", { cx: "9", cy: "9", r: "5.5" }),
521
+ /* @__PURE__ */ a2("path", { d: "M13 13l3.5 3.5" })
522
+ ] });
523
+ var CloseIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M5.5 5.5l9 9M14.5 5.5l-9 9" }) });
524
+ var CameraIcon = () => /* @__PURE__ */ a2(Icon, { children: [
525
+ /* @__PURE__ */ a2("path", { d: "M3.5 7.2c0-.9.7-1.6 1.6-1.6h1.6l1.2-1.8h4.2l1.2 1.8h1.6c.9 0 1.6.7 1.6 1.6v6.6c0 .9-.7 1.6-1.6 1.6H5.1c-.9 0-1.6-.7-1.6-1.6z" }),
526
+ /* @__PURE__ */ a2("circle", { cx: "10", cy: "10.4", r: "2.6" })
527
+ ] });
528
+ var ExpandIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M4 8V4h4M16 8V4h-4M4 12v4h4M16 12v4h-4" }) });
529
+ var CollapseIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M8 4v4H4M12 4v4h4M8 16v-4H4M12 16v-4h4" }) });
530
+ var MinusIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M5 10h10" }) });
531
+ var PlusIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M5 10h10M10 5v10" }) });
532
+ var ChevronLeftIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M12 5l-5 5 5 5" }) });
533
+ var ChevronRightIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M8 5l5 5-5 5" }) });
534
+ var ResetIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: [
535
+ /* @__PURE__ */ a2("path", { d: "M4.5 10a5.5 5.5 0 1 0 1.7-4" }),
536
+ /* @__PURE__ */ a2("path", { d: "M4.5 3.5V7H8" })
537
+ ] });
538
+ var ToRealtimeIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M5 5.5l4.5 4.5L5 14.5M10 5.5l4.5 4.5-4.5 4.5" }) });
539
+ var ChevronDownIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: /* @__PURE__ */ a2("path", { d: "M5 8l5 5 5-5" }) });
540
+ var CursorIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M5 3.5l10 6.2-4.4 1.1-2 4.2z" }) });
541
+ var TrendLineIcon = () => /* @__PURE__ */ a2(Icon, { children: [
542
+ /* @__PURE__ */ a2("path", { d: "M5 15L15 5" }),
543
+ /* @__PURE__ */ a2("circle", { cx: "5", cy: "15", r: "1.6", fill: "currentColor" }),
544
+ /* @__PURE__ */ a2("circle", { cx: "15", cy: "5", r: "1.6", fill: "currentColor" })
545
+ ] });
546
+ var HorizontalLineIcon = () => /* @__PURE__ */ a2(Icon, { children: [
547
+ /* @__PURE__ */ a2("path", { d: "M3 10h14" }),
548
+ /* @__PURE__ */ a2("circle", { cx: "10", cy: "10", r: "1.6", fill: "currentColor" })
549
+ ] });
550
+ var FibonacciIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M3 4.5h14M3 8.2h14M3 11.8h14M3 15.5h14" }) });
551
+ var TrashIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M4.5 6h11M8 6V4.5h4V6M6 6l.7 9.5h6.6L14 6" }) });
552
+ var PencilIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M12.8 4.2l3 3L7.5 15.5 4 16l.5-3.5z" }) });
553
+ var CandlesIcon = () => /* @__PURE__ */ a2(Icon, { children: [
554
+ /* @__PURE__ */ a2("path", { d: "M6.5 3.5v13M13.5 3.5v13" }),
555
+ /* @__PURE__ */ a2("rect", { x: "4.5", y: "6", width: "4", height: "6", rx: "0.8", fill: "currentColor" }),
556
+ /* @__PURE__ */ a2("rect", { x: "11.5", y: "8", width: "4", height: "5", rx: "0.8" })
557
+ ] });
558
+ var LineChartIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M3 14l4.5-5 3.5 3 6-7" }) });
559
+ var AreaChartIcon = () => /* @__PURE__ */ a2(Icon, { children: [
560
+ /* @__PURE__ */ a2("path", { d: "M3 14l4.5-5 3.5 3 6-7" }),
561
+ /* @__PURE__ */ a2("path", { d: "M3 14l4.5-5 3.5 3 6-7V17H3z", fill: "currentColor", "fill-opacity": "0.25", stroke: "none" })
562
+ ] });
563
+ var IndicatorIcon = () => /* @__PURE__ */ a2(Icon, { children: [
564
+ /* @__PURE__ */ a2("path", { d: "M3 15.5l4-5 3 2.5 3.5-5L17 11" }),
565
+ /* @__PURE__ */ a2("path", { d: "M3 4.5v11.5h14" })
566
+ ] });
567
+ var GearIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: [
568
+ /* @__PURE__ */ a2("circle", { cx: "10", cy: "10", r: "2.4" }),
569
+ /* @__PURE__ */ a2("path", { d: "M10 3v2M10 15v2M3 10h2M15 10h2M5 5l1.4 1.4M13.6 13.6L15 15M5 15l1.4-1.4M13.6 6.4L15 5" })
570
+ ] });
571
+ var SmallCloseIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: /* @__PURE__ */ a2("path", { d: "M6 6l8 8M14 6l-8 8" }) });
572
+ var EyeIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: [
573
+ /* @__PURE__ */ a2("path", { d: "M2 10s3-5.5 8-5.5S18 10 18 10s-3 5.5-8 5.5S2 10 2 10z" }),
574
+ /* @__PURE__ */ a2("circle", { cx: "10", cy: "10", r: "2.3" })
575
+ ] });
576
+ var EyeOffIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: /* @__PURE__ */ a2("path", { d: "M3 3l14 14M8.2 5a8 8 0 0 1 1.8-.5c5 0 8 5.5 8 5.5a14 14 0 0 1-2.2 2.9M5.4 6.4A14 14 0 0 0 2 10s3 5.5 8 5.5a7.7 7.7 0 0 0 3.6-.9" }) });
577
+ var MagnetIcon = () => /* @__PURE__ */ a2(Icon, { children: [
578
+ /* @__PURE__ */ a2("path", { d: "M5 4h3v6.5a2 2 0 0 0 4 0V4h3v6.5a5 5 0 0 1-10 0z" }),
579
+ /* @__PURE__ */ a2("path", { d: "M5 7h3M12 7h3" })
580
+ ] });
581
+ var StarIcon = ({ filled = false }) => /* @__PURE__ */ a2(Icon, { size: 16, children: /* @__PURE__ */ a2(
582
+ "path",
583
+ {
584
+ d: "M10 3.2l2.1 4.3 4.7.7-3.4 3.3.8 4.7L10 14l-4.2 2.2.8-4.7-3.4-3.3 4.7-.7z",
585
+ fill: filled ? "currentColor" : "none"
586
+ }
587
+ ) });
588
+ var CollapseLeftIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: /* @__PURE__ */ a2("path", { d: "M11 5l-5 5 5 5M15 5l-5 5 5 5" }) });
589
+ var SettingsIcon = () => /* @__PURE__ */ a2(Icon, { children: [
590
+ /* @__PURE__ */ a2("path", { d: "M10 12.6a2.6 2.6 0 1 0 0-5.2 2.6 2.6 0 0 0 0 5.2z" }),
591
+ /* @__PURE__ */ a2("path", { d: "M16.2 12a1.3 1.3 0 0 0 .26 1.43l.05.05a1.6 1.6 0 1 1-2.26 2.26l-.05-.05a1.3 1.3 0 0 0-1.43-.26 1.3 1.3 0 0 0-.79 1.19v.14a1.6 1.6 0 0 1-3.2 0v-.07a1.3 1.3 0 0 0-.85-1.19 1.3 1.3 0 0 0-1.43.26l-.05.05a1.6 1.6 0 1 1-2.26-2.26l.05-.05A1.3 1.3 0 0 0 4.5 12a1.3 1.3 0 0 0-1.19-.79h-.14a1.6 1.6 0 0 1 0-3.2h.07a1.3 1.3 0 0 0 1.19-.85 1.3 1.3 0 0 0-.26-1.43l-.05-.05a1.6 1.6 0 1 1 2.26-2.26l.05.05a1.3 1.3 0 0 0 1.43.26h.06a1.3 1.3 0 0 0 .79-1.19v-.14a1.6 1.6 0 0 1 3.2 0v.07a1.3 1.3 0 0 0 .79 1.19 1.3 1.3 0 0 0 1.43-.26l.05-.05a1.6 1.6 0 1 1 2.26 2.26l-.05.05a1.3 1.3 0 0 0-.26 1.43v.06a1.3 1.3 0 0 0 1.19.79h.14a1.6 1.6 0 0 1 0 3.2h-.07a1.3 1.3 0 0 0-1.19.79z" })
592
+ ] });
593
+ var FunnestMark = () => /* @__PURE__ */ a2("svg", { width: "20", height: "20", viewBox: "0 0 32 32", fill: "none", "aria-hidden": "true", children: [
594
+ /* @__PURE__ */ a2("rect", { width: "32", height: "32", rx: "9", fill: "#6C4DFF" }),
595
+ /* @__PURE__ */ a2("path", { d: "M7 13.5C9.5 21.5 17 23.5 23.4 11.2", stroke: "#fff", "stroke-width": "3.2", "stroke-linecap": "round" }),
596
+ /* @__PURE__ */ a2("circle", { cx: "24.6", cy: "8.6", r: "2.9", fill: "#FFD43B" })
597
+ ] });
598
+
599
+ // ../../node_modules/.pnpm/preact@11.2.0/node_modules/preact/hooks/dist/hooks.mjs
600
+ var t2;
601
+ var r2;
602
+ var u2;
603
+ var i2;
604
+ var o3 = Object.is;
605
+ var f3 = 0;
606
+ var c2 = [];
607
+ var e2 = [];
608
+ var a3 = n;
609
+ var v2 = a3.__b;
610
+ var l2 = a3.__r;
611
+ var m2 = a3.diffed;
612
+ var h2 = a3.__c;
613
+ var s2 = a3.unmount;
614
+ var p2 = a3.__;
615
+ function y2(n2, t3) {
616
+ a3.__h && a3.__h(r2, n2, f3 || t3), f3 = 0;
617
+ var u3 = r2.__H || (r2.__H = { __: [], __h: [] });
618
+ return n2 >= u3.__.length && u3.__.push({}), u3.__[n2];
619
+ }
620
+ function d2(n2) {
621
+ return f3 = 1, _2(G2, n2);
622
+ }
623
+ function _2(n2, u3, i3) {
624
+ var f4 = y2(t2++, 2);
625
+ if (f4.t = n2, !f4.__c && (f4.__ = [i3 ? i3(u3) : G2(void 0, u3), function(n3) {
626
+ var t3 = (f4.__N || f4.__)[0], r3 = f4.t(t3, n3);
627
+ o3(t3, r3) || (f4.__N = [r3, f4.__[1]], f4.__c.setState({}));
628
+ }], f4.__c = r2, !r2.__f)) {
629
+ r2.__f = true;
630
+ var c3 = r2.shouldComponentUpdate;
631
+ r2.shouldComponentUpdate = function(n3, t3, r3) {
632
+ var u4 = this.__H;
633
+ if (!u4) return true;
634
+ var i4 = false, f5 = this.props != n3;
635
+ if (u4.__.some(function(n4) {
636
+ n4.__N && (i4 = true, o3(n4.__[0], n4.__N[0]) || (f5 = true));
637
+ }), c3) {
638
+ var e3 = c3.call(this, n3, t3, r3);
639
+ return i4 ? e3 || f5 : e3;
640
+ }
641
+ return !i4 || f5;
642
+ };
643
+ }
644
+ return f4.__;
645
+ }
646
+ function A2(n2, u3) {
647
+ var i3 = y2(t2++, 3);
648
+ !a3.__s && E2(i3.__H, u3) && (i3.__P = true, i3.__ = n2, i3.u = u3, r2.__H.__h.push(i3));
649
+ }
650
+ function F2(n2, u3) {
651
+ var i3 = y2(t2++, 4);
652
+ !a3.__s && E2(i3.__H, u3) && (i3.__P = false, i3.__ = n2, i3.u = u3, r2.__h.push(i3));
653
+ }
654
+ function T2(n2) {
655
+ return f3 = 5, w2(function() {
656
+ return { current: n2 };
657
+ }, []);
658
+ }
659
+ function w2(n2, r3) {
660
+ var u3 = y2(t2++, 7);
661
+ return E2(u3.__H, r3) && (u3.__ = n2(), u3.__H = r3), u3.__;
662
+ }
663
+ function g2() {
664
+ var n2;
665
+ do {
666
+ for (; n2 = e2.shift(); ) try {
667
+ C2(n2);
668
+ } catch (t4) {
669
+ a3.__e(t4, { __: (n2 = n2.__P) && n2.__v });
670
+ }
671
+ for (; n2 = c2.shift(); ) {
672
+ var t3 = n2.__H;
673
+ if (n2.__P && t3) try {
674
+ t3.__h.some(C2), t3.__h.some(D2), t3.__h = [];
675
+ } catch (r3) {
676
+ t3.__h = [], a3.__e(r3, n2.__v);
677
+ }
678
+ }
679
+ } while (e2.length);
680
+ }
681
+ a3.__b = function(n2) {
682
+ r2 = null, v2 && v2(n2);
683
+ }, a3.__ = function(n2, t3) {
684
+ n2 && t3.__k && t3.__k.__m && (n2.__m = t3.__k.__m), p2 && p2(n2, t3);
685
+ }, a3.__r = function(n2) {
686
+ l2 && l2(n2), t2 = 0;
687
+ var i3 = (r2 = n2.__c).__H;
688
+ i3 && (u2 == r2 ? r2.__h = [] : (i3.__h.some(C2), i3.__h.some(D2), t2 = 0), i3.__h = [], i3.__.some(function(n3) {
689
+ n3.__N && (n3.__ = n3.__N), n3.u = n3.__N = void 0;
690
+ })), u2 = r2;
691
+ }, a3.diffed = function(n2) {
692
+ m2 && m2(n2);
693
+ var t3 = n2.__c;
694
+ t3 && t3.__H && (t3.__H.__h.length && B2(c2.push(t3)), t3.__H.__.some(function(n3) {
695
+ n3.u && (n3.__H = n3.u);
696
+ })), u2 = r2 = null;
697
+ }, a3.__c = function(n2, t3) {
698
+ t3.some(function(n3) {
699
+ try {
700
+ n3.__h.some(C2), n3.__h = n3.__h.filter(function(n4) {
701
+ return !n4.__ || D2(n4);
702
+ });
703
+ } catch (t4) {
704
+ n3.__h = [function() {
705
+ throw t4;
706
+ }];
707
+ }
708
+ }), h2 && h2(n2, t3);
709
+ }, a3.unmount = function(n2) {
710
+ s2 && s2(n2);
711
+ var t3, r3, u3 = n2.__c;
712
+ u3 && u3.__H && (u3.__H.__.some(function(u4) {
713
+ try {
714
+ if (u4.__P && u4.__c) {
715
+ if (void 0 === r3) {
716
+ for (r3 = n2.__; r3 && (!r3.__c || !r3.__c.__P); ) r3 = r3.__;
717
+ r3 = r3 && r3.__c;
718
+ }
719
+ u4.__P = r3, B2(e2.push(u4));
720
+ } else C2(u4);
721
+ } catch (n3) {
722
+ t3 = n3;
723
+ }
724
+ }), u3.__H = void 0, t3 && a3.__e(t3, u3.__v));
725
+ };
726
+ var k = "function" == typeof requestAnimationFrame;
727
+ function z2(n2) {
728
+ var t3, r3 = function() {
729
+ clearTimeout(u3), k && cancelAnimationFrame(t3), setTimeout(n2);
730
+ }, u3 = setTimeout(r3, 35);
731
+ k && (t3 = requestAnimationFrame(r3));
732
+ }
733
+ function B2(n2) {
734
+ 1 != n2 && i2 == a3.requestAnimationFrame || ((i2 = a3.requestAnimationFrame) || z2)(g2);
735
+ }
736
+ function C2(n2) {
737
+ var t3 = r2, u3 = n2.__c;
738
+ "function" == typeof u3 && (n2.__c = void 0, u3()), r2 = t3;
739
+ }
740
+ function D2(n2) {
741
+ var t3 = r2;
742
+ n2.__c = n2.__(), r2 = t3;
743
+ }
744
+ function E2(n2, t3) {
745
+ return !n2 || n2.length != t3.length || t3.some(function(t4, r3) {
746
+ return !o3(t4, n2[r3]);
747
+ });
748
+ }
749
+ function G2(n2, t3) {
750
+ return "function" == typeof t3 ? t3(n2) : t3;
751
+ }
752
+
753
+ // src/store.ts
754
+ var Store = class {
755
+ constructor(state) {
756
+ this.state = state;
757
+ }
758
+ state;
759
+ listeners = /* @__PURE__ */ new Set();
760
+ get() {
761
+ return this.state;
762
+ }
763
+ set(patch) {
764
+ let changed = false;
765
+ for (const key in patch) {
766
+ if (!Object.is(patch[key], this.state[key])) {
767
+ changed = true;
768
+ break;
769
+ }
770
+ }
771
+ if (!changed) return;
772
+ this.state = { ...this.state, ...patch };
773
+ for (const listener of this.listeners) listener();
774
+ }
775
+ subscribe(listener) {
776
+ this.listeners.add(listener);
777
+ return () => this.listeners.delete(listener);
778
+ }
779
+ };
780
+ function useStore(store) {
781
+ const [state, setState] = d2(store.get());
782
+ A2(() => {
783
+ setState(store.get());
784
+ return store.subscribe(() => setState(store.get()));
785
+ }, [store]);
786
+ return state;
787
+ }
788
+
789
+ // src/components/drawing-toolbar.tsx
790
+ var TOOLS = [
791
+ { tool: null, label: "cursor", icon: CursorIcon },
792
+ { tool: "trendline", label: "trendline", icon: TrendLineIcon },
793
+ { tool: "horizontal", label: "horizontal", icon: HorizontalLineIcon },
794
+ { tool: "fibonacci", label: "fibonacci", icon: FibonacciIcon }
795
+ ];
796
+ function DrawingToolbar({ ctx }) {
797
+ const s3 = useStore(ctx.store);
798
+ const t3 = STRINGS[s3.locale];
799
+ const pick = (tool) => {
800
+ ctx.chart.setDrawingTool(tool);
801
+ if (tool && ctx.store.get().drawbarOpen) ctx.store.set({ drawbarOpen: false });
802
+ };
803
+ return /* @__PURE__ */ a2($, { children: [
804
+ s3.drawbarCollapsed && /* @__PURE__ */ a2("button", { class: "fnc-drawbar-tab", "aria-label": t3.expandDrawbar, title: t3.expandDrawbar, onClick: () => ctx.setDrawbarCollapsed(false), children: /* @__PURE__ */ a2(ChevronRightIcon, {}) }),
805
+ /* @__PURE__ */ a2(
806
+ "div",
807
+ {
808
+ class: "fnc-drawbar",
809
+ role: "toolbar",
810
+ "aria-label": t3.drawingTools,
811
+ "aria-orientation": "vertical",
812
+ "data-open": s3.drawbarOpen,
813
+ "data-collapsed": s3.drawbarCollapsed,
814
+ children: [
815
+ TOOLS.map(({ tool, label, icon: Icon2 }) => /* @__PURE__ */ a2(
816
+ "button",
817
+ {
818
+ class: "fnc-btn",
819
+ title: t3[label],
820
+ "aria-label": t3[label],
821
+ "aria-pressed": s3.drawingTool === tool,
822
+ onClick: () => pick(tool),
823
+ children: /* @__PURE__ */ a2(Icon2, {})
824
+ }
825
+ )),
826
+ /* @__PURE__ */ a2("div", { class: "fnc-hsep" }),
827
+ /* @__PURE__ */ a2(
828
+ "button",
829
+ {
830
+ class: "fnc-btn",
831
+ title: t3.magnet,
832
+ "aria-label": t3.magnet,
833
+ "aria-pressed": s3.magnet,
834
+ onClick: () => ctx.chart.setMagnet(!s3.magnet),
835
+ children: /* @__PURE__ */ a2(MagnetIcon, {})
836
+ }
837
+ ),
838
+ /* @__PURE__ */ a2(
839
+ "button",
840
+ {
841
+ class: "fnc-btn",
842
+ title: t3.removeAll,
843
+ "aria-label": t3.removeAll,
844
+ disabled: s3.drawingCount === 0,
845
+ onClick: () => ctx.chart.clearDrawings(),
846
+ children: /* @__PURE__ */ a2(TrashIcon, {})
847
+ }
848
+ ),
849
+ /* @__PURE__ */ a2("span", { class: "fnc-spacer" }),
850
+ /* @__PURE__ */ a2(
851
+ "button",
852
+ {
853
+ class: "fnc-btn fnc-drawbar-collapse",
854
+ title: t3.collapseDrawbar,
855
+ "aria-label": t3.collapseDrawbar,
856
+ onClick: () => ctx.setDrawbarCollapsed(true),
857
+ children: /* @__PURE__ */ a2(ChevronLeftIcon, {})
858
+ }
859
+ )
860
+ ]
861
+ }
862
+ )
863
+ ] });
864
+ }
865
+ var SWATCHES = ["#3182F6", "#F04452", "#12B886", "#FF9F0A", "#7048E8"];
866
+ function SelectionBar({ ctx }) {
867
+ const s3 = useStore(ctx.store);
868
+ const t3 = STRINGS[s3.locale];
869
+ const d3 = s3.selection;
870
+ if (!d3 || s3.layout.plotWidth <= 0) return null;
871
+ return /* @__PURE__ */ a2("div", { class: "fnc-selbar", role: "toolbar", style: { left: `${s3.layout.plotWidth / 2}px` }, children: [
872
+ SWATCHES.map((color) => /* @__PURE__ */ a2(
873
+ "button",
874
+ {
875
+ class: "fnc-swatch",
876
+ style: { background: color },
877
+ title: t3.color,
878
+ "aria-label": `${t3.color} ${color}`,
879
+ "aria-pressed": (d3.color ?? SWATCHES[0]) === color,
880
+ onClick: () => ctx.chart.updateDrawing(d3.id, { color })
881
+ }
882
+ )),
883
+ /* @__PURE__ */ a2("div", { class: "fnc-sep" }),
884
+ /* @__PURE__ */ a2("button", { class: "fnc-btn", title: t3.remove, "aria-label": t3.remove, onClick: () => ctx.chart.removeDrawing(d3.id), children: /* @__PURE__ */ a2(TrashIcon, {}) })
885
+ ] });
886
+ }
887
+
888
+ // src/components/overlay.tsx
889
+ import { formatCompact, formatPrice } from "@funnest/charts";
890
+
891
+ // src/components/symbol-avatar.tsx
892
+ function hue(text) {
893
+ let h3 = 0;
894
+ for (const ch of text) h3 = (h3 * 31 + ch.charCodeAt(0)) % 360;
895
+ return h3;
896
+ }
897
+ function SymbolAvatar({ info, size = 24 }) {
898
+ const [failed, setFailed] = d2(false);
899
+ A2(() => setFailed(false), [info.logoUrl]);
900
+ const style = { width: `${size}px`, height: `${size}px`, fontSize: `${Math.round(size * 0.46)}px` };
901
+ if (info.logoUrl && !failed) {
902
+ return /* @__PURE__ */ a2("img", { class: "fnc-avatar", src: info.logoUrl, alt: "", style, onError: () => setFailed(true) });
903
+ }
904
+ const h3 = hue(info.symbol);
905
+ return /* @__PURE__ */ a2(
906
+ "span",
907
+ {
908
+ class: "fnc-avatar fnc-avatar-initial",
909
+ "aria-hidden": "true",
910
+ style: { ...style, background: `hsl(${h3} 70% 50% / 0.16)`, color: `hsl(${h3} 60% 42%)` },
911
+ children: (info.name || info.symbol).trim().charAt(0).toUpperCase()
912
+ }
913
+ );
914
+ }
915
+
916
+ // src/components/overlay.tsx
917
+ var ZOOM_STEP = 1.25;
918
+ var CONTROL_OFFSET = 52;
919
+ var dirClass = (v3) => v3 > 0 ? "fnc-up" : v3 < 0 ? "fnc-down" : "fnc-flat";
920
+ function IndicatorItem({ ctx, item }) {
921
+ const t3 = STRINGS[ctx.store.get().locale];
922
+ return /* @__PURE__ */ a2(
923
+ "div",
924
+ {
925
+ class: item.visible ? "fnc-ind" : "fnc-ind fnc-ind-hidden",
926
+ role: "button",
927
+ tabIndex: 0,
928
+ "aria-label": `${item.title} ${t3.indicatorSettings}`,
929
+ onClick: (e3) => {
930
+ if (e3.target.closest(".fnc-ind-actions")) return;
931
+ ctx.store.set({ dialog: { kind: "indicatorSettings", id: item.id } });
932
+ },
933
+ onKeyDown: (e3) => e3.key === "Enter" && ctx.store.set({ dialog: { kind: "indicatorSettings", id: item.id } }),
934
+ children: [
935
+ /* @__PURE__ */ a2(
936
+ "button",
937
+ {
938
+ class: "fnc-ind-btn fnc-ind-eye",
939
+ title: item.visible ? t3.hide : t3.show,
940
+ "aria-label": `${item.title} ${item.visible ? t3.hide : t3.show}`,
941
+ "aria-pressed": !item.visible,
942
+ onClick: (e3) => {
943
+ e3.stopPropagation();
944
+ ctx.chart.setIndicatorVisible(item.id, !item.visible);
945
+ },
946
+ children: item.visible ? /* @__PURE__ */ a2(EyeIcon, {}) : /* @__PURE__ */ a2(EyeOffIcon, {})
947
+ }
948
+ ),
949
+ /* @__PURE__ */ a2("span", { class: "fnc-ind-title", children: item.title }),
950
+ item.visible && item.values.map((v3) => /* @__PURE__ */ a2("b", { style: { color: v3.color }, title: v3.label, children: v3.value })),
951
+ /* @__PURE__ */ a2("span", { class: "fnc-ind-actions", children: [
952
+ /* @__PURE__ */ a2(
953
+ "button",
954
+ {
955
+ class: "fnc-ind-btn",
956
+ title: t3.indicatorSettings,
957
+ "aria-label": `${item.title} ${t3.indicatorSettings}`,
958
+ onClick: () => ctx.store.set({ dialog: { kind: "indicatorSettings", id: item.id } }),
959
+ children: /* @__PURE__ */ a2(GearIcon, {})
960
+ }
961
+ ),
962
+ /* @__PURE__ */ a2(
963
+ "button",
964
+ {
965
+ class: "fnc-ind-btn",
966
+ title: t3.removeIndicator,
967
+ "aria-label": `${item.title} ${t3.removeIndicator}`,
968
+ onClick: () => ctx.chart.removeIndicator(item.id),
969
+ children: /* @__PURE__ */ a2(SmallCloseIcon, {})
970
+ }
971
+ )
972
+ ] })
973
+ ]
974
+ }
975
+ );
976
+ }
977
+ function Legend({ ctx }) {
978
+ const s3 = useStore(ctx.store);
979
+ const t3 = STRINGS[s3.locale];
980
+ const [hover, setHover] = d2(null);
981
+ const [, refresh] = _2((n2) => n2 + 1, 0);
982
+ const [collapsed, setCollapsed] = d2(false);
983
+ A2(() => {
984
+ const offs = [
985
+ ctx.chart.on(
986
+ "crosshairMove",
987
+ (info) => setHover(info ? { bar: info.bar, prev: ctx.chart.bars[info.index - 1], index: info.index } : null)
988
+ ),
989
+ ctx.chart.on("dataChange", () => refresh(void 0))
990
+ ];
991
+ return () => offs.forEach((off) => off());
992
+ }, [ctx.chart]);
993
+ const ref = T2(null);
994
+ A2(() => {
995
+ const el = ref.current;
996
+ if (!el) return;
997
+ const update = () => ctx.chart.setTopInset(el.offsetTop + el.offsetHeight + 4);
998
+ const ro = new ResizeObserver(update);
999
+ ro.observe(el);
1000
+ return () => ro.disconnect();
1001
+ }, [ctx.chart]);
1002
+ if (!s3.symbol) return /* @__PURE__ */ a2("div", { class: "fnc-legend", ref });
1003
+ const bars = ctx.chart.bars;
1004
+ const bar = hover?.bar ?? bars[bars.length - 1];
1005
+ const prev = hover ? hover.prev : bars[bars.length - 2];
1006
+ const fmt = (p3) => formatPrice(p3, s3.symbol.pricePrecision);
1007
+ let values = null;
1008
+ if (bar) {
1009
+ const cls = dirClass(bar.close - bar.open);
1010
+ const change = prev ? bar.close - prev.close : 0;
1011
+ const pct = prev && prev.close ? change / prev.close * 100 : 0;
1012
+ const sign = change > 0 ? "+" : "";
1013
+ values = /* @__PURE__ */ a2("div", { class: "fnc-legend-values", children: [
1014
+ /* @__PURE__ */ a2("span", { children: [
1015
+ t3.open,
1016
+ " ",
1017
+ /* @__PURE__ */ a2("b", { class: cls, children: fmt(bar.open) })
1018
+ ] }),
1019
+ /* @__PURE__ */ a2("span", { children: [
1020
+ t3.high,
1021
+ " ",
1022
+ /* @__PURE__ */ a2("b", { class: cls, children: fmt(bar.high) })
1023
+ ] }),
1024
+ /* @__PURE__ */ a2("span", { children: [
1025
+ t3.low,
1026
+ " ",
1027
+ /* @__PURE__ */ a2("b", { class: cls, children: fmt(bar.low) })
1028
+ ] }),
1029
+ /* @__PURE__ */ a2("span", { children: [
1030
+ t3.close_,
1031
+ " ",
1032
+ /* @__PURE__ */ a2("b", { class: cls, children: fmt(bar.close) })
1033
+ ] }),
1034
+ /* @__PURE__ */ a2("b", { class: dirClass(change), children: [
1035
+ sign,
1036
+ fmt(change),
1037
+ " (",
1038
+ sign,
1039
+ pct.toFixed(2),
1040
+ "%)"
1041
+ ] }),
1042
+ bar.volume !== void 0 && /* @__PURE__ */ a2("span", { children: [
1043
+ t3.volume,
1044
+ " ",
1045
+ /* @__PURE__ */ a2("b", { class: "fnc-flat", children: formatCompact(bar.volume, s3.locale) })
1046
+ ] })
1047
+ ] });
1048
+ }
1049
+ const items = s3.indicators.length ? ctx.chart.indicatorLegend(hover?.index) : [];
1050
+ const overlayItems = items.filter((i3) => i3.overlay);
1051
+ const paneLegends = s3.layout.panes.filter((p3) => p3.kind === "indicator").map((pane) => ({ pane, item: items.find((i3) => i3.id === pane.id) }));
1052
+ return /* @__PURE__ */ a2($, { children: [
1053
+ /* @__PURE__ */ a2("div", { class: "fnc-legend", ref, children: [
1054
+ /* @__PURE__ */ a2("div", { class: "fnc-legend-title", children: [
1055
+ ctx.showLogo && /* @__PURE__ */ a2(SymbolAvatar, { info: s3.symbol, size: 18 }),
1056
+ /* @__PURE__ */ a2("b", { children: s3.symbol.name }),
1057
+ /* @__PURE__ */ a2("span", { children: "\xB7" }),
1058
+ /* @__PURE__ */ a2("span", { children: resolutionLabel(s3.resolution, s3.locale) }),
1059
+ /* @__PURE__ */ a2("span", { children: "\xB7" }),
1060
+ /* @__PURE__ */ a2("span", { children: s3.symbol.exchange }),
1061
+ s3.loading && /* @__PURE__ */ a2("span", { class: "fnc-spinner", role: "status" })
1062
+ ] }),
1063
+ values,
1064
+ !collapsed && overlayItems.map((item) => /* @__PURE__ */ a2(IndicatorItem, { ctx, item })),
1065
+ overlayItems.length > 0 && (collapsed ? /* @__PURE__ */ a2("button", { class: "fnc-ind-handle", "aria-expanded": "false", "aria-label": t3.expand, title: t3.expand, onClick: () => setCollapsed(false), children: /* @__PURE__ */ a2("span", {}) }) : /* @__PURE__ */ a2("button", { class: "fnc-ind-collapse", "aria-expanded": "true", "aria-label": t3.collapse, title: t3.collapse, onClick: () => setCollapsed(true), children: /* @__PURE__ */ a2(CollapseLeftIcon, {}) }))
1066
+ ] }),
1067
+ paneLegends.map(
1068
+ ({ pane, item }) => item && /* @__PURE__ */ a2("div", { class: "fnc-legend fnc-pane-legend", style: { top: `${pane.top + 4}px` }, children: /* @__PURE__ */ a2(IndicatorItem, { ctx, item }) })
1069
+ )
1070
+ ] });
1071
+ }
1072
+ function NavControls({ ctx }) {
1073
+ const s3 = useStore(ctx.store);
1074
+ const t3 = STRINGS[s3.locale];
1075
+ const { plotWidth, panesHeight } = s3.layout;
1076
+ if (plotWidth <= 0) return null;
1077
+ const top = panesHeight - CONTROL_OFFSET;
1078
+ const page = () => Math.max(1, Math.round(plotWidth / ctx.chart.timeScale.barSpacing / 3));
1079
+ const button = (label, onClick, icon) => /* @__PURE__ */ a2("button", { class: "fnc-btn", title: label, "aria-label": label, onClick, children: icon });
1080
+ return /* @__PURE__ */ a2($, { children: [
1081
+ /* @__PURE__ */ a2("div", { class: "fnc-nav", style: { left: `${plotWidth / 2}px`, top: `${top}px` }, children: [
1082
+ button(t3.zoomOut, () => ctx.chart.zoom(1 / ZOOM_STEP), /* @__PURE__ */ a2(MinusIcon, {})),
1083
+ button(t3.zoomIn, () => ctx.chart.zoom(ZOOM_STEP), /* @__PURE__ */ a2(PlusIcon, {})),
1084
+ button(t3.scrollBack, () => ctx.chart.scrollBars(-page()), /* @__PURE__ */ a2(ChevronLeftIcon, {})),
1085
+ button(t3.scrollForward, () => ctx.chart.scrollBars(page()), /* @__PURE__ */ a2(ChevronRightIcon, {})),
1086
+ button(t3.resetView, () => ctx.chart.resetView(), /* @__PURE__ */ a2(ResetIcon, {}))
1087
+ ] }),
1088
+ !s3.atRealtime && /* @__PURE__ */ a2(
1089
+ "button",
1090
+ {
1091
+ class: "fnc-realtime",
1092
+ style: { left: `${plotWidth - 44}px`, top: `${top}px` },
1093
+ title: t3.toRealtime,
1094
+ "aria-label": t3.toRealtime,
1095
+ onClick: () => ctx.chart.scrollToRealtime(),
1096
+ children: /* @__PURE__ */ a2(ToRealtimeIcon, {})
1097
+ }
1098
+ )
1099
+ ] });
1100
+ }
1101
+ var ATTRIBUTION_URL = "https://charts.funnest.xyz/?utm_source=chart&utm_medium=attribution";
1102
+ function Attribution({ ctx }) {
1103
+ const s3 = useStore(ctx.store);
1104
+ const { panesHeight, plotWidth } = s3.layout;
1105
+ if (!ctx.attribution || plotWidth <= 0) return null;
1106
+ const label = s3.locale === "ko" ? "Funnest \uCC28\uD2B8" : "Charts by Funnest";
1107
+ return /* @__PURE__ */ a2(
1108
+ "a",
1109
+ {
1110
+ class: "fnc-attribution",
1111
+ href: ATTRIBUTION_URL,
1112
+ target: "_blank",
1113
+ rel: "noopener",
1114
+ title: label,
1115
+ "aria-label": label,
1116
+ style: { top: `${panesHeight - 36}px` },
1117
+ children: [
1118
+ /* @__PURE__ */ a2(FunnestMark, {}),
1119
+ /* @__PURE__ */ a2("span", { children: "funnest" })
1120
+ ]
1121
+ }
1122
+ );
1123
+ }
1124
+ function Overlay({ ctx }) {
1125
+ return /* @__PURE__ */ a2($, { children: [
1126
+ /* @__PURE__ */ a2(Attribution, { ctx }),
1127
+ /* @__PURE__ */ a2(Legend, { ctx }),
1128
+ /* @__PURE__ */ a2(NavControls, { ctx }),
1129
+ /* @__PURE__ */ a2(SelectionBar, { ctx })
1130
+ ] });
1131
+ }
1132
+
1133
+ // src/components/chart-type-menu.tsx
1134
+ var TYPES = [
1135
+ { type: "candles", label: "candles", icon: CandlesIcon },
1136
+ { type: "line", label: "line", icon: LineChartIcon },
1137
+ { type: "area", label: "area", icon: AreaChartIcon }
1138
+ ];
1139
+ function ChartTypeMenu({ ctx }) {
1140
+ const s3 = useStore(ctx.store);
1141
+ const t3 = STRINGS[s3.locale];
1142
+ const [open, setOpen] = d2(false);
1143
+ const ref = T2(null);
1144
+ const current = TYPES.find((x2) => x2.type === s3.chartType) ?? TYPES[0];
1145
+ A2(() => {
1146
+ if (!open) return;
1147
+ const onDown = (e3) => !ref.current?.contains(e3.target) && setOpen(false);
1148
+ const onKey = (e3) => e3.key === "Escape" && setOpen(false);
1149
+ document.addEventListener("pointerdown", onDown);
1150
+ document.addEventListener("keydown", onKey);
1151
+ return () => {
1152
+ document.removeEventListener("pointerdown", onDown);
1153
+ document.removeEventListener("keydown", onKey);
1154
+ };
1155
+ }, [open]);
1156
+ const choose = (type) => {
1157
+ ctx.chart.setChartType(type);
1158
+ ctx.store.set({ chartType: type });
1159
+ setOpen(false);
1160
+ };
1161
+ return /* @__PURE__ */ a2("div", { class: "fnc-menu-wrap", ref, children: [
1162
+ /* @__PURE__ */ a2(
1163
+ "button",
1164
+ {
1165
+ class: "fnc-btn fnc-btn-wide",
1166
+ title: t3.chartType,
1167
+ "aria-label": t3.chartType,
1168
+ "aria-haspopup": "menu",
1169
+ "aria-expanded": open,
1170
+ onClick: () => setOpen(!open),
1171
+ children: [
1172
+ /* @__PURE__ */ a2(current.icon, {}),
1173
+ /* @__PURE__ */ a2(ChevronDownIcon, {})
1174
+ ]
1175
+ }
1176
+ ),
1177
+ open && /* @__PURE__ */ a2("div", { class: "fnc-menu", role: "menu", children: TYPES.map(({ type, label, icon: Icon2 }) => /* @__PURE__ */ a2(
1178
+ "button",
1179
+ {
1180
+ class: "fnc-menu-item",
1181
+ role: "menuitemradio",
1182
+ "aria-checked": type === s3.chartType,
1183
+ onClick: () => choose(type),
1184
+ children: [
1185
+ /* @__PURE__ */ a2(Icon2, {}),
1186
+ /* @__PURE__ */ a2("span", { children: t3[label] })
1187
+ ]
1188
+ }
1189
+ )) })
1190
+ ] });
1191
+ }
1192
+
1193
+ // src/components/indicator-dialogs.tsx
1194
+ import {
1195
+ INDICATORS,
1196
+ maLines,
1197
+ PRICE_SOURCES,
1198
+ SERIES_COLORS
1199
+ } from "@funnest/charts";
1200
+
1201
+ // src/components/settings-dialog.tsx
1202
+ import { DEFAULT_CHART_SETTINGS } from "@funnest/charts";
1203
+ function toHex(color) {
1204
+ const ctx = document.createElement("canvas").getContext("2d");
1205
+ if (!ctx) return "#000000";
1206
+ ctx.fillStyle = "#000000";
1207
+ ctx.fillStyle = color;
1208
+ return ctx.fillStyle.startsWith("#") ? ctx.fillStyle : "#000000";
1209
+ }
1210
+ var SECTIONS = [
1211
+ {
1212
+ title: "settingsDisplay",
1213
+ toggles: [
1214
+ { key: "highLowMarkers", label: "settingHighLow" },
1215
+ { key: "lastPriceLine", label: "settingLastPrice" },
1216
+ { key: "volume", label: "settingVolume" }
1217
+ ]
1218
+ },
1219
+ {
1220
+ title: "settingsGrid",
1221
+ toggles: [
1222
+ { key: "gridHorizontal", label: "settingGridH" },
1223
+ { key: "gridVertical", label: "settingGridV" }
1224
+ ]
1225
+ }
1226
+ ];
1227
+ function SettingsDialog({ ctx }) {
1228
+ const { locale } = useStore(ctx.store);
1229
+ const t3 = STRINGS[locale];
1230
+ const [settings, setSettings] = d2(ctx.chart.settings);
1231
+ A2(() => ctx.chart.on("settingsChange", setSettings), [ctx.chart]);
1232
+ const close = () => ctx.store.set({ dialog: null });
1233
+ const set = (patch) => ctx.chart.setSettings(patch);
1234
+ const themeColor = (name) => toHex(getComputedStyle(ctx.root).getPropertyValue(name).trim());
1235
+ return /* @__PURE__ */ a2("div", { class: "fnc-backdrop", onPointerDown: (e3) => e3.target === e3.currentTarget && close(), children: /* @__PURE__ */ a2("div", { class: "fnc-dialog", role: "dialog", "aria-modal": "true", "aria-label": t3.chartSettings, onKeyDown: (e3) => e3.key === "Escape" && close(), children: [
1236
+ /* @__PURE__ */ a2("div", { class: "fnc-dialog-head", children: [
1237
+ /* @__PURE__ */ a2("b", { children: t3.chartSettings }),
1238
+ /* @__PURE__ */ a2("button", { class: "fnc-btn", "aria-label": t3.close, onClick: close, children: /* @__PURE__ */ a2(CloseIcon, {}) })
1239
+ ] }),
1240
+ /* @__PURE__ */ a2("form", { class: "fnc-form", onSubmit: (e3) => (e3.preventDefault(), close()), children: [
1241
+ /* @__PURE__ */ a2("div", { class: "fnc-section", children: t3.settingsCandles }),
1242
+ ["upColor", "downColor"].map((key) => /* @__PURE__ */ a2("label", { class: "fnc-row", children: [
1243
+ /* @__PURE__ */ a2("span", { class: "fnc-row-label fnc-row-label-wide", children: key === "upColor" ? t3.settingUpColor : t3.settingDownColor }),
1244
+ /* @__PURE__ */ a2(
1245
+ "input",
1246
+ {
1247
+ type: "color",
1248
+ class: "fnc-color",
1249
+ value: settings[key] ?? themeColor(key === "upColor" ? "--fnc-up" : "--fnc-down"),
1250
+ onInput: (e3) => set({ [key]: e3.currentTarget.value })
1251
+ }
1252
+ ),
1253
+ settings[key] && /* @__PURE__ */ a2("button", { type: "button", class: "fnc-link", onClick: () => set({ [key]: null }), children: t3.followTheme })
1254
+ ] })),
1255
+ SECTIONS.map((section) => /* @__PURE__ */ a2($, { children: [
1256
+ /* @__PURE__ */ a2("div", { class: "fnc-section", children: t3[section.title] }),
1257
+ section.toggles.map(({ key, label }) => /* @__PURE__ */ a2("label", { class: "fnc-row fnc-toggle-row", children: [
1258
+ /* @__PURE__ */ a2("span", { children: t3[label] }),
1259
+ /* @__PURE__ */ a2(
1260
+ "input",
1261
+ {
1262
+ type: "checkbox",
1263
+ role: "switch",
1264
+ class: "fnc-switch",
1265
+ checked: settings[key],
1266
+ onChange: (e3) => set({ [key]: e3.currentTarget.checked })
1267
+ }
1268
+ )
1269
+ ] }))
1270
+ ] })),
1271
+ /* @__PURE__ */ a2("div", { class: "fnc-form-actions", children: [
1272
+ /* @__PURE__ */ a2("button", { type: "button", class: "fnc-action fnc-action-danger", onClick: () => ctx.chart.setSettings(DEFAULT_CHART_SETTINGS), children: t3.resetDefaults }),
1273
+ /* @__PURE__ */ a2("button", { type: "submit", class: "fnc-action fnc-action-primary", children: t3.done })
1274
+ ] })
1275
+ ] })
1276
+ ] }) });
1277
+ }
1278
+
1279
+ // src/components/indicator-dialogs.tsx
1280
+ function Dialog({ title, onClose, children, wide }) {
1281
+ const ref = T2(null);
1282
+ A2(() => {
1283
+ ref.current?.querySelector("input, button.fnc-sr-row")?.focus();
1284
+ }, []);
1285
+ return /* @__PURE__ */ a2("div", { class: "fnc-backdrop", onPointerDown: (e3) => e3.target === e3.currentTarget && onClose(), children: /* @__PURE__ */ a2(
1286
+ "div",
1287
+ {
1288
+ class: wide ? "fnc-dialog fnc-dialog-wide" : "fnc-dialog",
1289
+ role: "dialog",
1290
+ "aria-modal": "true",
1291
+ "aria-label": title,
1292
+ ref,
1293
+ onKeyDown: (e3) => e3.key === "Escape" && onClose(),
1294
+ children: [
1295
+ /* @__PURE__ */ a2("div", { class: "fnc-dialog-head", children: [
1296
+ /* @__PURE__ */ a2("b", { children: title }),
1297
+ /* @__PURE__ */ a2("button", { class: "fnc-btn", "aria-label": "close", onClick: onClose, children: /* @__PURE__ */ a2(CloseIcon, {}) })
1298
+ ] }),
1299
+ children
1300
+ ]
1301
+ }
1302
+ ) });
1303
+ }
1304
+ function IndicatorPicker({ ctx }) {
1305
+ const { locale } = useStore(ctx.store);
1306
+ const t3 = STRINGS[locale];
1307
+ const close = () => ctx.store.set({ dialog: null });
1308
+ const add = (type) => {
1309
+ ctx.chart.addIndicator(type);
1310
+ close();
1311
+ };
1312
+ const groups = [
1313
+ [locale === "ko" ? "\uAC00\uACA9 \uCC28\uD2B8\uC5D0 \uD45C\uC2DC" : "On price", ["ma", "ema", "bb"]],
1314
+ [locale === "ko" ? "\uC544\uB798 \uC601\uC5ED\uC5D0 \uD45C\uC2DC" : "Separate pane", ["rsi", "macd"]]
1315
+ ];
1316
+ return /* @__PURE__ */ a2(Dialog, { title: t3.addIndicator, onClose: close, children: /* @__PURE__ */ a2("div", { class: "fnc-sr-list fnc-scroll", children: groups.map(([label, types]) => /* @__PURE__ */ a2($, { children: [
1317
+ /* @__PURE__ */ a2("div", { class: "fnc-sr-section", children: label }),
1318
+ types.map((type) => {
1319
+ const def = INDICATORS[type];
1320
+ return /* @__PURE__ */ a2("button", { class: "fnc-sr-row", onClick: () => add(type), children: /* @__PURE__ */ a2("span", { class: "fnc-sr-main", children: [
1321
+ /* @__PURE__ */ a2("span", { class: "fnc-sr-name", children: def.name[locale] }),
1322
+ /* @__PURE__ */ a2("span", { class: "fnc-sr-meta", children: def.description[locale] })
1323
+ ] }) });
1324
+ })
1325
+ ] })) }) });
1326
+ }
1327
+ var WIDTHS = [1, 2, 3, 4];
1328
+ function defaultSeries(type, params) {
1329
+ return INDICATORS[type].compute([], params).series.filter((s3) => s3.kind === "line");
1330
+ }
1331
+ function StyleControls({
1332
+ t: t3,
1333
+ row,
1334
+ onChange
1335
+ }) {
1336
+ return /* @__PURE__ */ a2($, { children: [
1337
+ /* @__PURE__ */ a2(
1338
+ "input",
1339
+ {
1340
+ type: "checkbox",
1341
+ class: "fnc-check",
1342
+ checked: row.visible,
1343
+ "aria-label": row.visible ? t3.hide : t3.show,
1344
+ onChange: (e3) => onChange({ visible: e3.currentTarget.checked })
1345
+ }
1346
+ ),
1347
+ /* @__PURE__ */ a2(
1348
+ "input",
1349
+ {
1350
+ type: "color",
1351
+ class: "fnc-color",
1352
+ value: row.color,
1353
+ "aria-label": t3.color,
1354
+ onInput: (e3) => onChange({ color: e3.currentTarget.value })
1355
+ }
1356
+ ),
1357
+ /* @__PURE__ */ a2("select", { class: "fnc-select fnc-select-width", value: row.width, "aria-label": t3.lineWidth, onChange: (e3) => onChange({ width: Number(e3.currentTarget.value) }), children: WIDTHS.map((w3) => /* @__PURE__ */ a2("option", { value: w3, children: [
1358
+ w3,
1359
+ "px"
1360
+ ] })) })
1361
+ ] });
1362
+ }
1363
+ function SourceSelect({ value, locale, label, onChange }) {
1364
+ return /* @__PURE__ */ a2("select", { class: "fnc-select fnc-select-source", value, "aria-label": label, onChange: (e3) => onChange(e3.currentTarget.value), children: PRICE_SOURCES.map((s3) => /* @__PURE__ */ a2("option", { value: s3.value, children: s3.label[locale] })) });
1365
+ }
1366
+ function IndicatorSettings({ ctx, id }) {
1367
+ const { locale, indicators } = useStore(ctx.store);
1368
+ const t3 = STRINGS[locale];
1369
+ const config = indicators.find((c3) => c3.id === id);
1370
+ const def = config ? INDICATORS[config.type] : null;
1371
+ const hasLines = !!def?.params.some((p3) => p3.kind === "lines");
1372
+ const [lines, setLines] = d2(() => {
1373
+ if (!config || !hasLines) return [];
1374
+ const defaults = defaultSeries(config.type, config.params);
1375
+ return maLines(config.params).map((l3, i3) => {
1376
+ const style = config.styles?.[`l${i3}`];
1377
+ return {
1378
+ period: String(l3.period),
1379
+ source: l3.source,
1380
+ color: style?.color ?? defaults[i3]?.color ?? SERIES_COLORS[i3 % SERIES_COLORS.length],
1381
+ width: style?.width ?? defaults[i3]?.width ?? 1,
1382
+ visible: style?.visible ?? true
1383
+ };
1384
+ });
1385
+ });
1386
+ const [values, setValues] = d2(
1387
+ () => Object.fromEntries(Object.entries(config?.params ?? {}).filter(([, v3]) => !Array.isArray(v3)).map(([k2, v3]) => [k2, String(v3)]))
1388
+ );
1389
+ const [styles, setStyles] = d2(() => {
1390
+ if (!config || hasLines) return [];
1391
+ return defaultSeries(config.type, config.params).map((s3) => {
1392
+ const style = config.styles?.[s3.key];
1393
+ return { key: s3.key, label: s3.label, color: style?.color ?? s3.color, width: style?.width ?? s3.width, visible: style?.visible ?? true };
1394
+ });
1395
+ });
1396
+ const close = () => ctx.store.set({ dialog: null });
1397
+ if (!config || !def) return null;
1398
+ const updateLine = (i3, patch) => setLines(lines.map((l3, j2) => j2 === i3 ? { ...l3, ...patch } : l3));
1399
+ const addLine = () => {
1400
+ const i3 = lines.length;
1401
+ const last = lines[i3 - 1];
1402
+ setLines([
1403
+ ...lines,
1404
+ {
1405
+ period: String(last ? Number(last.period) * 2 || 10 : 5),
1406
+ source: "close",
1407
+ color: SERIES_COLORS[i3 % SERIES_COLORS.length],
1408
+ width: 1,
1409
+ visible: true
1410
+ }
1411
+ ]);
1412
+ };
1413
+ const apply = (e3) => {
1414
+ e3.preventDefault();
1415
+ const params = {};
1416
+ const nextStyles = {};
1417
+ for (const spec of def.params) {
1418
+ const clamp = (n2) => Math.min(spec.max ?? Infinity, Math.max(spec.min ?? -Infinity, n2));
1419
+ if (spec.kind === "lines") {
1420
+ const valid = lines.filter((l3) => Number(l3.period) > 0);
1421
+ params[spec.key] = valid.map((l3) => ({ period: clamp(Math.round(Number(l3.period))), source: l3.source }));
1422
+ valid.forEach((l3, i3) => nextStyles[`l${i3}`] = { color: l3.color, width: l3.width, visible: l3.visible });
1423
+ } else if (spec.kind === "source") {
1424
+ params[spec.key] = values[spec.key] ?? "close";
1425
+ } else {
1426
+ const n2 = Number(values[spec.key]);
1427
+ if (Number.isFinite(n2)) params[spec.key] = clamp(n2);
1428
+ }
1429
+ }
1430
+ for (const s3 of styles) nextStyles[s3.key] = { color: s3.color, width: s3.width, visible: s3.visible };
1431
+ ctx.chart.updateIndicator(id, { params, styles: nextStyles });
1432
+ close();
1433
+ };
1434
+ return /* @__PURE__ */ a2(Dialog, { title: `${def.name[locale]} \xB7 ${t3.indicatorSettings}`, onClose: close, wide: true, children: /* @__PURE__ */ a2("form", { class: "fnc-form", onSubmit: apply, children: [
1435
+ hasLines ? /* @__PURE__ */ a2("div", { class: "fnc-rows", children: [
1436
+ lines.map((l3, i3) => /* @__PURE__ */ a2("div", { class: "fnc-row", children: [
1437
+ /* @__PURE__ */ a2("span", { class: "fnc-row-label", children: [
1438
+ t3.period,
1439
+ i3 + 1
1440
+ ] }),
1441
+ /* @__PURE__ */ a2(StyleControls, { t: t3, row: l3, onChange: (p3) => updateLine(i3, p3) }),
1442
+ /* @__PURE__ */ a2(SourceSelect, { value: l3.source, locale, label: t3.source, onChange: (source) => updateLine(i3, { source }) }),
1443
+ /* @__PURE__ */ a2(
1444
+ "input",
1445
+ {
1446
+ class: "fnc-input fnc-input-period",
1447
+ inputMode: "numeric",
1448
+ value: l3.period,
1449
+ "aria-label": `${t3.period}${i3 + 1}`,
1450
+ onInput: (e3) => updateLine(i3, { period: e3.currentTarget.value.replace(/\D/g, "") })
1451
+ }
1452
+ ),
1453
+ /* @__PURE__ */ a2("button", { type: "button", class: "fnc-ind-btn", "aria-label": t3.removePeriod, title: t3.removePeriod, onClick: () => setLines(lines.filter((_3, j2) => j2 !== i3)), children: /* @__PURE__ */ a2(SmallCloseIcon, {}) })
1454
+ ] })),
1455
+ /* @__PURE__ */ a2("button", { type: "button", class: "fnc-add-row", onClick: addLine, children: [
1456
+ "+ ",
1457
+ t3.addPeriod
1458
+ ] })
1459
+ ] }) : /* @__PURE__ */ a2($, { children: [
1460
+ /* @__PURE__ */ a2("div", { class: "fnc-section", children: t3.inputs }),
1461
+ def.params.map((spec) => /* @__PURE__ */ a2("div", { class: "fnc-row", children: [
1462
+ /* @__PURE__ */ a2("span", { class: "fnc-row-label fnc-row-label-wide", children: spec.label[locale] }),
1463
+ spec.kind === "source" ? /* @__PURE__ */ a2(
1464
+ SourceSelect,
1465
+ {
1466
+ value: values[spec.key] ?? "close",
1467
+ locale,
1468
+ label: spec.label[locale],
1469
+ onChange: (v3) => setValues({ ...values, [spec.key]: v3 })
1470
+ }
1471
+ ) : /* @__PURE__ */ a2(
1472
+ "input",
1473
+ {
1474
+ class: "fnc-input",
1475
+ inputMode: "decimal",
1476
+ value: values[spec.key] ?? "",
1477
+ "aria-label": spec.label[locale],
1478
+ onInput: (e3) => setValues({ ...values, [spec.key]: e3.currentTarget.value })
1479
+ }
1480
+ )
1481
+ ] })),
1482
+ styles.length > 0 && /* @__PURE__ */ a2("div", { class: "fnc-section", children: t3.style }),
1483
+ styles.map((row, i3) => /* @__PURE__ */ a2("div", { class: "fnc-row", children: [
1484
+ /* @__PURE__ */ a2("span", { class: "fnc-row-label fnc-row-label-wide", children: row.label }),
1485
+ /* @__PURE__ */ a2(StyleControls, { t: t3, row, onChange: (p3) => setStyles(styles.map((s3, j2) => j2 === i3 ? { ...s3, ...p3 } : s3)) })
1486
+ ] }))
1487
+ ] }),
1488
+ /* @__PURE__ */ a2("div", { class: "fnc-form-actions", children: [
1489
+ /* @__PURE__ */ a2(
1490
+ "button",
1491
+ {
1492
+ type: "button",
1493
+ class: "fnc-action fnc-action-danger",
1494
+ onClick: () => {
1495
+ ctx.chart.removeIndicator(id);
1496
+ close();
1497
+ },
1498
+ children: t3.remove
1499
+ }
1500
+ ),
1501
+ /* @__PURE__ */ a2("button", { type: "button", class: "fnc-action", onClick: close, children: t3.cancel }),
1502
+ /* @__PURE__ */ a2("button", { type: "submit", class: "fnc-action fnc-action-primary", children: t3.apply })
1503
+ ] })
1504
+ ] }) });
1505
+ }
1506
+ function Dialogs({ ctx }) {
1507
+ const { dialog } = useStore(ctx.store);
1508
+ if (dialog?.kind === "indicators") return /* @__PURE__ */ a2(IndicatorPicker, { ctx });
1509
+ if (dialog?.kind === "indicatorSettings") return /* @__PURE__ */ a2(IndicatorSettings, { ctx, id: dialog.id });
1510
+ if (dialog?.kind === "chartSettings") return /* @__PURE__ */ a2(SettingsDialog, { ctx });
1511
+ return null;
1512
+ }
1513
+
1514
+ // src/components/interval-picker.tsx
1515
+ var GROUP_LABEL = {
1516
+ minutes: "intervalMinutes",
1517
+ hours: "intervalHours",
1518
+ days: "intervalDays"
1519
+ };
1520
+ function IntervalPicker({ ctx }) {
1521
+ const s3 = useStore(ctx.store);
1522
+ const t3 = STRINGS[s3.locale];
1523
+ const [open, setOpen] = d2(false);
1524
+ const ref = T2(null);
1525
+ A2(() => {
1526
+ if (!open) return;
1527
+ const onDown = (e3) => !ref.current?.contains(e3.target) && setOpen(false);
1528
+ const onKey = (e3) => e3.key === "Escape" && setOpen(false);
1529
+ document.addEventListener("pointerdown", onDown);
1530
+ document.addEventListener("keydown", onKey);
1531
+ return () => {
1532
+ document.removeEventListener("pointerdown", onDown);
1533
+ document.removeEventListener("keydown", onKey);
1534
+ };
1535
+ }, [open]);
1536
+ const supported = s3.symbol?.supportedResolutions;
1537
+ const isSupported = (r3) => !supported || supported.includes(r3);
1538
+ const order = ctx.intervalGroups.flatMap((g3) => g3.intervals);
1539
+ const favorites = [...s3.favoriteIntervals].sort((a4, b) => order.indexOf(a4) - order.indexOf(b));
1540
+ const select = (r3) => {
1541
+ setOpen(false);
1542
+ ctx.selectResolution(r3);
1543
+ };
1544
+ return /* @__PURE__ */ a2("div", { class: "fnc-interval-picker", ref, children: [
1545
+ /* @__PURE__ */ a2("div", { class: "fnc-intervals", children: favorites.map((r3) => /* @__PURE__ */ a2(
1546
+ "button",
1547
+ {
1548
+ class: "fnc-chip",
1549
+ "aria-pressed": r3 === s3.resolution,
1550
+ disabled: !isSupported(r3),
1551
+ title: isSupported(r3) ? resolutionLabel(r3, s3.locale, "long") : t3.unsupportedInterval,
1552
+ onClick: () => select(r3),
1553
+ children: resolutionLabel(r3, s3.locale)
1554
+ }
1555
+ )) }),
1556
+ /* @__PURE__ */ a2(
1557
+ "button",
1558
+ {
1559
+ class: `fnc-btn fnc-btn-wide fnc-interval-trigger${favorites.includes(s3.resolution) ? "" : " fnc-interval-trigger-active"}`,
1560
+ title: t3.moreIntervals,
1561
+ "aria-label": t3.moreIntervals,
1562
+ "aria-haspopup": "menu",
1563
+ "aria-expanded": open,
1564
+ onClick: () => setOpen(!open),
1565
+ children: [
1566
+ /* @__PURE__ */ a2("b", { class: "fnc-interval-current", children: resolutionLabel(s3.resolution, s3.locale) }),
1567
+ /* @__PURE__ */ a2(ChevronDownIcon, {})
1568
+ ]
1569
+ }
1570
+ ),
1571
+ open && /* @__PURE__ */ a2("div", { class: "fnc-menu fnc-interval-menu", role: "menu", children: ctx.intervalGroups.map((group) => /* @__PURE__ */ a2("div", { class: "fnc-menu-group", role: "group", "aria-label": t3[GROUP_LABEL[group.id]], children: [
1572
+ /* @__PURE__ */ a2("div", { class: "fnc-menu-title", children: t3[GROUP_LABEL[group.id]] }),
1573
+ group.intervals.map((r3) => {
1574
+ const favorite = s3.favoriteIntervals.includes(r3);
1575
+ const ok = isSupported(r3);
1576
+ return /* @__PURE__ */ a2("div", { class: `fnc-menu-row${favorite ? " is-favorite" : ""}`, children: [
1577
+ /* @__PURE__ */ a2(
1578
+ "button",
1579
+ {
1580
+ class: "fnc-menu-item",
1581
+ role: "menuitemradio",
1582
+ "aria-checked": r3 === s3.resolution,
1583
+ disabled: !ok,
1584
+ title: ok ? void 0 : t3.unsupportedInterval,
1585
+ onClick: () => select(r3),
1586
+ children: resolutionLabel(r3, s3.locale, "long")
1587
+ }
1588
+ ),
1589
+ /* @__PURE__ */ a2(
1590
+ "button",
1591
+ {
1592
+ class: "fnc-star",
1593
+ "aria-pressed": favorite,
1594
+ "aria-label": `${resolutionLabel(r3, s3.locale, "long")} ${favorite ? t3.removeFavorite : t3.addFavorite}`,
1595
+ title: favorite ? t3.removeFavorite : t3.addFavorite,
1596
+ onClick: () => ctx.toggleFavoriteInterval(r3),
1597
+ children: /* @__PURE__ */ a2(StarIcon, { filled: favorite })
1598
+ }
1599
+ )
1600
+ ] });
1601
+ })
1602
+ ] })) })
1603
+ ] });
1604
+ }
1605
+
1606
+ // src/components/slot.tsx
1607
+ function Slot({ el }) {
1608
+ const ref = T2(null);
1609
+ F2(() => {
1610
+ ref.current?.appendChild(el);
1611
+ return () => el.remove();
1612
+ }, [el]);
1613
+ return /* @__PURE__ */ a2("div", { class: "fnc-slot-host", ref });
1614
+ }
1615
+
1616
+ // src/components/symbol-search.tsx
1617
+ import { formatPrice as formatPrice2 } from "@funnest/charts";
1618
+ var DEBOUNCE_MS = 150;
1619
+ var RECENT_KEY = "recent-symbols";
1620
+ var FAVORITE_KEY = "favorite-symbols";
1621
+ var MAX_RECENT = 8;
1622
+ var displayCode = (symbol) => symbol.slice(symbol.lastIndexOf(":") + 1);
1623
+ var toStored = (info) => JSON.parse(JSON.stringify({ ...info, tickSize: 0, price: void 0, changePercent: void 0, sparkline: void 0 }));
1624
+ function Highlight({ text, query }) {
1625
+ const q2 = query.trim();
1626
+ if (!q2) return /* @__PURE__ */ a2($, { children: text });
1627
+ const parts = [];
1628
+ const lower = text.toLowerCase();
1629
+ const needle = q2.toLowerCase();
1630
+ let i3 = 0;
1631
+ for (let at = lower.indexOf(needle); at >= 0; at = lower.indexOf(needle, i3)) {
1632
+ if (at > i3) parts.push(text.slice(i3, at));
1633
+ parts.push(/* @__PURE__ */ a2("mark", { children: text.slice(at, at + q2.length) }));
1634
+ i3 = at + q2.length;
1635
+ }
1636
+ parts.push(text.slice(i3));
1637
+ return /* @__PURE__ */ a2($, { children: parts });
1638
+ }
1639
+ function Sparkline({ values, up }) {
1640
+ if (values.length < 2) return null;
1641
+ const min = Math.min(...values);
1642
+ const max = Math.max(...values);
1643
+ const w3 = 72;
1644
+ const h3 = 24;
1645
+ const points = values.map((v3, i3) => `${(i3 / (values.length - 1) * w3).toFixed(1)},${(h3 - 2 - (v3 - min) / (max - min || 1) * (h3 - 4)).toFixed(1)}`).join(" ");
1646
+ return /* @__PURE__ */ a2("svg", { class: `fnc-sr-spark ${up ? "fnc-up" : "fnc-down"}`, width: w3, height: h3, viewBox: `0 0 ${w3} ${h3}`, "aria-hidden": "true", children: /* @__PURE__ */ a2("polyline", { points, fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linejoin": "round" }) });
1647
+ }
1648
+ function SymbolSearch({ ctx }) {
1649
+ const { locale, symbol: current } = useStore(ctx.store);
1650
+ const t3 = STRINGS[locale];
1651
+ const [query, setQuery] = d2("");
1652
+ const [category, setCategory] = d2(ctx.searchCategories[0]?.id ?? "all");
1653
+ const [view, setView] = d2({ kind: "all" });
1654
+ const [exchange, setExchange] = d2(null);
1655
+ const [results, setResults] = d2(null);
1656
+ const [loading, setLoading] = d2(true);
1657
+ const [recent, setRecent] = d2(() => ctx.storage.get(RECENT_KEY, []));
1658
+ const [favorites, setFavorites] = d2(() => ctx.storage.get(FAVORITE_KEY, []));
1659
+ const [active, setActive] = d2(0);
1660
+ const inputRef = T2(null);
1661
+ const listRef = T2(null);
1662
+ A2(() => inputRef.current?.focus(), []);
1663
+ A2(() => {
1664
+ let alive = true;
1665
+ setLoading(true);
1666
+ const id = setTimeout(() => {
1667
+ ctx.datafeed.searchSymbols?.(query.trim()).then((items) => alive && setResults(items)).catch(() => alive && setResults([])).finally(() => alive && setLoading(false));
1668
+ }, DEBOUNCE_MS);
1669
+ return () => {
1670
+ alive = false;
1671
+ clearTimeout(id);
1672
+ };
1673
+ }, [query]);
1674
+ const matchCategory = ctx.searchCategories.find((c3) => c3.id === category)?.match ?? (() => true);
1675
+ const categoryOf = (info) => ctx.searchCategories.find((c3) => c3.id !== "all" && c3.match(info));
1676
+ const isFavorite = (info) => favorites.some((f4) => f4.symbol === info.symbol);
1677
+ const filter = (list) => list.filter((i3) => matchCategory(i3) && (!exchange || i3.exchange === exchange));
1678
+ const exchanges = w2(() => {
1679
+ const counts = /* @__PURE__ */ new Map();
1680
+ for (const r3 of results ?? []) if (matchCategory(r3)) counts.set(r3.exchange, (counts.get(r3.exchange) ?? 0) + 1);
1681
+ return [...counts.entries()].sort((a4, b) => b[1] - a4[1]);
1682
+ }, [results, category]);
1683
+ const q2 = query.trim().toLowerCase();
1684
+ const favoriteRows = filter(favorites).filter((f4) => !q2 || f4.name.toLowerCase().includes(q2) || f4.symbol.toLowerCase().includes(q2));
1685
+ const showRecent = view.kind === "all" && !q2 && recent.length > 0;
1686
+ const recentRows = showRecent ? filter(recent) : [];
1687
+ const resultRows = view.kind === "favorites" ? favoriteRows : filter(results ?? []);
1688
+ const rows = [...recentRows, ...resultRows];
1689
+ A2(() => setActive(0), [query, category, exchange, view, results]);
1690
+ A2(() => {
1691
+ listRef.current?.querySelector(`[data-index="${active}"]`)?.scrollIntoView({ block: "nearest" });
1692
+ }, [active]);
1693
+ const close = () => ctx.setSearchOpen(false);
1694
+ const save = (key, next, set) => {
1695
+ set(next);
1696
+ ctx.storage.set(key, next);
1697
+ };
1698
+ const toggleFavorite = (info) => save(FAVORITE_KEY, isFavorite(info) ? favorites.filter((f4) => f4.symbol !== info.symbol) : [...favorites, toStored(info)], setFavorites);
1699
+ const choose = (info) => {
1700
+ if (!info) return;
1701
+ save(RECENT_KEY, [toStored(info), ...recent.filter((r3) => r3.symbol !== info.symbol)].slice(0, MAX_RECENT), setRecent);
1702
+ close();
1703
+ ctx.selectSymbol(info.symbol);
1704
+ };
1705
+ const onKeyDown = (e3) => {
1706
+ const count = rows.length;
1707
+ if (e3.key === "Escape") close();
1708
+ else if (e3.key === "ArrowDown" && count) setActive((a4) => (a4 + 1) % count);
1709
+ else if (e3.key === "ArrowUp" && count) setActive((a4) => (a4 - 1 + count) % count);
1710
+ else if (e3.key === "Enter") choose(rows[active]);
1711
+ else if (e3.key === "Backspace" && !query && exchange) setExchange(null);
1712
+ else return;
1713
+ e3.preventDefault();
1714
+ };
1715
+ const row = (info, index, isRecent) => {
1716
+ const cat = categoryOf(info);
1717
+ const fav = isFavorite(info);
1718
+ const change = info.changePercent ?? 0;
1719
+ const up = change >= 0;
1720
+ const trend = change > 0 ? "fnc-up" : change < 0 ? "fnc-down" : "fnc-flat";
1721
+ return /* @__PURE__ */ a2(
1722
+ "div",
1723
+ {
1724
+ class: "fnc-sr-row",
1725
+ role: "option",
1726
+ "aria-selected": index === active,
1727
+ "data-index": index,
1728
+ onPointerMove: () => setActive(index),
1729
+ onClick: () => choose(info),
1730
+ children: [
1731
+ /* @__PURE__ */ a2(
1732
+ "button",
1733
+ {
1734
+ class: `fnc-sr-star${fav ? " is-on" : ""}`,
1735
+ "aria-pressed": fav,
1736
+ "aria-label": `${info.name} ${fav ? t3.removeFavorite : t3.addFavorite}`,
1737
+ title: fav ? t3.removeFavorite : t3.addFavorite,
1738
+ onClick: (e3) => {
1739
+ e3.stopPropagation();
1740
+ toggleFavorite(info);
1741
+ },
1742
+ children: /* @__PURE__ */ a2(StarIcon, { filled: fav })
1743
+ }
1744
+ ),
1745
+ ctx.showLogo && /* @__PURE__ */ a2(SymbolAvatar, { info, size: 32 }),
1746
+ /* @__PURE__ */ a2("span", { class: "fnc-sr-main", children: [
1747
+ /* @__PURE__ */ a2("span", { class: "fnc-sr-name", children: [
1748
+ /* @__PURE__ */ a2(Highlight, { text: info.name, query }),
1749
+ current?.symbol === info.symbol && /* @__PURE__ */ a2("span", { class: "fnc-sr-current", children: t3.current })
1750
+ ] }),
1751
+ /* @__PURE__ */ a2("span", { class: "fnc-sr-meta", children: [
1752
+ /* @__PURE__ */ a2(Highlight, { text: displayCode(info.symbol), query }),
1753
+ " \xB7 ",
1754
+ info.exchange,
1755
+ cat && ` \xB7 ${cat.label[locale]}`
1756
+ ] })
1757
+ ] }),
1758
+ info.sparkline && /* @__PURE__ */ a2(Sparkline, { values: info.sparkline, up }),
1759
+ info.price !== void 0 && /* @__PURE__ */ a2("span", { class: "fnc-sr-quote", children: [
1760
+ /* @__PURE__ */ a2("b", { children: formatPrice2(info.price, info.pricePrecision) }),
1761
+ info.changePercent !== void 0 && /* @__PURE__ */ a2("span", { class: trend, children: [
1762
+ change > 0 ? "\u2191 " : change < 0 ? "\u2193 " : "",
1763
+ Math.abs(info.changePercent).toFixed(2),
1764
+ "%"
1765
+ ] })
1766
+ ] }),
1767
+ isRecent && /* @__PURE__ */ a2(
1768
+ "button",
1769
+ {
1770
+ class: "fnc-ind-btn",
1771
+ "aria-label": `${info.name} ${t3.removeRecent}`,
1772
+ title: t3.removeRecent,
1773
+ onClick: (e3) => {
1774
+ e3.stopPropagation();
1775
+ save(RECENT_KEY, recent.filter((r3) => r3.symbol !== info.symbol), setRecent);
1776
+ },
1777
+ children: /* @__PURE__ */ a2(SmallCloseIcon, {})
1778
+ }
1779
+ )
1780
+ ]
1781
+ }
1782
+ );
1783
+ };
1784
+ const sideItem = (label, selected, onClick, extra) => /* @__PURE__ */ a2("button", { class: "fnc-sr-side-item", "aria-pressed": selected, onClick, children: [
1785
+ /* @__PURE__ */ a2("span", { children: label }),
1786
+ extra
1787
+ ] });
1788
+ return /* @__PURE__ */ a2("div", { class: "fnc-backdrop", onPointerDown: (e3) => e3.target === e3.currentTarget && close(), children: /* @__PURE__ */ a2("div", { class: "fnc-dialog fnc-search-dialog", role: "dialog", "aria-modal": "true", "aria-label": t3.searchSymbol, onKeyDown, children: [
1789
+ /* @__PURE__ */ a2("div", { class: "fnc-search", children: [
1790
+ /* @__PURE__ */ a2(SearchIcon, {}),
1791
+ exchange && /* @__PURE__ */ a2("span", { class: "fnc-chip-token", children: [
1792
+ exchange,
1793
+ /* @__PURE__ */ a2("button", { "aria-label": `${exchange} ${t3.cancel}`, onClick: () => setExchange(null), children: /* @__PURE__ */ a2(SmallCloseIcon, {}) })
1794
+ ] }),
1795
+ /* @__PURE__ */ a2(
1796
+ "input",
1797
+ {
1798
+ ref: inputRef,
1799
+ value: query,
1800
+ placeholder: t3.searchPlaceholder,
1801
+ role: "combobox",
1802
+ "aria-expanded": "true",
1803
+ "aria-controls": "fnc-search-results",
1804
+ autocomplete: "off",
1805
+ spellcheck: false,
1806
+ onInput: (e3) => setQuery(e3.currentTarget.value)
1807
+ }
1808
+ ),
1809
+ query && /* @__PURE__ */ a2("button", { class: "fnc-ind-btn", "aria-label": t3.cancel, onClick: () => (setQuery(""), inputRef.current?.focus()), children: /* @__PURE__ */ a2(SmallCloseIcon, {}) }),
1810
+ /* @__PURE__ */ a2("kbd", { class: "fnc-kbd fnc-hide-mobile", children: "ESC" })
1811
+ ] }),
1812
+ /* @__PURE__ */ a2("div", { class: "fnc-tabs", role: "tablist", children: ctx.searchCategories.map((c3) => /* @__PURE__ */ a2("button", { class: "fnc-tab", role: "tab", "aria-selected": c3.id === category, onClick: () => setCategory(c3.id), children: c3.label[locale] })) }),
1813
+ /* @__PURE__ */ a2("div", { class: "fnc-sr-body", children: [
1814
+ /* @__PURE__ */ a2("nav", { class: "fnc-sr-side fnc-scroll", "aria-label": t3.searchSymbol, children: [
1815
+ /* @__PURE__ */ a2("div", { class: "fnc-sr-side-title", children: t3.browse }),
1816
+ sideItem(t3.allSymbols, view.kind === "all" && !exchange, () => (setView({ kind: "all" }), setExchange(null))),
1817
+ sideItem(t3.favorites, view.kind === "favorites", () => (setView({ kind: "favorites" }), setExchange(null)), /* @__PURE__ */ a2("em", { children: favorites.length || "" })),
1818
+ exchanges.length > 0 && /* @__PURE__ */ a2("div", { class: "fnc-sr-side-title", children: t3.exchanges }),
1819
+ exchanges.map(
1820
+ ([name, count]) => sideItem(name, exchange === name, () => (setView({ kind: "all" }), setExchange(exchange === name ? null : name)), /* @__PURE__ */ a2("em", { children: count }))
1821
+ )
1822
+ ] }),
1823
+ /* @__PURE__ */ a2("div", { class: "fnc-sr-list fnc-scroll", id: "fnc-search-results", role: "listbox", ref: listRef, children: [
1824
+ recentRows.length > 0 && /* @__PURE__ */ a2("div", { class: "fnc-sr-section", children: [
1825
+ /* @__PURE__ */ a2("span", { children: t3.recentSearches }),
1826
+ /* @__PURE__ */ a2("button", { class: "fnc-link", onClick: () => save(RECENT_KEY, [], setRecent), children: t3.clearRecent })
1827
+ ] }),
1828
+ recentRows.map((info, i3) => row(info, i3, true)),
1829
+ (recentRows.length > 0 || view.kind === "favorites") && /* @__PURE__ */ a2("div", { class: "fnc-sr-section", children: view.kind === "favorites" ? t3.favorites : t3.symbols }),
1830
+ loading && !results && view.kind === "all" ? Array.from({ length: 6 }, () => /* @__PURE__ */ a2("div", { class: "fnc-sr-skeleton", "aria-hidden": "true" })) : resultRows.map((info, i3) => row(info, recentRows.length + i3, false)),
1831
+ !loading && rows.length === 0 && /* @__PURE__ */ a2("div", { class: "fnc-empty", children: view.kind === "favorites" && favorites.length === 0 ? t3.noFavorites : t3.noResults })
1832
+ ] })
1833
+ ] }),
1834
+ /* @__PURE__ */ a2("div", { class: "fnc-sr-footer", children: [
1835
+ /* @__PURE__ */ a2("span", { children: [
1836
+ /* @__PURE__ */ a2("kbd", { class: "fnc-kbd", children: "\u2191\u2193" }),
1837
+ " ",
1838
+ t3.navigate
1839
+ ] }),
1840
+ /* @__PURE__ */ a2("span", { children: [
1841
+ /* @__PURE__ */ a2("kbd", { class: "fnc-kbd", children: "\u21B5" }),
1842
+ " ",
1843
+ t3.openAction
1844
+ ] }),
1845
+ /* @__PURE__ */ a2("span", { children: [
1846
+ /* @__PURE__ */ a2("kbd", { class: "fnc-kbd", children: "ESC" }),
1847
+ " ",
1848
+ t3.close
1849
+ ] }),
1850
+ /* @__PURE__ */ a2("span", { class: "fnc-spacer" }),
1851
+ /* @__PURE__ */ a2("span", { children: [
1852
+ /* @__PURE__ */ a2("kbd", { class: "fnc-kbd", children: [
1853
+ /Mac|iPhone|iPad/.test(navigator.platform) ? "\u2318" : "Ctrl",
1854
+ " + K"
1855
+ ] }),
1856
+ " ",
1857
+ t3.searchSymbol
1858
+ ] })
1859
+ ] })
1860
+ ] }) });
1861
+ }
1862
+
1863
+ // src/components/toolbar.tsx
1864
+ function Toolbar({ ctx }) {
1865
+ const s3 = useStore(ctx.store);
1866
+ const t3 = STRINGS[s3.locale];
1867
+ const canSearch = typeof ctx.datafeed.searchSymbols === "function";
1868
+ return /* @__PURE__ */ a2("div", { class: "fnc-toolbar", role: "toolbar", children: [
1869
+ /* @__PURE__ */ a2(
1870
+ "button",
1871
+ {
1872
+ class: "fnc-symbol",
1873
+ disabled: !canSearch,
1874
+ title: canSearch ? t3.searchSymbol : void 0,
1875
+ "aria-haspopup": "dialog",
1876
+ onClick: () => ctx.setSearchOpen(true),
1877
+ children: [
1878
+ ctx.showLogo && s3.symbol ? /* @__PURE__ */ a2(SymbolAvatar, { info: s3.symbol, size: 24 }) : canSearch && /* @__PURE__ */ a2("span", { class: "fnc-icon", children: /* @__PURE__ */ a2(SearchIcon, {}) }),
1879
+ /* @__PURE__ */ a2("span", { class: "fnc-symbol-name", children: s3.symbol?.name ?? "\u2014" }),
1880
+ canSearch && ctx.showLogo && /* @__PURE__ */ a2("span", { class: "fnc-icon", children: /* @__PURE__ */ a2(ChevronDownIcon, {}) })
1881
+ ]
1882
+ }
1883
+ ),
1884
+ /* @__PURE__ */ a2("div", { class: "fnc-sep" }),
1885
+ /* @__PURE__ */ a2(IntervalPicker, { ctx }),
1886
+ /* @__PURE__ */ a2(Slot, { el: ctx.slots.toolbar }),
1887
+ /* @__PURE__ */ a2("div", { class: "fnc-sep" }),
1888
+ /* @__PURE__ */ a2(ChartTypeMenu, { ctx }),
1889
+ /* @__PURE__ */ a2(
1890
+ "button",
1891
+ {
1892
+ class: "fnc-btn fnc-btn-wide fnc-btn-label",
1893
+ title: t3.addIndicator,
1894
+ "aria-label": t3.addIndicator,
1895
+ "aria-haspopup": "dialog",
1896
+ onClick: () => ctx.store.set({ dialog: { kind: "indicators" } }),
1897
+ children: [
1898
+ /* @__PURE__ */ a2(IndicatorIcon, {}),
1899
+ /* @__PURE__ */ a2("span", { children: t3.indicators })
1900
+ ]
1901
+ }
1902
+ ),
1903
+ /* @__PURE__ */ a2("div", { class: "fnc-spacer" }),
1904
+ ctx.drawingToolbar && /* @__PURE__ */ a2(
1905
+ "button",
1906
+ {
1907
+ class: "fnc-btn fnc-btn-drawbar",
1908
+ title: t3.drawingTools,
1909
+ "aria-label": t3.drawingTools,
1910
+ "aria-pressed": s3.drawbarOpen || s3.drawingTool !== null,
1911
+ onClick: () => ctx.store.set({ drawbarOpen: !s3.drawbarOpen }),
1912
+ children: /* @__PURE__ */ a2(PencilIcon, {})
1913
+ }
1914
+ ),
1915
+ /* @__PURE__ */ a2(Slot, { el: ctx.slots.toolbarEnd }),
1916
+ /* @__PURE__ */ a2(
1917
+ "button",
1918
+ {
1919
+ class: "fnc-btn",
1920
+ title: t3.chartSettings,
1921
+ "aria-label": t3.chartSettings,
1922
+ "aria-haspopup": "dialog",
1923
+ onClick: () => ctx.store.set({ dialog: { kind: "chartSettings" } }),
1924
+ children: /* @__PURE__ */ a2(SettingsIcon, {})
1925
+ }
1926
+ ),
1927
+ /* @__PURE__ */ a2("button", { class: "fnc-btn fnc-btn-screenshot", title: t3.screenshot, "aria-label": t3.screenshot, onClick: ctx.screenshot, children: /* @__PURE__ */ a2(CameraIcon, {}) }),
1928
+ /* @__PURE__ */ a2(
1929
+ "button",
1930
+ {
1931
+ class: "fnc-btn",
1932
+ title: s3.fullscreen ? t3.exitFullscreen : t3.fullscreen,
1933
+ "aria-label": s3.fullscreen ? t3.exitFullscreen : t3.fullscreen,
1934
+ onClick: ctx.toggleFullscreen,
1935
+ children: s3.fullscreen ? /* @__PURE__ */ a2(CollapseIcon, {}) : /* @__PURE__ */ a2(ExpandIcon, {})
1936
+ }
1937
+ ),
1938
+ s3.searchOpen && canSearch && /* @__PURE__ */ a2(SymbolSearch, { ctx }),
1939
+ /* @__PURE__ */ a2(Dialogs, { ctx })
1940
+ ] });
1941
+ }
1942
+
1943
+ // src/storage.ts
1944
+ function createStorage(prefix) {
1945
+ const memory = /* @__PURE__ */ new Map();
1946
+ const ls = (() => {
1947
+ try {
1948
+ return prefix && typeof localStorage !== "undefined" ? localStorage : null;
1949
+ } catch {
1950
+ return null;
1951
+ }
1952
+ })();
1953
+ const full = (key) => `${prefix}:${key}`;
1954
+ return {
1955
+ get(key, fallback) {
1956
+ try {
1957
+ const raw = ls ? ls.getItem(full(key)) : memory.get(key) ?? null;
1958
+ return raw === null ? fallback : JSON.parse(raw);
1959
+ } catch {
1960
+ return fallback;
1961
+ }
1962
+ },
1963
+ set(key, value) {
1964
+ const raw = JSON.stringify(value);
1965
+ try {
1966
+ if (ls) ls.setItem(full(key), raw);
1967
+ else memory.set(key, raw);
1968
+ } catch {
1969
+ memory.set(key, raw);
1970
+ }
1971
+ }
1972
+ };
1973
+ }
1974
+
1975
+ // src/styles.ts
1976
+ var CSS = (
1977
+ /* css */
1978
+ `
1979
+ .fnc-root {
1980
+ position: relative;
1981
+ display: flex;
1982
+ flex-direction: column;
1983
+ width: 100%;
1984
+ height: 100%;
1985
+ min-height: 0;
1986
+ background: var(--fnc-bg);
1987
+ color: var(--fnc-text);
1988
+ font-family: var(--fnc-font);
1989
+ font-size: 14px;
1990
+ line-height: 1.4;
1991
+ -webkit-font-smoothing: antialiased;
1992
+ overflow: hidden;
1993
+ }
1994
+ .fnc-root *, .fnc-root *::before, .fnc-root *::after { box-sizing: border-box; }
1995
+ .fnc-root.fnc-pseudo-fullscreen { position: fixed; inset: 0; z-index: 2147483000; }
1996
+ :where(.fnc-root) button {
1997
+ font: inherit;
1998
+ color: inherit;
1999
+ background: none;
2000
+ border: 0;
2001
+ margin: 0;
2002
+ padding: 0;
2003
+ cursor: pointer;
2004
+ -webkit-tap-highlight-color: transparent;
2005
+ }
2006
+ .fnc-root button:focus-visible { outline: 2px solid var(--fnc-accent); outline-offset: 1px; }
2007
+
2008
+ /* \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2009
+ .fnc-toolbar {
2010
+ display: flex;
2011
+ align-items: center;
2012
+ gap: 4px;
2013
+ height: 48px;
2014
+ padding: 0 8px;
2015
+ border-bottom: 1px solid var(--fnc-border);
2016
+ flex: none;
2017
+ }
2018
+ .fnc-symbol {
2019
+ display: flex;
2020
+ align-items: center;
2021
+ gap: 6px;
2022
+ height: 36px;
2023
+ max-width: 220px;
2024
+ padding: 0 10px 0 8px;
2025
+ border-radius: var(--fnc-radius);
2026
+ font-weight: 700;
2027
+ font-size: 15px;
2028
+ flex: none;
2029
+ }
2030
+ .fnc-symbol .fnc-icon { color: var(--fnc-text-2); display: flex; }
2031
+ .fnc-symbol-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2032
+ .fnc-symbol:not(:disabled):hover, .fnc-btn:hover, .fnc-chip:hover { background: var(--fnc-surface); }
2033
+ .fnc-symbol:disabled { cursor: default; }
2034
+ .fnc-sep { width: 1px; height: 20px; background: var(--fnc-border); margin: 0 4px; flex: none; }
2035
+ .fnc-intervals {
2036
+ display: flex;
2037
+ gap: 2px;
2038
+ overflow-x: auto;
2039
+ scrollbar-width: none;
2040
+ min-width: 0;
2041
+ }
2042
+ .fnc-intervals::-webkit-scrollbar { display: none; }
2043
+ .fnc-chip {
2044
+ height: 32px;
2045
+ padding: 0 10px;
2046
+ border-radius: var(--fnc-radius);
2047
+ font-size: 13px;
2048
+ font-weight: 600;
2049
+ color: var(--fnc-text-2);
2050
+ white-space: nowrap;
2051
+ flex: none;
2052
+ }
2053
+ .fnc-chip[aria-pressed='true'] { color: var(--fnc-text); background: var(--fnc-surface); }
2054
+ .fnc-spacer { flex: 1; min-width: 8px; }
2055
+ .fnc-btn {
2056
+ display: flex;
2057
+ align-items: center;
2058
+ justify-content: center;
2059
+ width: 36px;
2060
+ height: 36px;
2061
+ border-radius: var(--fnc-radius);
2062
+ color: var(--fnc-text-2);
2063
+ flex: none;
2064
+ }
2065
+ .fnc-btn:hover { color: var(--fnc-text); }
2066
+ .fnc-btn[aria-pressed='true'] { color: var(--fnc-text); background: var(--fnc-surface); }
2067
+ .fnc-btn:disabled { opacity: 0.35; cursor: default; background: none; }
2068
+ .fnc-btn-wide { width: auto; gap: 2px; padding: 0 6px 0 8px; }
2069
+ .fnc-btn-drawbar { display: none; }
2070
+ .fnc-btn-label { font-size: 13px; font-weight: 600; gap: 4px; padding: 0 10px 0 8px; }
2071
+
2072
+ .fnc-avatar {
2073
+ display: inline-flex;
2074
+ align-items: center;
2075
+ justify-content: center;
2076
+ flex: none;
2077
+ border-radius: 50%;
2078
+ object-fit: cover;
2079
+ font-weight: 700;
2080
+ line-height: 1;
2081
+ background: var(--fnc-surface);
2082
+ }
2083
+
2084
+ /* \u2500\u2500 Menus \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2085
+ .fnc-menu-wrap { position: relative; flex: none; }
2086
+ .fnc-menu {
2087
+ position: absolute;
2088
+ top: calc(100% + 6px);
2089
+ left: 0;
2090
+ z-index: 20;
2091
+ display: flex;
2092
+ flex-direction: column;
2093
+ min-width: 140px;
2094
+ padding: 6px;
2095
+ border-radius: 12px;
2096
+ background: var(--fnc-bg);
2097
+ box-shadow: 0 8px 28px rgb(0 0 0 / 0.16), 0 0 0 1px var(--fnc-border);
2098
+ }
2099
+ .fnc-menu-item {
2100
+ display: flex;
2101
+ align-items: center;
2102
+ gap: 10px;
2103
+ height: 36px;
2104
+ padding: 0 10px;
2105
+ border-radius: var(--fnc-radius);
2106
+ font-size: 14px;
2107
+ color: var(--fnc-text-2);
2108
+ text-align: left;
2109
+ }
2110
+ .fnc-menu-item:hover, .fnc-menu-item[aria-checked='true'] { background: var(--fnc-surface); color: var(--fnc-text); }
2111
+
2112
+ /* \u2500\u2500 Chart body & overlays \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2113
+ .fnc-body { position: relative; display: flex; flex: 1; min-height: 0; }
2114
+ .fnc-stage { position: relative; flex: 1; min-width: 0; }
2115
+ .fnc-chart-host, .fnc-overlay { position: absolute; inset: 0; }
2116
+
2117
+ /* \u2500\u2500 Drawing tool bar (left) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2118
+ .fnc-drawbar-host { flex: none; display: flex; }
2119
+ .fnc-drawbar {
2120
+ display: flex;
2121
+ flex-direction: column;
2122
+ align-items: center;
2123
+ gap: 4px;
2124
+ width: 48px;
2125
+ padding: 8px 0;
2126
+ border-right: 1px solid var(--fnc-border);
2127
+ background: var(--fnc-bg);
2128
+ }
2129
+ .fnc-hsep { width: 20px; height: 1px; background: var(--fnc-border); margin: 4px 0; }
2130
+
2131
+ /* Selected drawing: floating color/delete bar */
2132
+ .fnc-selbar {
2133
+ position: absolute;
2134
+ top: 8px;
2135
+ display: flex;
2136
+ align-items: center;
2137
+ gap: 6px;
2138
+ padding: 4px 4px 4px 10px;
2139
+ transform: translateX(-50%);
2140
+ border-radius: calc(var(--fnc-radius) + 4px);
2141
+ background: var(--fnc-bg);
2142
+ box-shadow: 0 2px 12px rgb(0 0 0 / 0.12), 0 0 0 1px var(--fnc-border);
2143
+ pointer-events: auto;
2144
+ }
2145
+ .fnc-swatch {
2146
+ width: 18px;
2147
+ height: 18px;
2148
+ border-radius: 50%;
2149
+ flex: none;
2150
+ }
2151
+ .fnc-swatch[aria-pressed='true'] { box-shadow: 0 0 0 2px var(--fnc-bg), 0 0 0 4px var(--fnc-text-2); }
2152
+ .fnc-selbar .fnc-btn { width: 32px; height: 32px; }
2153
+ .fnc-overlay { pointer-events: none; }
2154
+ .fnc-overlay button { pointer-events: auto; }
2155
+
2156
+ .fnc-legend {
2157
+ position: absolute;
2158
+ left: 12px;
2159
+ top: 8px;
2160
+ right: 80px;
2161
+ display: flex;
2162
+ flex-direction: column;
2163
+ gap: 2px;
2164
+ font-size: 12px;
2165
+ color: var(--fnc-text-2);
2166
+ font-variant-numeric: tabular-nums;
2167
+ }
2168
+ .fnc-legend-title { display: flex; align-items: center; gap: 6px; font-size: 13px; }
2169
+ .fnc-legend-title b { color: var(--fnc-text); font-weight: 700; }
2170
+ .fnc-legend-values { display: flex; flex-wrap: wrap; gap: 0 10px; }
2171
+ .fnc-legend-values b { font-weight: 600; }
2172
+ /* Indicator legend rows */
2173
+ .fnc-ind {
2174
+ display: flex;
2175
+ flex-wrap: wrap;
2176
+ align-items: center;
2177
+ gap: 0 8px;
2178
+ min-height: 20px;
2179
+ pointer-events: auto;
2180
+ width: fit-content;
2181
+ max-width: 100%;
2182
+ border-radius: 6px;
2183
+ }
2184
+ .fnc-ind-title { color: var(--fnc-text-2); }
2185
+ .fnc-ind-hidden .fnc-ind-title { opacity: 0.5; text-decoration: line-through; }
2186
+ .fnc-ind-eye { margin-left: -4px; }
2187
+ .fnc-ind-hidden .fnc-ind-eye { color: var(--fnc-text-2); opacity: 0.7; }
2188
+ .fnc-ind-collapse, .fnc-ind-handle {
2189
+ display: inline-flex;
2190
+ align-items: center;
2191
+ justify-content: center;
2192
+ width: fit-content;
2193
+ height: 20px;
2194
+ margin-top: 2px;
2195
+ border-radius: 6px;
2196
+ color: var(--fnc-text-2);
2197
+ pointer-events: auto;
2198
+ }
2199
+ .fnc-ind-collapse { width: 24px; margin-left: -4px; }
2200
+ .fnc-ind-collapse:hover, .fnc-ind-handle:hover { background: var(--fnc-surface); color: var(--fnc-text); }
2201
+ /* Collapsed: a small horizontal bar centered in a comfortable tap target. */
2202
+ .fnc-ind-handle { width: 40px; margin-left: -6px; }
2203
+ .fnc-ind-handle span { width: 24px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.55; }
2204
+ .fnc-ind-handle:hover span { opacity: 0.9; }
2205
+ .fnc-ind b { font-weight: 600; }
2206
+ .fnc-ind-actions { display: inline-flex; gap: 2px; opacity: 0; transition: opacity 0.12s; }
2207
+ .fnc-ind:hover .fnc-ind-actions, .fnc-ind:focus-within .fnc-ind-actions { opacity: 1; }
2208
+ .fnc-ind-btn {
2209
+ display: inline-flex;
2210
+ align-items: center;
2211
+ justify-content: center;
2212
+ width: 22px;
2213
+ height: 22px;
2214
+ border-radius: 6px;
2215
+ color: var(--fnc-text-2);
2216
+ }
2217
+ .fnc-ind-btn:hover { background: var(--fnc-surface); color: var(--fnc-text); }
2218
+ .fnc-ind { cursor: pointer; }
2219
+ .fnc-ind:hover .fnc-ind-title { color: var(--fnc-text); }
2220
+ /* Touch: no tiny icons; tapping the row opens settings (which can also remove). */
2221
+ @media (hover: none) { .fnc-ind-actions { display: none; } }
2222
+ .fnc-pane-legend { right: auto; }
2223
+
2224
+ /* \u2500\u2500 Thin, quiet scrollbars for every scroll area in the widget \u2500\u2500 */
2225
+ .fnc-root .fnc-scroll, .fnc-root .fnc-menu, .fnc-root .fnc-dialog form {
2226
+ scrollbar-width: thin;
2227
+ scrollbar-color: var(--fnc-border) transparent;
2228
+ }
2229
+ .fnc-root ::-webkit-scrollbar { width: 10px; height: 10px; }
2230
+ .fnc-root ::-webkit-scrollbar-track { background: transparent; }
2231
+ .fnc-root ::-webkit-scrollbar-thumb {
2232
+ background: var(--fnc-border);
2233
+ border-radius: 10px;
2234
+ border: 3px solid transparent;
2235
+ background-clip: padding-box;
2236
+ }
2237
+ .fnc-root ::-webkit-scrollbar-thumb:hover { background-color: var(--fnc-text-2); }
2238
+
2239
+ /* \u2500\u2500 Interval picker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2240
+ .fnc-interval-picker { position: relative; display: flex; align-items: center; min-width: 0; }
2241
+ .fnc-interval-trigger { gap: 2px; padding: 0 6px; }
2242
+ .fnc-interval-current { display: none; font-size: 13px; color: var(--fnc-text); }
2243
+ .fnc-interval-trigger-active { background: var(--fnc-surface); }
2244
+ .fnc-interval-trigger-active .fnc-interval-current { display: inline; }
2245
+ .fnc-chip:disabled { opacity: 0.35; cursor: default; background: none; }
2246
+ .fnc-interval-menu {
2247
+ width: 208px;
2248
+ max-height: min(440px, 70vh);
2249
+ overflow-y: auto;
2250
+ gap: 0;
2251
+ }
2252
+ .fnc-menu-group + .fnc-menu-group { border-top: 1px solid var(--fnc-border); margin-top: 4px; padding-top: 4px; }
2253
+ .fnc-menu-title { padding: 8px 10px 4px; font-size: 11px; font-weight: 700; color: var(--fnc-text-2); letter-spacing: 0.02em; }
2254
+ .fnc-menu-row { display: flex; align-items: center; border-radius: var(--fnc-radius); }
2255
+ .fnc-menu-row:hover { background: var(--fnc-surface); }
2256
+ .fnc-menu-row .fnc-menu-item { flex: 1; background: none; }
2257
+ .fnc-menu-row .fnc-menu-item[aria-checked='true'] { color: var(--fnc-accent); font-weight: 700; }
2258
+ .fnc-menu-item:disabled { opacity: 0.35; cursor: default; }
2259
+ .fnc-star {
2260
+ display: flex;
2261
+ align-items: center;
2262
+ justify-content: center;
2263
+ width: 32px;
2264
+ height: 32px;
2265
+ flex: none;
2266
+ color: var(--fnc-text-2);
2267
+ opacity: 0;
2268
+ transition: opacity 0.12s, color 0.12s;
2269
+ }
2270
+ .fnc-menu-row:hover .fnc-star, .fnc-star:focus-visible { opacity: 1; }
2271
+ .fnc-star:hover { color: #FF9F0A; }
2272
+ .fnc-menu-row.is-favorite .fnc-star { opacity: 1; color: #FF9F0A; }
2273
+ @media (hover: none) { .fnc-star { opacity: 0.5; } }
2274
+
2275
+ /* Interval buttons keep their width; host content in the toolbar slot shrinks first. */
2276
+ .fnc-interval-picker { flex: none; }
2277
+
2278
+ /* \u2500\u2500 Slots: host-app UI inside the widget (collapse when empty) \u2500\u2500 */
2279
+ .fnc-slot:empty { display: none; }
2280
+ .fnc-slot-host { display: flex; align-self: stretch; min-width: 0; flex: 0 1 auto; }
2281
+ .fnc-slot-toolbar { position: relative; display: flex; align-items: center; min-width: 0; padding-left: 9px; margin-left: 4px; }
2282
+ .fnc-slot-toolbar::before {
2283
+ content: '';
2284
+ position: absolute;
2285
+ left: 0;
2286
+ top: 50%;
2287
+ width: 1px;
2288
+ height: 20px;
2289
+ margin-top: -10px;
2290
+ background: var(--fnc-border);
2291
+ }
2292
+ .fnc-slot-below-toolbar, .fnc-slot-footer { flex: none; min-width: 0; }
2293
+ .fnc-slot-toolbar-end { display: flex; align-items: center; gap: 2px; flex: none; }
2294
+
2295
+ /* \u2500\u2500 Drawing bar fold (desktop) \u2500\u2500 */
2296
+ .fnc-drawbar .fnc-spacer { flex: 1; min-height: 8px; }
2297
+ .fnc-drawbar[data-collapsed='true'] { display: none; }
2298
+ .fnc-drawbar-tab {
2299
+ align-self: center;
2300
+ display: flex;
2301
+ align-items: center;
2302
+ justify-content: center;
2303
+ width: 16px;
2304
+ height: 56px;
2305
+ border: 1px solid var(--fnc-border);
2306
+ border-left: 0;
2307
+ border-radius: 0 8px 8px 0;
2308
+ color: var(--fnc-text-2);
2309
+ background: var(--fnc-bg);
2310
+ }
2311
+ .fnc-drawbar-tab:hover { color: var(--fnc-text); background: var(--fnc-surface); }
2312
+
2313
+ /* \u2500\u2500 Symbol search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2314
+ .fnc-dialog.fnc-search-dialog { width: min(880px, calc(100% - 32px)); height: min(640px, 100%); max-height: none; }
2315
+ .fnc-sr-body { display: flex; flex: 1; min-height: 0; }
2316
+ .fnc-sr-side {
2317
+ width: 196px;
2318
+ flex: none;
2319
+ padding: 8px;
2320
+ border-right: 1px solid var(--fnc-border);
2321
+ overflow-y: auto;
2322
+ }
2323
+ .fnc-sr-side-title { padding: 12px 10px 6px; font-size: 12px; font-weight: 700; color: var(--fnc-text-2); }
2324
+ .fnc-sr-side-item {
2325
+ display: flex;
2326
+ align-items: center;
2327
+ gap: 8px;
2328
+ width: 100%;
2329
+ height: 36px;
2330
+ padding: 0 10px;
2331
+ border-radius: var(--fnc-radius);
2332
+ font-size: 14px;
2333
+ color: var(--fnc-text);
2334
+ text-align: left;
2335
+ }
2336
+ .fnc-sr-side-item span { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2337
+ .fnc-sr-side-item em { font-style: normal; font-size: 12px; color: var(--fnc-text-2); }
2338
+ .fnc-sr-side-item:hover { background: var(--fnc-surface); }
2339
+ .fnc-sr-side-item[aria-pressed='true'] { background: var(--fnc-surface); font-weight: 700; }
2340
+ .fnc-chip-token {
2341
+ display: inline-flex;
2342
+ align-items: center;
2343
+ gap: 2px;
2344
+ flex: none;
2345
+ height: 28px;
2346
+ padding: 0 4px 0 10px;
2347
+ border: 1px solid var(--fnc-border);
2348
+ border-radius: 8px;
2349
+ font-size: 13px;
2350
+ font-weight: 700;
2351
+ color: var(--fnc-text);
2352
+ }
2353
+ .fnc-chip-token button { display: flex; color: var(--fnc-text-2); }
2354
+ .fnc-chip-token button:hover { color: var(--fnc-text); }
2355
+ .fnc-kbd {
2356
+ display: inline-flex;
2357
+ align-items: center;
2358
+ height: 22px;
2359
+ padding: 0 6px;
2360
+ border: 1px solid var(--fnc-border);
2361
+ border-radius: 6px;
2362
+ font: inherit;
2363
+ font-size: 11px;
2364
+ font-weight: 600;
2365
+ color: var(--fnc-text-2);
2366
+ background: var(--fnc-surface);
2367
+ }
2368
+ .fnc-sr-star {
2369
+ display: flex;
2370
+ align-items: center;
2371
+ justify-content: center;
2372
+ width: 28px;
2373
+ height: 28px;
2374
+ flex: none;
2375
+ margin-right: -4px;
2376
+ color: var(--fnc-text-2);
2377
+ opacity: 0.45;
2378
+ border-radius: 6px;
2379
+ }
2380
+ .fnc-sr-star:hover { opacity: 1; color: #ff9f0a; }
2381
+ .fnc-sr-star.is-on { opacity: 1; color: #ff9f0a; }
2382
+ .fnc-sr-spark { flex: none; }
2383
+ .fnc-sr-quote { display: flex; flex-direction: column; align-items: flex-end; flex: none; min-width: 84px; line-height: 1.25; font-variant-numeric: tabular-nums; }
2384
+ .fnc-sr-quote b { font-size: 14px; font-weight: 700; }
2385
+ .fnc-sr-quote span { font-size: 12px; font-weight: 600; }
2386
+ .fnc-search mark { background: none; color: var(--fnc-accent); font-weight: 700; }
2387
+ .fnc-tabs {
2388
+ display: flex;
2389
+ gap: 4px;
2390
+ padding: 8px 12px;
2391
+ border-bottom: 1px solid var(--fnc-border);
2392
+ overflow-x: auto;
2393
+ scrollbar-width: none;
2394
+ flex: none;
2395
+ }
2396
+ .fnc-tabs::-webkit-scrollbar { display: none; }
2397
+ .fnc-tab {
2398
+ height: 32px;
2399
+ padding: 0 12px;
2400
+ border-radius: 999px;
2401
+ font-size: 13px;
2402
+ font-weight: 600;
2403
+ color: var(--fnc-text-2);
2404
+ white-space: nowrap;
2405
+ flex: none;
2406
+ }
2407
+ .fnc-tab:hover { color: var(--fnc-text); }
2408
+ .fnc-tab[aria-selected='true'] { background: var(--fnc-text); color: var(--fnc-bg); }
2409
+ .fnc-sr-list { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 8px 8px; }
2410
+ .fnc-sr-section {
2411
+ display: flex;
2412
+ align-items: center;
2413
+ justify-content: space-between;
2414
+ padding: 12px 8px 6px;
2415
+ font-size: 12px;
2416
+ font-weight: 700;
2417
+ color: var(--fnc-text-2);
2418
+ }
2419
+ .fnc-link { font-size: 12px; color: var(--fnc-text-2); }
2420
+ .fnc-link:hover { color: var(--fnc-text); text-decoration: underline; }
2421
+ .fnc-sr-row {
2422
+ width: 100%;
2423
+ text-align: left;
2424
+ display: flex;
2425
+ align-items: center;
2426
+ gap: 12px;
2427
+ min-height: 56px;
2428
+ padding: 8px;
2429
+ border-radius: 12px;
2430
+ cursor: pointer;
2431
+ }
2432
+ .fnc-sr-row[aria-selected='true'] { background: var(--fnc-surface); }
2433
+ .fnc-sr-main { display: flex; flex-direction: column; min-width: 0; flex: 1; }
2434
+ .fnc-sr-name { font-size: 15px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2435
+ .fnc-sr-meta { font-size: 12px; color: var(--fnc-text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2436
+ .fnc-sr-name mark, .fnc-sr-meta mark { background: none; color: var(--fnc-accent); font-weight: 700; }
2437
+ .fnc-sr-current { margin-left: 6px; font-size: 11px; font-weight: 700; color: var(--fnc-accent); }
2438
+ .fnc-sr-skeleton {
2439
+ height: 48px;
2440
+ margin: 8px;
2441
+ border-radius: 12px;
2442
+ background: linear-gradient(90deg, var(--fnc-surface) 0%, var(--fnc-bg) 50%, var(--fnc-surface) 100%);
2443
+ background-size: 200% 100%;
2444
+ animation: fnc-shimmer 1.2s ease-in-out infinite;
2445
+ }
2446
+ @keyframes fnc-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
2447
+ .fnc-sr-footer {
2448
+ display: flex;
2449
+ align-items: center;
2450
+ gap: 16px;
2451
+ padding: 10px 16px;
2452
+ border-top: 1px solid var(--fnc-border);
2453
+ font-size: 12px;
2454
+ color: var(--fnc-text-2);
2455
+ flex: none;
2456
+ }
2457
+
2458
+ .fnc-up { color: var(--fnc-up); }
2459
+ .fnc-down { color: var(--fnc-down); }
2460
+ .fnc-flat { color: var(--fnc-text); }
2461
+ .fnc-spinner {
2462
+ width: 12px;
2463
+ height: 12px;
2464
+ border: 2px solid var(--fnc-border);
2465
+ border-top-color: var(--fnc-text-2);
2466
+ border-radius: 50%;
2467
+ animation: fnc-spin 0.8s linear infinite;
2468
+ }
2469
+ @keyframes fnc-spin { to { transform: rotate(360deg); } }
2470
+
2471
+ .fnc-attribution {
2472
+ position: absolute;
2473
+ left: 10px;
2474
+ z-index: 1;
2475
+ display: flex;
2476
+ align-items: center;
2477
+ gap: 6px;
2478
+ height: 28px;
2479
+ padding: 0 10px 0 4px;
2480
+ border-radius: 14px;
2481
+ color: var(--fnc-text);
2482
+ font-weight: 800;
2483
+ font-size: 13px;
2484
+ letter-spacing: -0.3px;
2485
+ text-decoration: none;
2486
+ pointer-events: auto;
2487
+ opacity: 0.85;
2488
+ transition: opacity 0.15s, background 0.15s;
2489
+ }
2490
+ .fnc-attribution:hover { opacity: 1; background: var(--fnc-bg); box-shadow: 0 0 0 1px var(--fnc-border); }
2491
+ .fnc-attribution svg { flex: none; }
2492
+ .fnc-attribution span { max-width: 0; overflow: hidden; transition: max-width 0.2s; }
2493
+ .fnc-attribution:hover span, .fnc-attribution:focus-visible span { max-width: 80px; }
2494
+
2495
+ .fnc-nav {
2496
+ position: absolute;
2497
+ display: flex;
2498
+ gap: 4px;
2499
+ padding: 4px;
2500
+ transform: translateX(-50%);
2501
+ border-radius: calc(var(--fnc-radius) + 4px);
2502
+ background: var(--fnc-bg);
2503
+ box-shadow: 0 2px 12px rgb(0 0 0 / 0.12), 0 0 0 1px var(--fnc-border);
2504
+ opacity: 0;
2505
+ transition: opacity 0.15s;
2506
+ }
2507
+ .fnc-body:hover .fnc-nav, .fnc-nav:focus-within { opacity: 1; }
2508
+ .fnc-nav .fnc-btn { width: 32px; height: 32px; }
2509
+ .fnc-realtime {
2510
+ position: absolute;
2511
+ display: flex;
2512
+ align-items: center;
2513
+ justify-content: center;
2514
+ width: 32px;
2515
+ height: 32px;
2516
+ border-radius: 50%;
2517
+ background: var(--fnc-bg);
2518
+ color: var(--fnc-text);
2519
+ box-shadow: 0 2px 12px rgb(0 0 0 / 0.12), 0 0 0 1px var(--fnc-border);
2520
+ }
2521
+ @media (hover: none) {
2522
+ .fnc-nav { display: none; }
2523
+ }
2524
+
2525
+ /* \u2500\u2500 Symbol search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2526
+ .fnc-backdrop {
2527
+ position: absolute;
2528
+ inset: 0;
2529
+ z-index: 10;
2530
+ display: flex;
2531
+ justify-content: center;
2532
+ align-items: center;
2533
+ padding: 24px 16px;
2534
+ background: rgb(0 0 0 / 0.32);
2535
+ }
2536
+ .fnc-dialog {
2537
+ display: flex;
2538
+ flex-direction: column;
2539
+ width: min(480px, calc(100% - 32px));
2540
+ max-height: min(560px, 100%);
2541
+ border-radius: 16px;
2542
+ background: var(--fnc-bg);
2543
+ box-shadow: 0 12px 40px rgb(0 0 0 / 0.2);
2544
+ overflow: hidden;
2545
+ }
2546
+ .fnc-search {
2547
+ display: flex;
2548
+ align-items: center;
2549
+ gap: 8px;
2550
+ height: 56px;
2551
+ padding: 0 8px 0 16px;
2552
+ border-bottom: 1px solid var(--fnc-border);
2553
+ color: var(--fnc-text-2);
2554
+ }
2555
+ .fnc-search input {
2556
+ flex: 1;
2557
+ min-width: 0;
2558
+ height: 100%;
2559
+ border: 0;
2560
+ outline: 0;
2561
+ background: transparent;
2562
+ color: var(--fnc-text);
2563
+ font: inherit;
2564
+ font-size: 16px;
2565
+ }
2566
+ .fnc-search input::placeholder { color: var(--fnc-text-2); }
2567
+ .fnc-dialog-wide { width: min(600px, calc(100% - 32px)); }
2568
+ .fnc-dialog form { overflow-y: auto; }
2569
+ .fnc-section { font-size: 12px; font-weight: 700; color: var(--fnc-text-2); margin-top: 2px; }
2570
+ .fnc-rows { display: flex; flex-direction: column; gap: 8px; }
2571
+ .fnc-row { display: flex; align-items: center; gap: 8px; min-height: 40px; }
2572
+ .fnc-row-label { width: 44px; flex: none; font-size: 13px; color: var(--fnc-text-2); white-space: nowrap; }
2573
+ .fnc-row-label-wide { width: 88px; }
2574
+ .fnc-check { width: 18px; height: 18px; flex: none; accent-color: var(--fnc-accent); margin: 0; cursor: pointer; }
2575
+ .fnc-color {
2576
+ width: 32px;
2577
+ height: 32px;
2578
+ flex: none;
2579
+ padding: 0;
2580
+ border: 1px solid var(--fnc-border);
2581
+ border-radius: var(--fnc-radius);
2582
+ background: none;
2583
+ cursor: pointer;
2584
+ }
2585
+ .fnc-color::-webkit-color-swatch-wrapper { padding: 3px; }
2586
+ .fnc-color::-webkit-color-swatch { border: 0; border-radius: 4px; }
2587
+ .fnc-select, .fnc-input {
2588
+ height: 40px;
2589
+ min-width: 0;
2590
+ padding: 0 10px;
2591
+ border: 1px solid var(--fnc-border);
2592
+ border-radius: var(--fnc-radius);
2593
+ background: var(--fnc-bg);
2594
+ color: var(--fnc-text);
2595
+ font: inherit;
2596
+ font-size: 14px;
2597
+ outline: none;
2598
+ }
2599
+ .fnc-select:focus, .fnc-input:focus { border-color: var(--fnc-accent); }
2600
+ .fnc-input { flex: 1; font-size: 16px; }
2601
+ .fnc-input-period { flex: 0 0 72px; text-align: right; }
2602
+ .fnc-select-width { flex: 0 0 72px; }
2603
+ .fnc-select-source { flex: 1; }
2604
+ .fnc-toggle-row { justify-content: space-between; min-height: 36px; cursor: pointer; font-size: 14px; }
2605
+ .fnc-switch {
2606
+ appearance: none;
2607
+ -webkit-appearance: none;
2608
+ position: relative;
2609
+ width: 40px;
2610
+ height: 24px;
2611
+ flex: none;
2612
+ margin: 0;
2613
+ border-radius: 12px;
2614
+ background: var(--fnc-border);
2615
+ cursor: pointer;
2616
+ transition: background 0.15s;
2617
+ }
2618
+ .fnc-switch::after {
2619
+ content: '';
2620
+ position: absolute;
2621
+ top: 3px;
2622
+ left: 3px;
2623
+ width: 18px;
2624
+ height: 18px;
2625
+ border-radius: 50%;
2626
+ background: #fff;
2627
+ box-shadow: 0 1px 3px rgb(0 0 0 / 0.2);
2628
+ transition: transform 0.15s;
2629
+ }
2630
+ .fnc-switch:checked { background: var(--fnc-accent); }
2631
+ .fnc-switch:checked::after { transform: translateX(16px); }
2632
+ .fnc-switch:focus-visible { outline: 2px solid var(--fnc-accent); outline-offset: 2px; }
2633
+ .fnc-add-row {
2634
+ height: 40px;
2635
+ border: 1px dashed var(--fnc-border);
2636
+ border-radius: var(--fnc-radius);
2637
+ color: var(--fnc-text-2);
2638
+ font-size: 14px;
2639
+ font-weight: 600;
2640
+ }
2641
+ .fnc-add-row:hover { color: var(--fnc-text); border-color: var(--fnc-text-2); }
2642
+ .fnc-empty { padding: 32px 16px; text-align: center; color: var(--fnc-text-2); }
2643
+ .fnc-dialog-head {
2644
+ display: flex;
2645
+ align-items: center;
2646
+ justify-content: space-between;
2647
+ height: 56px;
2648
+ padding: 0 8px 0 20px;
2649
+ border-bottom: 1px solid var(--fnc-border);
2650
+ font-size: 16px;
2651
+ }
2652
+ .fnc-group { padding: 12px 12px 4px; font-size: 12px; font-weight: 600; color: var(--fnc-text-2); }
2653
+ .fnc-form { display: flex; flex-direction: column; gap: 14px; padding: 20px; }
2654
+ .fnc-field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--fnc-text-2); }
2655
+ .fnc-field input {
2656
+ height: 44px;
2657
+ padding: 0 14px;
2658
+ border: 1px solid var(--fnc-border);
2659
+ border-radius: var(--fnc-radius);
2660
+ background: var(--fnc-bg);
2661
+ color: var(--fnc-text);
2662
+ font: inherit;
2663
+ font-size: 16px;
2664
+ outline: none;
2665
+ }
2666
+ .fnc-field input:focus { border-color: var(--fnc-accent); }
2667
+ /* Actions stay pinned to the bottom of long, scrolling dialogs (indicator / chart settings). */
2668
+ .fnc-form-actions {
2669
+ position: sticky;
2670
+ bottom: -20px;
2671
+ display: flex;
2672
+ gap: 8px;
2673
+ margin: 6px -20px -20px;
2674
+ padding: 12px 20px 20px;
2675
+ border-top: 1px solid var(--fnc-border);
2676
+ background: var(--fnc-bg);
2677
+ }
2678
+ .fnc-action {
2679
+ flex: 1;
2680
+ height: 48px;
2681
+ border-radius: var(--fnc-radius);
2682
+ background: var(--fnc-surface);
2683
+ font-size: 15px;
2684
+ font-weight: 600;
2685
+ }
2686
+ .fnc-action-primary { background: var(--fnc-accent); color: #fff; }
2687
+ .fnc-action-danger { flex: 0 0 auto; padding: 0 16px; color: var(--fnc-danger, #F04452); background: none; }
2688
+
2689
+ /* \u2500\u2500 Mobile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
2690
+ @media (max-width: 640px) {
2691
+ .fnc-toolbar { padding: 0 4px; }
2692
+ .fnc-symbol { max-width: 140px; padding: 0 8px 0 6px; }
2693
+ .fnc-chip { padding: 0 8px; }
2694
+ .fnc-btn-screenshot { display: none; }
2695
+ .fnc-btn-label span { display: none; }
2696
+ .fnc-intervals { display: none; }
2697
+ .fnc-interval-current { display: inline; }
2698
+ .fnc-interval-menu { left: auto; right: -40px; }
2699
+ .fnc-btn-drawbar { display: flex; }
2700
+ .fnc-drawbar-tab, .fnc-drawbar-collapse { display: none; }
2701
+ .fnc-drawbar[data-collapsed='true'] { display: flex; }
2702
+ /* The drawing bar slides over the chart instead of taking width. */
2703
+ .fnc-drawbar-host { position: absolute; left: 0; top: 0; bottom: 0; z-index: 5; pointer-events: none; }
2704
+ .fnc-drawbar {
2705
+ pointer-events: auto;
2706
+ transform: translateX(-100%);
2707
+ transition: transform 0.18s ease;
2708
+ box-shadow: 4px 0 16px rgb(0 0 0 / 0.08);
2709
+ }
2710
+ .fnc-drawbar[data-open='true'] { transform: none; }
2711
+ .fnc-selbar { top: auto; bottom: 56px; }
2712
+ .fnc-legend { left: 10px; right: 64px; }
2713
+ .fnc-backdrop { padding: 0; align-items: stretch; }
2714
+ .fnc-dialog { width: 100%; max-height: none; border-radius: 0; }
2715
+ .fnc-dialog.fnc-search-dialog { width: 100%; height: 100%; }
2716
+ .fnc-sr-footer, .fnc-sr-side, .fnc-hide-mobile, .fnc-sr-spark { display: none; }
2717
+ /* The interval button must stay whole; the symbol name truncates instead. */
2718
+ .fnc-symbol { flex: 0 1 auto; min-width: 0; }
2719
+ /* MA rows wrap to two lines: [label \u2713 color width \xD7] / [source period] */
2720
+ .fnc-rows .fnc-row { flex-wrap: wrap; row-gap: 8px; padding-bottom: 10px; border-bottom: 1px solid var(--fnc-border); }
2721
+ /* Basis = row \u2212 indent(52) \u2212 gap(8) \u2212 period(72): too wide for line 1, so it always wraps. */
2722
+ .fnc-rows .fnc-select-source { order: 10; flex: 0 0 calc(100% - 132px); margin-left: 52px; }
2723
+ .fnc-rows .fnc-input-period { order: 11; }
2724
+ .fnc-rows .fnc-ind-btn { margin-left: auto; }
2725
+ }
2726
+ `
2727
+ );
2728
+ var STYLE_ID = "fnc-styles";
2729
+ function injectStyles(doc = document) {
2730
+ if (doc.getElementById(STYLE_ID)) return;
2731
+ const style = doc.createElement("style");
2732
+ style.id = STYLE_ID;
2733
+ style.textContent = CSS;
2734
+ doc.head.appendChild(style);
2735
+ }
2736
+
2737
+ // src/trading-chart.tsx
2738
+ var DEFAULT_INTERVAL_GROUPS = [
2739
+ { id: "minutes", intervals: ["1", "5", "15", "30"] },
2740
+ { id: "hours", intervals: ["60", "240"] },
2741
+ { id: "days", intervals: ["1D", "1W", "1M", "3M", "6M", "12M"] }
2742
+ ];
2743
+ var DEFAULT_FAVORITE_INTERVALS = ["1", "5", "15", "60", "240", "1D"];
2744
+ var stockLike = (i3) => i3.type === "stock" || i3.type === "etf" || i3.type === "index";
2745
+ var DEFAULT_SEARCH_CATEGORIES = [
2746
+ { id: "all", label: { ko: "\uC804\uCCB4", en: "All" }, match: () => true },
2747
+ { id: "kr-stock", label: { ko: "\uAD6D\uB0B4\uC8FC\uC2DD", en: "KR stocks" }, match: (i3) => stockLike(i3) && i3.country === "KR" },
2748
+ { id: "global-stock", label: { ko: "\uD574\uC678\uC8FC\uC2DD", en: "Global stocks" }, match: (i3) => stockLike(i3) && i3.country !== "KR" },
2749
+ { id: "crypto", label: { ko: "\uCF54\uC778", en: "Crypto" }, match: (i3) => i3.type === "crypto" },
2750
+ { id: "futures", label: { ko: "\uC120\uBB3C", en: "Futures" }, match: (i3) => i3.type === "futures" }
2751
+ ];
2752
+ var THEME_VARS = {
2753
+ background: "--fnc-bg",
2754
+ surface: "--fnc-surface",
2755
+ text: "--fnc-text",
2756
+ textSecondary: "--fnc-text-2",
2757
+ border: "--fnc-border",
2758
+ up: "--fnc-up",
2759
+ down: "--fnc-down",
2760
+ accent: "--fnc-accent",
2761
+ fontFamily: "--fnc-font"
2762
+ };
2763
+ function applyThemeVars(el, theme) {
2764
+ for (const [key, cssVar] of Object.entries(THEME_VARS)) {
2765
+ el.style.setProperty(cssVar, String(theme[key]));
2766
+ }
2767
+ el.style.setProperty("--fnc-radius", `${theme.radius}px`);
2768
+ }
2769
+ function div(className, parent) {
2770
+ const el = document.createElement("div");
2771
+ el.className = className;
2772
+ parent?.appendChild(el);
2773
+ return el;
2774
+ }
2775
+ function createTradingChart(container, options) {
2776
+ const {
2777
+ datafeed,
2778
+ symbol,
2779
+ resolution = "1D",
2780
+ intervalGroups = DEFAULT_INTERVAL_GROUPS,
2781
+ favoriteIntervals,
2782
+ onFavoriteIntervalsChange,
2783
+ searchCategories = DEFAULT_SEARCH_CATEGORIES,
2784
+ storageKey = "funnest-charts",
2785
+ searchHotkey = true,
2786
+ symbolLogo = true,
2787
+ drawingToolbar = true,
2788
+ chartType = "candles",
2789
+ attribution = true,
2790
+ colorScheme = "system",
2791
+ ...chartOptions
2792
+ } = options;
2793
+ const locale = chartOptions.locale ?? "ko";
2794
+ const displayTimezone = chartOptions.displayTimezone ?? (locale === "ko" ? "Asia/Seoul" : Intl.DateTimeFormat().resolvedOptions().timeZone);
2795
+ const storage = createStorage(storageKey);
2796
+ injectStyles(container.ownerDocument);
2797
+ const win = container.ownerDocument.defaultView;
2798
+ const darkQuery = win?.matchMedia?.("(prefers-color-scheme: dark)") ?? null;
2799
+ let scheme = colorScheme;
2800
+ let themePatch = { ...chartOptions.theme };
2801
+ const isDark = () => scheme === "system" ? !!darkQuery?.matches : scheme === "dark";
2802
+ const resolveTheme = () => ({ ...isDark() ? darkTheme : lightTheme, ...themePatch });
2803
+ let theme = resolveTheme();
2804
+ const root = div("fnc-root", container);
2805
+ const applyScheme = () => {
2806
+ root.dataset.colorScheme = isDark() ? "dark" : "light";
2807
+ root.style.colorScheme = isDark() ? "dark" : "light";
2808
+ applyThemeVars(root, theme);
2809
+ };
2810
+ applyScheme();
2811
+ const toolbarHost = div("fnc-toolbar-host", root);
2812
+ const slots = {
2813
+ toolbar: div("fnc-slot fnc-slot-toolbar"),
2814
+ toolbarEnd: div("fnc-slot fnc-slot-toolbar-end"),
2815
+ belowToolbar: div("fnc-slot fnc-slot-below-toolbar", root),
2816
+ footer: div("fnc-slot fnc-slot-footer")
2817
+ };
2818
+ const body = div("fnc-body", root);
2819
+ const drawbarHost = drawingToolbar ? div("fnc-drawbar-host", body) : null;
2820
+ const stage = div("fnc-stage", body);
2821
+ const chartHost = div("fnc-chart-host", stage);
2822
+ const overlayHost = div("fnc-overlay", stage);
2823
+ root.appendChild(slots.footer);
2824
+ const chart = createChart(chartHost, { ...chartOptions, theme, datafeed, displayTimezone });
2825
+ const refreshScheme = () => {
2826
+ theme = resolveTheme();
2827
+ applyScheme();
2828
+ chart.setTheme(theme);
2829
+ };
2830
+ const onSchemeChange = () => scheme === "system" && refreshScheme();
2831
+ darkQuery?.addEventListener?.("change", onSchemeChange);
2832
+ chart.setChartType(chartType);
2833
+ const store = new Store({
2834
+ locale,
2835
+ symbol: null,
2836
+ resolution,
2837
+ loading: false,
2838
+ layout: chart.layout,
2839
+ atRealtime: true,
2840
+ fullscreen: false,
2841
+ searchOpen: false,
2842
+ chartType,
2843
+ drawingTool: null,
2844
+ selection: null,
2845
+ drawingCount: 0,
2846
+ drawbarOpen: false,
2847
+ magnet: chart.magnetEnabled,
2848
+ indicators: chart.getIndicators(),
2849
+ dialog: null,
2850
+ favoriteIntervals: storage.get("favorite-intervals", [...favoriteIntervals ?? DEFAULT_FAVORITE_INTERVALS]),
2851
+ drawbarCollapsed: storage.get("drawbar-collapsed", false)
2852
+ });
2853
+ const syncRealtime = () => store.set({ atRealtime: chart.isAtRealtime() });
2854
+ const offs = [
2855
+ chart.on("symbolChange", (info) => store.set({ symbol: info, resolution: chart.currentResolution ?? resolution })),
2856
+ chart.on("loadingChange", (loading) => store.set({ loading })),
2857
+ chart.on("layoutChange", (layout) => store.set({ layout })),
2858
+ chart.on("visibleRangeChange", syncRealtime),
2859
+ chart.on("dataChange", syncRealtime),
2860
+ chart.on("drawingToolChange", (drawingTool) => store.set({ drawingTool })),
2861
+ chart.on("selectionChange", (selection) => store.set({ selection })),
2862
+ chart.on("drawingsChange", (drawings) => store.set({ drawingCount: drawings.length })),
2863
+ chart.on("indicatorsChange", (indicators) => store.set({ indicators })),
2864
+ chart.on("magnetChange", (magnet) => store.set({ magnet })),
2865
+ chart.on("chartTypeChange", (chartType2) => store.set({ chartType: chartType2 }))
2866
+ ];
2867
+ const load = (sym, res) => {
2868
+ store.set({ resolution: res });
2869
+ return chart.setSymbol(sym, res).catch((error) => {
2870
+ console.error("funnest-charts: failed to load", sym, res, error);
2871
+ store.set({ loading: false });
2872
+ });
2873
+ };
2874
+ const isNativeFullscreen = () => document.fullscreenElement === root;
2875
+ const onFullscreenChange = () => store.set({ fullscreen: isNativeFullscreen() || root.classList.contains("fnc-pseudo-fullscreen") });
2876
+ const onKeyDown = (e3) => {
2877
+ const s3 = store.get();
2878
+ if (searchHotkey && (e3.ctrlKey || e3.metaKey) && e3.key.toLowerCase() === "k" && datafeed.searchSymbols && !s3.dialog) {
2879
+ e3.preventDefault();
2880
+ store.set({ searchOpen: true });
2881
+ return;
2882
+ }
2883
+ if (e3.key === "Escape" && !e3.defaultPrevented && (s3.searchOpen || s3.dialog)) {
2884
+ e3.preventDefault();
2885
+ store.set(s3.searchOpen ? { searchOpen: false } : { dialog: null });
2886
+ return;
2887
+ }
2888
+ if (e3.key === "Escape" && root.classList.contains("fnc-pseudo-fullscreen") && !s3.searchOpen && !s3.dialog) {
2889
+ root.classList.remove("fnc-pseudo-fullscreen");
2890
+ onFullscreenChange();
2891
+ }
2892
+ };
2893
+ const onKeyUp = (e3) => {
2894
+ const s3 = store.get();
2895
+ if (e3.key === "Escape" && (s3.searchOpen || s3.dialog)) store.set(s3.searchOpen ? { searchOpen: false } : { dialog: null });
2896
+ };
2897
+ document.addEventListener("fullscreenchange", onFullscreenChange);
2898
+ document.addEventListener("keydown", onKeyDown);
2899
+ document.addEventListener("keyup", onKeyUp);
2900
+ const ctx = {
2901
+ root,
2902
+ chart,
2903
+ datafeed,
2904
+ store,
2905
+ storage,
2906
+ intervalGroups,
2907
+ searchCategories,
2908
+ displayTimezone,
2909
+ showLogo: symbolLogo,
2910
+ drawingToolbar,
2911
+ attribution,
2912
+ slots,
2913
+ setDrawbarCollapsed: (collapsed) => {
2914
+ store.set({ drawbarCollapsed: collapsed });
2915
+ storage.set("drawbar-collapsed", collapsed);
2916
+ },
2917
+ selectSymbol: (sym) => void load(sym, store.get().resolution),
2918
+ selectResolution: (res) => {
2919
+ const current = store.get().symbol;
2920
+ if (current) void load(current.symbol, res);
2921
+ },
2922
+ toggleFavoriteInterval: (res) => {
2923
+ const current = store.get().favoriteIntervals;
2924
+ const next = current.includes(res) ? current.filter((r3) => r3 !== res) : [...current, res];
2925
+ store.set({ favoriteIntervals: next });
2926
+ storage.set("favorite-intervals", next);
2927
+ onFavoriteIntervalsChange?.(next);
2928
+ },
2929
+ setSearchOpen: (open) => store.set({ searchOpen: open }),
2930
+ screenshot: () => {
2931
+ const s3 = store.get();
2932
+ chart.takeScreenshot().toBlob((blob) => {
2933
+ if (!blob) return;
2934
+ const a4 = document.createElement("a");
2935
+ a4.href = URL.createObjectURL(blob);
2936
+ a4.download = `${s3.symbol?.symbol ?? "chart"}_${s3.resolution}_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.png`;
2937
+ a4.click();
2938
+ setTimeout(() => URL.revokeObjectURL(a4.href), 1e3);
2939
+ });
2940
+ },
2941
+ toggleFullscreen: () => {
2942
+ if (isNativeFullscreen()) {
2943
+ void document.exitFullscreen();
2944
+ } else if (root.classList.contains("fnc-pseudo-fullscreen")) {
2945
+ root.classList.remove("fnc-pseudo-fullscreen");
2946
+ onFullscreenChange();
2947
+ } else if (root.requestFullscreen && document.fullscreenEnabled) {
2948
+ root.requestFullscreen().catch(() => {
2949
+ root.classList.add("fnc-pseudo-fullscreen");
2950
+ onFullscreenChange();
2951
+ });
2952
+ } else {
2953
+ root.classList.add("fnc-pseudo-fullscreen");
2954
+ onFullscreenChange();
2955
+ }
2956
+ }
2957
+ };
2958
+ Q(/* @__PURE__ */ a2(Toolbar, { ctx }), toolbarHost);
2959
+ Q(/* @__PURE__ */ a2(Overlay, { ctx }), overlayHost);
2960
+ if (drawbarHost) Q(/* @__PURE__ */ a2(DrawingToolbar, { ctx }), drawbarHost);
2961
+ void load(symbol, resolution);
2962
+ return {
2963
+ chart,
2964
+ slots,
2965
+ setSymbol: (sym, res) => load(sym, res ?? store.get().resolution),
2966
+ setResolution: (res) => {
2967
+ const current = store.get().symbol;
2968
+ return current ? load(current.symbol, res) : Promise.resolve();
2969
+ },
2970
+ setLocale: (locale2) => {
2971
+ chart.setLocale(locale2);
2972
+ store.set({ locale: locale2 });
2973
+ },
2974
+ save: () => chart.save(),
2975
+ load: (state) => chart.load(state),
2976
+ setTheme: (patch) => {
2977
+ themePatch = { ...themePatch, ...patch };
2978
+ refreshScheme();
2979
+ },
2980
+ setColorScheme: (next) => {
2981
+ scheme = next;
2982
+ refreshScheme();
2983
+ },
2984
+ destroy: () => {
2985
+ offs.forEach((off) => off());
2986
+ document.removeEventListener("fullscreenchange", onFullscreenChange);
2987
+ document.removeEventListener("keydown", onKeyDown);
2988
+ document.removeEventListener("keyup", onKeyUp);
2989
+ darkQuery?.removeEventListener?.("change", onSchemeChange);
2990
+ if (isNativeFullscreen()) void document.exitFullscreen();
2991
+ Q(null, toolbarHost);
2992
+ Q(null, overlayHost);
2993
+ if (drawbarHost) Q(null, drawbarHost);
2994
+ chart.destroy();
2995
+ root.remove();
2996
+ }
2997
+ };
2998
+ }
2999
+ export {
3000
+ DEFAULT_FAVORITE_INTERVALS,
3001
+ DEFAULT_INTERVAL_GROUPS,
3002
+ DEFAULT_SEARCH_CATEGORIES,
3003
+ createTradingChart,
3004
+ resolutionLabel
3005
+ };