@coderyo/ui-shell 1.0.1 → 1.0.3
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.d.ts +70 -5
- package/dist/index.js +412 -68
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
package/dist/index.js
CHANGED
|
@@ -1,9 +1,42 @@
|
|
|
1
1
|
// src/top-bar.ts
|
|
2
2
|
import { DEFAULT_INTERVALS } from "@coderyo/data";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
// src/logo-slot.ts
|
|
5
|
+
function mountLogoSlot(parent, opts = {}) {
|
|
6
|
+
const slot = document.createElement("div");
|
|
7
|
+
slot.className = "tv-logo-slot";
|
|
8
|
+
slot.style.cssText = "display:flex;align-items:center;gap:8px;margin-right:12px;flex-shrink:0;font-weight:600;font-size:13px;color:var(--tv-fg,#e6edf3);";
|
|
9
|
+
const label = opts.label ?? "TradView";
|
|
10
|
+
const text = document.createElement("span");
|
|
11
|
+
text.textContent = label;
|
|
12
|
+
const children = [];
|
|
13
|
+
if (opts.imageSrc) {
|
|
14
|
+
const img = document.createElement("img");
|
|
15
|
+
img.src = opts.imageSrc;
|
|
16
|
+
img.alt = label;
|
|
17
|
+
img.style.cssText = "height:22px;width:auto;display:block;";
|
|
18
|
+
children.push(img);
|
|
19
|
+
}
|
|
20
|
+
children.push(text);
|
|
21
|
+
if (opts.href) {
|
|
22
|
+
const link = document.createElement("a");
|
|
23
|
+
link.href = opts.href;
|
|
24
|
+
link.style.cssText = "color:inherit;text-decoration:none;display:flex;align-items:center;gap:8px;";
|
|
25
|
+
link.append(...children);
|
|
26
|
+
slot.appendChild(link);
|
|
27
|
+
} else {
|
|
28
|
+
slot.append(...children);
|
|
29
|
+
}
|
|
30
|
+
parent.appendChild(slot);
|
|
31
|
+
return slot;
|
|
32
|
+
}
|
|
4
33
|
|
|
5
34
|
// src/user-preferences.ts
|
|
6
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
defaultChartStorage,
|
|
37
|
+
loadIndicatorConfig as loadIndicatorConfigFromCore,
|
|
38
|
+
saveIndicatorConfig as saveIndicatorConfigToCore
|
|
39
|
+
} from "@coderyo/core";
|
|
7
40
|
var GRID_SETTING_KEY = "tradview:settings:showGrid";
|
|
8
41
|
var RETURN_CURSOR_KEY = "tradview:settings:returnToCursorAfterDraw";
|
|
9
42
|
function loadShowGridPreference() {
|
|
@@ -33,30 +66,21 @@ function saveReturnToCursorPreference(v) {
|
|
|
33
66
|
}
|
|
34
67
|
}
|
|
35
68
|
function loadIndicatorConfig(symbol, interval) {
|
|
36
|
-
|
|
37
|
-
const raw = localStorage.getItem(indicatorConfigStorageKey(symbol, interval));
|
|
38
|
-
if (!raw) return { ...DEFAULT_INDICATOR_CONFIG };
|
|
39
|
-
return { ...DEFAULT_INDICATOR_CONFIG, ...JSON.parse(raw) };
|
|
40
|
-
} catch {
|
|
41
|
-
return { ...DEFAULT_INDICATOR_CONFIG };
|
|
42
|
-
}
|
|
69
|
+
return loadIndicatorConfigFromCore(defaultChartStorage, symbol, interval);
|
|
43
70
|
}
|
|
44
71
|
function saveIndicatorConfig(symbol, interval, config) {
|
|
45
|
-
|
|
46
|
-
localStorage.setItem(indicatorConfigStorageKey(symbol, interval), JSON.stringify(config));
|
|
47
|
-
} catch {
|
|
48
|
-
}
|
|
72
|
+
saveIndicatorConfigToCore(defaultChartStorage, symbol, interval, config);
|
|
49
73
|
}
|
|
50
74
|
|
|
51
75
|
// src/settings-panel.ts
|
|
52
|
-
import { DEFAULT_INDICATOR_CONFIG
|
|
76
|
+
import { DEFAULT_INDICATOR_CONFIG } from "@coderyo/indicators";
|
|
53
77
|
import { t } from "@coderyo/i18n";
|
|
54
78
|
function mountSettingsPanel(parent, opts = {}) {
|
|
55
79
|
let open = false;
|
|
56
80
|
let tab = "chart";
|
|
57
81
|
let showGrid = opts.showGrid ?? loadShowGridPreference();
|
|
58
82
|
let returnToCursor = opts.returnToCursorAfterDraw ?? loadReturnToCursorPreference();
|
|
59
|
-
let indicatorConfig = { ...opts.indicatorConfig ??
|
|
83
|
+
let indicatorConfig = { ...opts.indicatorConfig ?? DEFAULT_INDICATOR_CONFIG };
|
|
60
84
|
const wrap = document.createElement("div");
|
|
61
85
|
wrap.style.cssText = "position:relative;";
|
|
62
86
|
const btn = document.createElement("button");
|
|
@@ -101,6 +125,17 @@ function mountSettingsPanel(parent, opts = {}) {
|
|
|
101
125
|
row.append(input, document.createTextNode(label));
|
|
102
126
|
return row;
|
|
103
127
|
};
|
|
128
|
+
const actionButton = (label, onClick) => {
|
|
129
|
+
const btn2 = document.createElement("button");
|
|
130
|
+
btn2.type = "button";
|
|
131
|
+
btn2.textContent = label;
|
|
132
|
+
btn2.style.cssText = "display:block;width:100%;margin-top:10px;padding:6px 10px;border-radius:4px;border:1px solid #f85149;background:#21262d;color:#f85149;cursor:pointer;font-size:12px;";
|
|
133
|
+
btn2.onclick = (e) => {
|
|
134
|
+
e.stopPropagation();
|
|
135
|
+
onClick();
|
|
136
|
+
};
|
|
137
|
+
return btn2;
|
|
138
|
+
};
|
|
104
139
|
const numberField = (label, value, onChange) => {
|
|
105
140
|
const row = document.createElement("label");
|
|
106
141
|
row.style.cssText = "display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;";
|
|
@@ -131,6 +166,14 @@ function mountSettingsPanel(parent, opts = {}) {
|
|
|
131
166
|
opts.onReturnToCursorChange?.(v);
|
|
132
167
|
})
|
|
133
168
|
);
|
|
169
|
+
if (opts.onClearAllDrawings) {
|
|
170
|
+
content.appendChild(
|
|
171
|
+
actionButton(t("settings.drawing.clearAll", "\u6E05\u9664\u6240\u6709\u756B\u7DDA"), () => {
|
|
172
|
+
opts.onClearAllDrawings?.();
|
|
173
|
+
renderContent();
|
|
174
|
+
})
|
|
175
|
+
);
|
|
176
|
+
}
|
|
134
177
|
} else {
|
|
135
178
|
content.appendChild(
|
|
136
179
|
checkbox(t("settings.ind.macd", "MACD \u7A97\u683C"), indicatorConfig.showMacd, (v) => {
|
|
@@ -222,6 +265,15 @@ function mountSettingsPanel(parent, opts = {}) {
|
|
|
222
265
|
opts.onIndicatorConfigChange?.(indicatorConfig);
|
|
223
266
|
})
|
|
224
267
|
);
|
|
268
|
+
if (opts.onClearAllIndicators) {
|
|
269
|
+
content.appendChild(
|
|
270
|
+
actionButton(t("settings.ind.clearAll", "\u6E05\u7A7A\u6240\u6709\u6307\u6A19"), () => {
|
|
271
|
+
opts.onClearAllIndicators?.();
|
|
272
|
+
if (opts.indicatorConfig) indicatorConfig = { ...opts.indicatorConfig };
|
|
273
|
+
renderContent();
|
|
274
|
+
})
|
|
275
|
+
);
|
|
276
|
+
}
|
|
225
277
|
}
|
|
226
278
|
};
|
|
227
279
|
renderTabs();
|
|
@@ -230,6 +282,10 @@ function mountSettingsPanel(parent, opts = {}) {
|
|
|
230
282
|
btn.onclick = (e) => {
|
|
231
283
|
e.stopPropagation();
|
|
232
284
|
open = !open;
|
|
285
|
+
if (open) {
|
|
286
|
+
if (opts.indicatorConfig) indicatorConfig = { ...opts.indicatorConfig };
|
|
287
|
+
renderContent();
|
|
288
|
+
}
|
|
233
289
|
panel.style.display = open ? "block" : "none";
|
|
234
290
|
};
|
|
235
291
|
const close = () => {
|
|
@@ -243,6 +299,108 @@ function mountSettingsPanel(parent, opts = {}) {
|
|
|
243
299
|
return wrap;
|
|
244
300
|
}
|
|
245
301
|
|
|
302
|
+
// src/symbol-search-dialog.ts
|
|
303
|
+
function createSymbolSearchDialog(opts) {
|
|
304
|
+
const i18n = opts.i18n;
|
|
305
|
+
const backdrop = document.createElement("div");
|
|
306
|
+
backdrop.className = "tv-symbol-dialog-backdrop";
|
|
307
|
+
backdrop.style.cssText = "display:none;position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,0.55);align-items:flex-start;justify-content:center;padding:12vh 16px 16px;";
|
|
308
|
+
const panel = document.createElement("div");
|
|
309
|
+
panel.className = "tv-symbol-dialog";
|
|
310
|
+
panel.style.cssText = "width:min(420px,100%);background:var(--tv-surface,#161b22);border:1px solid var(--tv-border,#30363d);border-radius:8px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,0.4);";
|
|
311
|
+
const title = document.createElement("div");
|
|
312
|
+
title.textContent = i18n?.t("symbol.search", "\u641C\u5C0B\u5546\u54C1") ?? "\u641C\u5C0B\u5546\u54C1";
|
|
313
|
+
title.style.cssText = "font-size:13px;font-weight:600;color:var(--tv-fg,#e6edf3);margin-bottom:8px;";
|
|
314
|
+
const input = document.createElement("input");
|
|
315
|
+
input.type = "search";
|
|
316
|
+
input.placeholder = i18n?.t("symbol.search", "\u641C\u5C0B\u5546\u54C1") ?? "\u641C\u5C0B\u5546\u54C1";
|
|
317
|
+
input.value = opts.initialSymbol ?? "";
|
|
318
|
+
input.style.cssText = "width:100%;box-sizing:border-box;padding:8px 10px;border-radius:4px;border:1px solid var(--tv-border,#30363d);background:var(--tv-bg,#0d1117);color:var(--tv-fg,#e6edf3);font-size:13px;";
|
|
319
|
+
const list = document.createElement("div");
|
|
320
|
+
list.style.cssText = "margin-top:8px;max-height:240px;overflow:auto;";
|
|
321
|
+
panel.append(title, input, list);
|
|
322
|
+
backdrop.appendChild(panel);
|
|
323
|
+
let timer;
|
|
324
|
+
let mounted = false;
|
|
325
|
+
const renderHits = (hits) => {
|
|
326
|
+
list.replaceChildren();
|
|
327
|
+
if (hits.length === 0) {
|
|
328
|
+
const empty = document.createElement("div");
|
|
329
|
+
empty.textContent = "\u2014";
|
|
330
|
+
empty.style.cssText = "padding:8px;color:var(--tv-muted,#8b949e);font-size:12px;";
|
|
331
|
+
list.appendChild(empty);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
for (const hit of hits) {
|
|
335
|
+
const row = document.createElement("button");
|
|
336
|
+
row.type = "button";
|
|
337
|
+
row.textContent = hit.exchange ? `${hit.symbol} \xB7 ${hit.exchange}` : hit.symbol;
|
|
338
|
+
row.style.cssText = "display:block;width:100%;text-align:left;padding:8px 10px;border:none;background:transparent;color:var(--tv-fg,#e6edf3);cursor:pointer;font-size:12px;border-radius:4px;";
|
|
339
|
+
row.onmouseenter = () => {
|
|
340
|
+
row.style.background = "var(--tv-btn-bg,#21262d)";
|
|
341
|
+
};
|
|
342
|
+
row.onmouseleave = () => {
|
|
343
|
+
row.style.background = "transparent";
|
|
344
|
+
};
|
|
345
|
+
row.onclick = () => {
|
|
346
|
+
opts.onSelect(hit.symbol);
|
|
347
|
+
close();
|
|
348
|
+
};
|
|
349
|
+
list.appendChild(row);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const runSearch = () => {
|
|
353
|
+
const q = input.value.trim();
|
|
354
|
+
if (q.length < 1) {
|
|
355
|
+
list.replaceChildren();
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
void opts.onSearch(q).then(renderHits).catch(() => list.replaceChildren());
|
|
359
|
+
};
|
|
360
|
+
input.addEventListener("input", () => {
|
|
361
|
+
clearTimeout(timer);
|
|
362
|
+
timer = setTimeout(runSearch, 200);
|
|
363
|
+
});
|
|
364
|
+
input.addEventListener("keydown", (e) => {
|
|
365
|
+
if (e.key === "Escape") close();
|
|
366
|
+
if (e.key === "Enter") runSearch();
|
|
367
|
+
});
|
|
368
|
+
backdrop.addEventListener("click", (e) => {
|
|
369
|
+
if (e.target === backdrop) close();
|
|
370
|
+
});
|
|
371
|
+
const open = () => {
|
|
372
|
+
if (!mounted) {
|
|
373
|
+
document.body.appendChild(backdrop);
|
|
374
|
+
mounted = true;
|
|
375
|
+
}
|
|
376
|
+
backdrop.style.display = "flex";
|
|
377
|
+
input.focus();
|
|
378
|
+
input.select();
|
|
379
|
+
runSearch();
|
|
380
|
+
};
|
|
381
|
+
const close = () => {
|
|
382
|
+
backdrop.style.display = "none";
|
|
383
|
+
};
|
|
384
|
+
const destroy = () => {
|
|
385
|
+
clearTimeout(timer);
|
|
386
|
+
backdrop.remove();
|
|
387
|
+
mounted = false;
|
|
388
|
+
};
|
|
389
|
+
return { open, close, destroy };
|
|
390
|
+
}
|
|
391
|
+
function mountSymbolSearchDialogTrigger(parent, dialog, opts) {
|
|
392
|
+
const wrap = document.createElement("div");
|
|
393
|
+
wrap.style.cssText = "display:flex;align-items:center;margin-right:8px;flex-shrink:0;";
|
|
394
|
+
const btn = document.createElement("button");
|
|
395
|
+
btn.type = "button";
|
|
396
|
+
btn.textContent = opts.initialSymbol ?? (opts.i18n?.t("symbol.search", "\u641C\u5C0B\u5546\u54C1") ?? "\u641C\u5C0B\u5546\u54C1");
|
|
397
|
+
btn.style.cssText = "min-width:140px;text-align:left;background:var(--tv-bg,#0d1117);color:var(--tv-fg,#e6edf3);border:1px solid var(--tv-border,#30363d);border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;";
|
|
398
|
+
btn.onclick = () => dialog.open();
|
|
399
|
+
wrap.appendChild(btn);
|
|
400
|
+
parent.appendChild(wrap);
|
|
401
|
+
return wrap;
|
|
402
|
+
}
|
|
403
|
+
|
|
246
404
|
// src/symbol-search.ts
|
|
247
405
|
import { t as t2 } from "@coderyo/i18n";
|
|
248
406
|
function mountSymbolSearch(parent, opts) {
|
|
@@ -306,13 +464,32 @@ function mountSymbolSearch(parent, opts) {
|
|
|
306
464
|
return wrap;
|
|
307
465
|
}
|
|
308
466
|
|
|
467
|
+
// src/theme-toggle.ts
|
|
468
|
+
function mountThemeToggle(parent, opts) {
|
|
469
|
+
const i18n = opts.i18n;
|
|
470
|
+
const btn = document.createElement("button");
|
|
471
|
+
btn.type = "button";
|
|
472
|
+
btn.className = "tv-theme-toggle";
|
|
473
|
+
btn.style.cssText = "background:var(--tv-btn-bg,#21262d);color:var(--tv-fg,#e6edf3);border:1px solid var(--tv-border,#30363d);border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;flex-shrink:0;";
|
|
474
|
+
const paint = (theme) => {
|
|
475
|
+
btn.textContent = theme === "dark" ? i18n?.t("theme.dark", "\u6DF1\u8272") ?? "\u6DF1\u8272" : i18n?.t("theme.light", "\u6DFA\u8272") ?? "\u6DFA\u8272";
|
|
476
|
+
};
|
|
477
|
+
btn.onclick = () => {
|
|
478
|
+
const next = opts.themeProvider.toggle();
|
|
479
|
+
opts.onThemeChange?.(next);
|
|
480
|
+
};
|
|
481
|
+
opts.themeProvider.subscribe((theme) => paint(theme));
|
|
482
|
+
parent.appendChild(btn);
|
|
483
|
+
return btn;
|
|
484
|
+
}
|
|
485
|
+
|
|
309
486
|
// src/top-bar.ts
|
|
310
487
|
function mountManualSymbolInput(parent, opts) {
|
|
311
488
|
const wrap = document.createElement("div");
|
|
312
489
|
wrap.style.cssText = "display:flex;align-items:center;gap:4px;margin-right:8px;";
|
|
313
490
|
const input = document.createElement("input");
|
|
314
491
|
input.type = "text";
|
|
315
|
-
input.placeholder =
|
|
492
|
+
input.placeholder = opts.i18n?.t("symbol.search", "Symbol") ?? "Symbol";
|
|
316
493
|
input.value = opts.initialSymbol ?? "";
|
|
317
494
|
input.style.cssText = "width:140px;background:#0d1117;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 8px;font-size:12px;";
|
|
318
495
|
const apply = () => {
|
|
@@ -332,9 +509,14 @@ function mountManualSymbolInput(parent, opts) {
|
|
|
332
509
|
parent.appendChild(wrap);
|
|
333
510
|
}
|
|
334
511
|
function mountTopBar(parent, opts = {}) {
|
|
512
|
+
const i18n = opts.i18n;
|
|
513
|
+
const tKey = (key, fallback) => i18n?.t(key, fallback) ?? fallback;
|
|
335
514
|
const bar = document.createElement("div");
|
|
336
515
|
bar.className = "tv-topbar";
|
|
337
|
-
bar.style.cssText = "display:flex;gap:8px;padding:8px 12px;align-items:center;border-bottom:1px solid
|
|
516
|
+
bar.style.cssText = "display:flex;gap:8px;padding:8px 12px;align-items:center;border-bottom:1px solid var(--tv-border,#30363d);background:var(--tv-surface,#161b22);flex-shrink:0;box-sizing:border-box;width:100%;min-width:0;overflow-x:auto;overflow-y:visible;position:relative;";
|
|
517
|
+
if (opts.logo !== false) {
|
|
518
|
+
mountLogoSlot(bar, opts.logo ?? { label: "TradView" });
|
|
519
|
+
}
|
|
338
520
|
const symbolMode = opts.symbolInput ?? "manual";
|
|
339
521
|
if (symbolMode === "search" && opts.onSymbolSearch && opts.onSymbolSelect) {
|
|
340
522
|
mountSymbolSearch(bar, {
|
|
@@ -342,17 +524,32 @@ function mountTopBar(parent, opts = {}) {
|
|
|
342
524
|
onSearch: opts.onSymbolSearch,
|
|
343
525
|
onSelect: opts.onSymbolSelect
|
|
344
526
|
});
|
|
527
|
+
} else if (symbolMode === "dialog" && opts.onSymbolSearch && opts.onSymbolSelect) {
|
|
528
|
+
const dialog = createSymbolSearchDialog({
|
|
529
|
+
initialSymbol: opts.initialSymbol,
|
|
530
|
+
onSearch: opts.onSymbolSearch,
|
|
531
|
+
onSelect: opts.onSymbolSelect,
|
|
532
|
+
i18n
|
|
533
|
+
});
|
|
534
|
+
mountSymbolSearchDialogTrigger(bar, dialog, {
|
|
535
|
+
initialSymbol: opts.initialSymbol,
|
|
536
|
+
i18n
|
|
537
|
+
});
|
|
345
538
|
} else if (symbolMode === "manual" && opts.onSymbolSelect) {
|
|
346
539
|
mountManualSymbolInput(bar, {
|
|
347
540
|
initialSymbol: opts.initialSymbol,
|
|
348
|
-
onSymbolSelect: opts.onSymbolSelect
|
|
541
|
+
onSymbolSelect: opts.onSymbolSelect,
|
|
542
|
+
i18n
|
|
349
543
|
});
|
|
350
544
|
}
|
|
351
545
|
const intervals = opts.intervals ?? DEFAULT_INTERVALS;
|
|
352
|
-
const btnStyle = "background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;";
|
|
546
|
+
const btnStyle = "background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;flex-shrink:0;";
|
|
353
547
|
const btnActiveStyle = btnStyle.replace("#21262d", "#388bfd").replace("#e6edf3", "#fff");
|
|
354
548
|
const intervalButtons = /* @__PURE__ */ new Map();
|
|
355
549
|
let activeInterval = opts.activeInterval ?? intervals[0];
|
|
550
|
+
const intervalRow = document.createElement("div");
|
|
551
|
+
intervalRow.className = "tv-topbar-intervals";
|
|
552
|
+
intervalRow.style.cssText = "display:flex;gap:8px;flex-wrap:nowrap;align-items:center;flex-shrink:0;";
|
|
356
553
|
const paintIntervalButtons = () => {
|
|
357
554
|
for (const [iv, btn] of intervalButtons) {
|
|
358
555
|
btn.style.cssText = iv === activeInterval ? btnActiveStyle : btnStyle;
|
|
@@ -361,7 +558,7 @@ function mountTopBar(parent, opts = {}) {
|
|
|
361
558
|
for (const iv of intervals) {
|
|
362
559
|
const btn = document.createElement("button");
|
|
363
560
|
btn.type = "button";
|
|
364
|
-
btn.textContent =
|
|
561
|
+
btn.textContent = tKey(`interval.${iv}`, iv);
|
|
365
562
|
btn.style.cssText = btnStyle;
|
|
366
563
|
btn.onclick = () => {
|
|
367
564
|
if (activeInterval === iv) return;
|
|
@@ -370,9 +567,10 @@ function mountTopBar(parent, opts = {}) {
|
|
|
370
567
|
opts.onIntervalChange?.(iv);
|
|
371
568
|
};
|
|
372
569
|
intervalButtons.set(iv, btn);
|
|
373
|
-
|
|
570
|
+
intervalRow.appendChild(btn);
|
|
374
571
|
}
|
|
375
572
|
paintIntervalButtons();
|
|
573
|
+
bar.appendChild(intervalRow);
|
|
376
574
|
const spacer = document.createElement("div");
|
|
377
575
|
spacer.style.flex = "1";
|
|
378
576
|
bar.appendChild(spacer);
|
|
@@ -384,11 +582,19 @@ function mountTopBar(parent, opts = {}) {
|
|
|
384
582
|
b.onclick = () => fn?.();
|
|
385
583
|
return b;
|
|
386
584
|
};
|
|
387
|
-
|
|
585
|
+
if (opts.themeProvider) {
|
|
586
|
+
mountThemeToggle(bar, {
|
|
587
|
+
themeProvider: opts.themeProvider,
|
|
588
|
+
i18n,
|
|
589
|
+
onThemeChange: opts.onThemeChange
|
|
590
|
+
});
|
|
591
|
+
} else {
|
|
592
|
+
bar.appendChild(mkBtn(tKey("theme.dark", "\u4E3B\u984C"), opts.onThemeToggle));
|
|
593
|
+
}
|
|
388
594
|
if (opts.showSettings && opts.settings) mountSettingsPanel(bar, opts.settings);
|
|
389
595
|
bar.appendChild(mkBtn("\u26F6", opts.onFullscreen));
|
|
390
596
|
bar.appendChild(mkBtn("\u{1F4F7}", opts.onScreenshot));
|
|
391
|
-
parent.
|
|
597
|
+
parent.appendChild(bar);
|
|
392
598
|
return {
|
|
393
599
|
el: bar,
|
|
394
600
|
setActiveInterval: (interval) => {
|
|
@@ -399,8 +605,116 @@ function mountTopBar(parent, opts = {}) {
|
|
|
399
605
|
};
|
|
400
606
|
}
|
|
401
607
|
|
|
608
|
+
// src/theme-provider.ts
|
|
609
|
+
var THEME_STORAGE_KEY = "tradview:theme";
|
|
610
|
+
var THEME_VARS = {
|
|
611
|
+
dark: {
|
|
612
|
+
"--tv-bg": "#0d1117",
|
|
613
|
+
"--tv-surface": "#161b22",
|
|
614
|
+
"--tv-border": "#30363d",
|
|
615
|
+
"--tv-fg": "#e6edf3",
|
|
616
|
+
"--tv-muted": "#8b949e",
|
|
617
|
+
"--tv-accent": "#388bfd",
|
|
618
|
+
"--tv-btn-bg": "#21262d"
|
|
619
|
+
},
|
|
620
|
+
light: {
|
|
621
|
+
"--tv-bg": "#ffffff",
|
|
622
|
+
"--tv-surface": "#f6f8fa",
|
|
623
|
+
"--tv-border": "#d0d7de",
|
|
624
|
+
"--tv-fg": "#24292f",
|
|
625
|
+
"--tv-muted": "#656d76",
|
|
626
|
+
"--tv-accent": "#0969da",
|
|
627
|
+
"--tv-btn-bg": "#f6f8fa"
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
function loadTheme() {
|
|
631
|
+
try {
|
|
632
|
+
const v = localStorage.getItem(THEME_STORAGE_KEY);
|
|
633
|
+
return v === "light" ? "light" : "dark";
|
|
634
|
+
} catch {
|
|
635
|
+
return "dark";
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function saveTheme(theme) {
|
|
639
|
+
try {
|
|
640
|
+
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
641
|
+
} catch {
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function applyThemeToDocument(theme, root = document.documentElement) {
|
|
645
|
+
const vars = THEME_VARS[theme];
|
|
646
|
+
for (const [k, v] of Object.entries(vars)) root.style.setProperty(k, v);
|
|
647
|
+
root.dataset.tradviewTheme = theme;
|
|
648
|
+
}
|
|
649
|
+
function createThemeProvider(initial) {
|
|
650
|
+
let theme = initial ?? loadTheme();
|
|
651
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
652
|
+
const notify = () => {
|
|
653
|
+
applyThemeToDocument(theme);
|
|
654
|
+
for (const l of listeners) l(theme);
|
|
655
|
+
};
|
|
656
|
+
applyThemeToDocument(theme);
|
|
657
|
+
return {
|
|
658
|
+
getTheme: () => theme,
|
|
659
|
+
setTheme: (next) => {
|
|
660
|
+
if (next === theme) return;
|
|
661
|
+
theme = next;
|
|
662
|
+
saveTheme(theme);
|
|
663
|
+
notify();
|
|
664
|
+
},
|
|
665
|
+
toggle: () => {
|
|
666
|
+
theme = theme === "dark" ? "light" : "dark";
|
|
667
|
+
saveTheme(theme);
|
|
668
|
+
notify();
|
|
669
|
+
return theme;
|
|
670
|
+
},
|
|
671
|
+
subscribe: (listener) => {
|
|
672
|
+
listeners.add(listener);
|
|
673
|
+
listener(theme);
|
|
674
|
+
return () => listeners.delete(listener);
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/i18n-provider.ts
|
|
680
|
+
import {
|
|
681
|
+
getLocale,
|
|
682
|
+
registerLocale,
|
|
683
|
+
setLocale,
|
|
684
|
+
t as i18nT
|
|
685
|
+
} from "@coderyo/i18n";
|
|
686
|
+
function interpolate(text, params) {
|
|
687
|
+
if (!params) return text;
|
|
688
|
+
return text.replace(/\{(\w+)\}/g, (_, key) => String(params[key] ?? `{${key}}`));
|
|
689
|
+
}
|
|
690
|
+
function createI18nProvider(defaultLocale = "zh-TW") {
|
|
691
|
+
setLocale(defaultLocale);
|
|
692
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
693
|
+
return {
|
|
694
|
+
t(key, fallback, params) {
|
|
695
|
+
return interpolate(i18nT(key, fallback), params);
|
|
696
|
+
},
|
|
697
|
+
getLocale,
|
|
698
|
+
setLocale(locale) {
|
|
699
|
+
setLocale(locale);
|
|
700
|
+
for (const l of listeners) l(locale);
|
|
701
|
+
},
|
|
702
|
+
registerLocale(loc, dictionary) {
|
|
703
|
+
registerLocale(loc, dictionary);
|
|
704
|
+
if (getLocale() === loc) {
|
|
705
|
+
for (const l of listeners) l(loc);
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
subscribe(listener) {
|
|
709
|
+
listeners.add(listener);
|
|
710
|
+
listener(getLocale());
|
|
711
|
+
return () => listeners.delete(listener);
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
|
|
402
716
|
// src/context-menu.ts
|
|
403
|
-
import { t as
|
|
717
|
+
import { t as t3 } from "@coderyo/i18n";
|
|
404
718
|
function attachChartContextMenu(chartHost, opts = {}) {
|
|
405
719
|
let menu = null;
|
|
406
720
|
const close = () => {
|
|
@@ -414,7 +728,7 @@ function attachChartContextMenu(chartHost, opts = {}) {
|
|
|
414
728
|
const actions = opts.actions ?? [
|
|
415
729
|
{
|
|
416
730
|
id: "fit",
|
|
417
|
-
label:
|
|
731
|
+
label: t3("context.fitContent", "\u9069\u914D\u756B\u9762"),
|
|
418
732
|
onClick: () => {
|
|
419
733
|
}
|
|
420
734
|
}
|
|
@@ -493,13 +807,13 @@ O ${fmt2(o?.o)} H ${fmt2(o?.h)} L ${fmt2(o?.l)} C ${fmt2(o?.c)}`;
|
|
|
493
807
|
}
|
|
494
808
|
|
|
495
809
|
// src/drawing-properties-panel.ts
|
|
496
|
-
import { t as
|
|
810
|
+
import { t as t4 } from "@coderyo/i18n";
|
|
497
811
|
function mountDrawingPropertiesPanel(parent, opts = {}) {
|
|
498
812
|
const panel = document.createElement("aside");
|
|
499
813
|
panel.className = "tv-drawing-props";
|
|
500
814
|
panel.style.cssText = "display:none;width:220px;flex-shrink:0;border-left:1px solid #30363d;background:#161b22;padding:10px 12px;font-size:12px;color:#e6edf3;overflow:auto;";
|
|
501
815
|
const title = document.createElement("div");
|
|
502
|
-
title.textContent =
|
|
816
|
+
title.textContent = t4("drawing.props.title", "\u7E6A\u5716\u5C6C\u6027");
|
|
503
817
|
title.style.cssText = "font-weight:600;margin-bottom:10px;";
|
|
504
818
|
const typeEl = document.createElement("div");
|
|
505
819
|
typeEl.style.color = "#8b949e";
|
|
@@ -513,26 +827,26 @@ function mountDrawingPropertiesPanel(parent, opts = {}) {
|
|
|
513
827
|
row.appendChild(span);
|
|
514
828
|
return row;
|
|
515
829
|
};
|
|
516
|
-
const colorRow = mkRow(
|
|
830
|
+
const colorRow = mkRow(t4("drawing.props.color", "\u984F\u8272"));
|
|
517
831
|
const colorInput = document.createElement("input");
|
|
518
832
|
colorInput.type = "color";
|
|
519
833
|
colorInput.style.cssText = "width:100%;height:28px;border:none;background:transparent;cursor:pointer;";
|
|
520
834
|
colorRow.appendChild(colorInput);
|
|
521
|
-
const widthRow = mkRow(
|
|
835
|
+
const widthRow = mkRow(t4("drawing.props.lineWidth", "\u7DDA\u5BEC"));
|
|
522
836
|
const widthInput = document.createElement("input");
|
|
523
837
|
widthInput.type = "range";
|
|
524
838
|
widthInput.min = "1";
|
|
525
839
|
widthInput.max = "6";
|
|
526
840
|
widthInput.style.width = "100%";
|
|
527
841
|
widthRow.appendChild(widthInput);
|
|
528
|
-
const textRow = mkRow(
|
|
842
|
+
const textRow = mkRow(t4("drawing.props.text", "\u6587\u5B57"));
|
|
529
843
|
const textInput = document.createElement("input");
|
|
530
844
|
textInput.type = "text";
|
|
531
845
|
textInput.style.cssText = "padding:4px 8px;border-radius:4px;border:1px solid #30363d;background:#0d1117;color:#e6edf3;";
|
|
532
846
|
textRow.appendChild(textInput);
|
|
533
847
|
const lockedHint = document.createElement("div");
|
|
534
848
|
lockedHint.style.cssText = "color:#f78166;font-size:11px;margin-top:6px;display:none;";
|
|
535
|
-
lockedHint.textContent =
|
|
849
|
+
lockedHint.textContent = t4("drawing.props.locked", "\u5DF2\u9396\u5B9A \u2014 \u89E3\u9396\u5F8C\u53EF\u7DE8\u8F2F");
|
|
536
850
|
panel.append(title, typeEl, colorRow, widthRow, textRow, lockedHint);
|
|
537
851
|
parent.appendChild(panel);
|
|
538
852
|
const emit = () => {
|
|
@@ -551,7 +865,7 @@ function mountDrawingPropertiesPanel(parent, opts = {}) {
|
|
|
551
865
|
return;
|
|
552
866
|
}
|
|
553
867
|
panel.style.display = "block";
|
|
554
|
-
typeEl.textContent = `${
|
|
868
|
+
typeEl.textContent = `${t4("drawing.props.type", "\u985E\u578B")}: ${drawing.type}`;
|
|
555
869
|
const meta = drawing.meta ?? {};
|
|
556
870
|
colorInput.value = String(meta.color ?? "#58a6ff");
|
|
557
871
|
widthInput.value = String(meta.lineWidth ?? 2);
|
|
@@ -635,7 +949,7 @@ function createDemoLayoutOptions(partial = {}) {
|
|
|
635
949
|
}
|
|
636
950
|
|
|
637
951
|
// src/status-bar.ts
|
|
638
|
-
import { getLocale, setLocale, t as
|
|
952
|
+
import { getLocale as getLocale2, setLocale as setLocale2, t as t5 } from "@coderyo/i18n";
|
|
639
953
|
function fmt(n, digits = 2) {
|
|
640
954
|
if (n == null || Number.isNaN(n)) return "\u2014";
|
|
641
955
|
return n.toLocaleString(void 0, { maximumFractionDigits: digits });
|
|
@@ -652,7 +966,7 @@ function mountStatusBar(parent, opts = {}) {
|
|
|
652
966
|
const localeWrap = document.createElement("label");
|
|
653
967
|
localeWrap.style.cssText = "display:flex;align-items:center;gap:4px;margin-left:auto;";
|
|
654
968
|
const localeLabel = document.createElement("span");
|
|
655
|
-
localeLabel.textContent =
|
|
969
|
+
localeLabel.textContent = t5("status.locale", "\u8A9E\u7CFB");
|
|
656
970
|
const localeSelect = document.createElement("select");
|
|
657
971
|
localeSelect.style.cssText = "background:#0d1117;color:#e6edf3;border:1px solid #30363d;border-radius:4px;font-size:11px;padding:2px 4px;";
|
|
658
972
|
for (const loc of ["zh-TW", "en"]) {
|
|
@@ -661,11 +975,11 @@ function mountStatusBar(parent, opts = {}) {
|
|
|
661
975
|
opt.textContent = loc;
|
|
662
976
|
localeSelect.appendChild(opt);
|
|
663
977
|
}
|
|
664
|
-
localeSelect.value = opts.locale ??
|
|
978
|
+
localeSelect.value = opts.locale ?? getLocale2();
|
|
665
979
|
localeSelect.onchange = () => {
|
|
666
|
-
|
|
980
|
+
setLocale2(localeSelect.value);
|
|
667
981
|
opts.onLocaleChange?.(localeSelect.value);
|
|
668
|
-
localeLabel.textContent =
|
|
982
|
+
localeLabel.textContent = t5("status.locale", "\u8A9E\u7CFB");
|
|
669
983
|
render(opts);
|
|
670
984
|
};
|
|
671
985
|
localeWrap.append(localeLabel, localeSelect);
|
|
@@ -673,14 +987,14 @@ function mountStatusBar(parent, opts = {}) {
|
|
|
673
987
|
parent.appendChild(bar);
|
|
674
988
|
const render = (state) => {
|
|
675
989
|
const merged = { ...opts, ...state };
|
|
676
|
-
conn.textContent = `${
|
|
990
|
+
conn.textContent = `${t5("status.connection", "\u9023\u7DDA")}\uFF1A${merged.connection ?? "\u2014"}`;
|
|
677
991
|
const parts = [merged.symbol, merged.interval].filter(Boolean);
|
|
678
992
|
sym.textContent = parts.length ? parts.join(" \xB7 ") : "";
|
|
679
993
|
const o = merged.ohlcv;
|
|
680
994
|
if (o && (o.o != null || o.c != null)) {
|
|
681
995
|
ohlcv.textContent = `O ${fmt(o.o)} H ${fmt(o.h)} L ${fmt(o.l)} C ${fmt(o.c)} V ${fmt(o.v, 0)}`;
|
|
682
996
|
} else {
|
|
683
|
-
ohlcv.textContent =
|
|
997
|
+
ohlcv.textContent = t5("status.ohlcvHint", "\u79FB\u52D5\u5341\u5B57\u7DDA\u67E5\u770B OHLCV");
|
|
684
998
|
}
|
|
685
999
|
};
|
|
686
1000
|
render(opts);
|
|
@@ -694,7 +1008,7 @@ function mountStatusBar(parent, opts = {}) {
|
|
|
694
1008
|
}
|
|
695
1009
|
|
|
696
1010
|
// src/shortcuts-modal.ts
|
|
697
|
-
import { t as
|
|
1011
|
+
import { t as t6 } from "@coderyo/i18n";
|
|
698
1012
|
var SHORTCUTS = [
|
|
699
1013
|
{ key: "\u2196 / Esc", desc: "\u6E38\u6A19\uFF08\u9078\u53D6/\u7DE8\u8F2F\u7E6A\u5716\uFF09" },
|
|
700
1014
|
{ key: "Delete", desc: "\u522A\u9664\u9078\u4E2D\u7E6A\u5716" },
|
|
@@ -723,7 +1037,7 @@ function openShortcutsModal() {
|
|
|
723
1037
|
const box = document.createElement("div");
|
|
724
1038
|
box.style.cssText = "min-width:320px;max-width:90vw;padding:16px 20px;background:#161b22;border:1px solid #30363d;border-radius:8px;color:#e6edf3;";
|
|
725
1039
|
const h = document.createElement("h2");
|
|
726
|
-
h.textContent =
|
|
1040
|
+
h.textContent = t6("shortcuts.title", "\u5FEB\u6377\u9375");
|
|
727
1041
|
h.style.cssText = "font-size:16px;margin:0 0 12px;";
|
|
728
1042
|
const list = document.createElement("div");
|
|
729
1043
|
list.style.cssText = "font-size:13px;line-height:1.8;";
|
|
@@ -734,7 +1048,7 @@ function openShortcutsModal() {
|
|
|
734
1048
|
}
|
|
735
1049
|
const closeBtn = document.createElement("button");
|
|
736
1050
|
closeBtn.type = "button";
|
|
737
|
-
closeBtn.textContent =
|
|
1051
|
+
closeBtn.textContent = t6("shortcuts.close", "\u95DC\u9589");
|
|
738
1052
|
closeBtn.style.cssText = "margin-top:14px;padding:6px 14px;background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;cursor:pointer;";
|
|
739
1053
|
const close = () => backdrop.remove();
|
|
740
1054
|
closeBtn.onclick = close;
|
|
@@ -784,7 +1098,15 @@ function mountChartLayout(root, opts = {}) {
|
|
|
784
1098
|
root.style.display = "flex";
|
|
785
1099
|
root.style.flexDirection = "column";
|
|
786
1100
|
root.style.height = "100%";
|
|
787
|
-
root.style.
|
|
1101
|
+
root.style.width = "100%";
|
|
1102
|
+
root.style.minWidth = "0";
|
|
1103
|
+
root.style.boxSizing = "border-box";
|
|
1104
|
+
root.style.overflow = "visible";
|
|
1105
|
+
const themeProvider = opts.themeProvider ?? createThemeProvider();
|
|
1106
|
+
const i18nProvider = opts.i18n ?? createI18nProvider();
|
|
1107
|
+
themeProvider.subscribe((theme) => {
|
|
1108
|
+
root.style.background = theme === "dark" ? "#0d1117" : "#f6f8fa";
|
|
1109
|
+
});
|
|
788
1110
|
let activeTool = opts.activeDrawingTool ?? "cursor";
|
|
789
1111
|
let setActiveDesktop = null;
|
|
790
1112
|
let setActiveMobile = null;
|
|
@@ -797,10 +1119,14 @@ function mountChartLayout(root, opts = {}) {
|
|
|
797
1119
|
setActiveDrawingTool(tool);
|
|
798
1120
|
opts.onDrawingToolSelect?.(tool);
|
|
799
1121
|
};
|
|
1122
|
+
const headerSlot = document.createElement("div");
|
|
1123
|
+
headerSlot.className = "tv-layout-header";
|
|
1124
|
+
headerSlot.style.cssText = "display:none;flex-shrink:0;width:100%;min-width:0;overflow:visible;position:relative;z-index:30;";
|
|
800
1125
|
const body = document.createElement("div");
|
|
801
|
-
body.
|
|
1126
|
+
body.className = "tv-layout-body";
|
|
1127
|
+
body.style.cssText = "display:flex;flex:1;min-height:0;min-width:0;overflow:hidden;position:relative;z-index:0;";
|
|
802
1128
|
const leftAside = document.createElement("aside");
|
|
803
|
-
leftAside.style.cssText = "width:48px;border-right:1px solid #30363d;background:#161b22;display:flex;flex-direction:column;align-items:center;padding:8px 4px;gap:8px;flex-shrink:0;
|
|
1129
|
+
leftAside.style.cssText = "width:48px;border-right:1px solid #30363d;background:#161b22;display:flex;flex-direction:column;align-items:center;padding:8px 4px;gap:8px;flex-shrink:0;";
|
|
804
1130
|
const bottomBar = document.createElement("div");
|
|
805
1131
|
bottomBar.style.cssText = "display:none;flex-shrink:0;gap:6px;padding:6px 8px;border-top:1px solid #30363d;background:#161b22;overflow-x:auto;";
|
|
806
1132
|
const chartColumn = document.createElement("div");
|
|
@@ -815,11 +1141,10 @@ function mountChartLayout(root, opts = {}) {
|
|
|
815
1141
|
onStyleChange: opts.onDrawingStyleChange
|
|
816
1142
|
});
|
|
817
1143
|
opts.onDrawingSelectionBind?.(propertiesPanel.bind);
|
|
1144
|
+
root.appendChild(headerSlot);
|
|
818
1145
|
root.appendChild(body);
|
|
819
1146
|
root.appendChild(bottomBar);
|
|
820
|
-
let topBar =
|
|
821
|
-
topBar.style.display = "none";
|
|
822
|
-
root.insertBefore(topBar, body);
|
|
1147
|
+
let topBar = headerSlot;
|
|
823
1148
|
let setActiveInterval = () => {
|
|
824
1149
|
};
|
|
825
1150
|
const crosshairLegend = mountCrosshairLegend(chartHost, { symbol: opts.initialSymbol });
|
|
@@ -853,18 +1178,27 @@ function mountChartLayout(root, opts = {}) {
|
|
|
853
1178
|
const applyLayoutFeatures = () => {
|
|
854
1179
|
const f = layoutFeatures;
|
|
855
1180
|
if (f.showTopBar) {
|
|
856
|
-
|
|
1181
|
+
headerSlot.replaceChildren();
|
|
1182
|
+
headerSlot.style.display = "";
|
|
857
1183
|
const mounted = mountTopBar(
|
|
858
|
-
|
|
1184
|
+
headerSlot,
|
|
859
1185
|
Object.assign(opts, {
|
|
860
1186
|
symbolInput: f.symbolInput,
|
|
861
|
-
showSettings: f.showSettings
|
|
1187
|
+
showSettings: f.showSettings,
|
|
1188
|
+
themeProvider: opts.themeProvider ?? themeProvider,
|
|
1189
|
+
i18n: opts.i18n ?? i18nProvider,
|
|
1190
|
+
onThemeChange: (theme) => {
|
|
1191
|
+
opts.onThemeChange?.(theme);
|
|
1192
|
+
if (!opts.onThemeChange) opts.onThemeToggle?.();
|
|
1193
|
+
}
|
|
862
1194
|
})
|
|
863
1195
|
);
|
|
864
1196
|
topBar = mounted.el;
|
|
865
1197
|
setActiveInterval = mounted.setActiveInterval;
|
|
866
1198
|
} else {
|
|
867
|
-
|
|
1199
|
+
headerSlot.replaceChildren();
|
|
1200
|
+
headerSlot.style.display = "none";
|
|
1201
|
+
topBar = headerSlot;
|
|
868
1202
|
}
|
|
869
1203
|
if (f.showLeftToolbar) mountLeftToolbar();
|
|
870
1204
|
else unmountLeftToolbar();
|
|
@@ -902,7 +1236,7 @@ function mountChartLayout(root, opts = {}) {
|
|
|
902
1236
|
}
|
|
903
1237
|
|
|
904
1238
|
// src/drawing-context-menu.ts
|
|
905
|
-
import { t as
|
|
1239
|
+
import { t as t7 } from "@coderyo/i18n";
|
|
906
1240
|
function openDrawingContextMenu(clientX, clientY, drawing, handlers) {
|
|
907
1241
|
const menu = document.createElement("div");
|
|
908
1242
|
menu.style.cssText = "position:fixed;z-index:60;min-width:168px;padding:4px 0;background:#161b22;border:1px solid #30363d;border-radius:6px;box-shadow:0 8px 24px #01040988;";
|
|
@@ -924,16 +1258,16 @@ function openDrawingContextMenu(clientX, clientY, drawing, handlers) {
|
|
|
924
1258
|
};
|
|
925
1259
|
if (drawing) {
|
|
926
1260
|
const locked = Boolean(drawing.meta?.locked);
|
|
927
|
-
add(
|
|
928
|
-
add(
|
|
1261
|
+
add(t7("drawing.ctx.delete", "\u522A\u9664"), handlers.onDelete);
|
|
1262
|
+
add(t7("drawing.ctx.copy", "\u8907\u88FD"), handlers.onCopy);
|
|
929
1263
|
add(
|
|
930
|
-
locked ?
|
|
1264
|
+
locked ? t7("drawing.ctx.unlock", "\u89E3\u9396") : t7("drawing.ctx.lock", "\u9396\u5B9A"),
|
|
931
1265
|
handlers.onToggleLock
|
|
932
1266
|
);
|
|
933
1267
|
if (drawing.type === "text") {
|
|
934
|
-
add(
|
|
1268
|
+
add(t7("drawing.ctx.editText", "\u7DE8\u8F2F\u6587\u5B57"), handlers.onEditText);
|
|
935
1269
|
}
|
|
936
|
-
add(
|
|
1270
|
+
add(t7("drawing.ctx.deselect", "\u53D6\u6D88\u9078\u53D6"), handlers.onDeselect);
|
|
937
1271
|
}
|
|
938
1272
|
document.body.appendChild(menu);
|
|
939
1273
|
const close = () => {
|
|
@@ -985,7 +1319,7 @@ import {
|
|
|
985
1319
|
lineNumbers,
|
|
986
1320
|
placeholder
|
|
987
1321
|
} from "@codemirror/view";
|
|
988
|
-
import { t as
|
|
1322
|
+
import { t as t8 } from "@coderyo/i18n";
|
|
989
1323
|
|
|
990
1324
|
// src/pine-language.ts
|
|
991
1325
|
import { StreamLanguage } from "@codemirror/language";
|
|
@@ -1045,10 +1379,10 @@ function createPineLinter(onStatus) {
|
|
|
1045
1379
|
const src = view.state.doc.toString();
|
|
1046
1380
|
const result = compilePineLite(src);
|
|
1047
1381
|
if (result.ok) {
|
|
1048
|
-
onStatus?.(
|
|
1382
|
+
onStatus?.(t8("pine.status.ok", "\u8A9E\u6CD5\u6B63\u78BA"), true);
|
|
1049
1383
|
return [];
|
|
1050
1384
|
}
|
|
1051
|
-
onStatus?.(result.errors[0] ??
|
|
1385
|
+
onStatus?.(result.errors[0] ?? t8("pine.status.error", "\u8A9E\u6CD5\u932F\u8AA4"), false);
|
|
1052
1386
|
return pineDiagnosticsToCm(src, result.diagnostics ?? []);
|
|
1053
1387
|
});
|
|
1054
1388
|
}
|
|
@@ -1077,16 +1411,16 @@ function mountPineEditorPanel(parent, opts = {}) {
|
|
|
1077
1411
|
wrap.open = true;
|
|
1078
1412
|
wrap.style.cssText = "flex-shrink:0;border-top:1px solid #30363d;background:#161b22;max-height:220px;display:flex;flex-direction:column;";
|
|
1079
1413
|
const summary = document.createElement("summary");
|
|
1080
|
-
summary.textContent =
|
|
1414
|
+
summary.textContent = t8("pine.editor.title", "Pine \u8173\u672C\u7DE8\u8F2F\u5668");
|
|
1081
1415
|
summary.style.cssText = "cursor:pointer;color:#58a6ff;user-select:none;padding:6px 12px;font-size:12px;flex-shrink:0;";
|
|
1082
1416
|
const toolbar = document.createElement("div");
|
|
1083
1417
|
toolbar.style.cssText = "display:flex;align-items:center;gap:8px;padding:0 12px 6px;flex-shrink:0;";
|
|
1084
1418
|
const status = document.createElement("span");
|
|
1085
1419
|
status.style.cssText = "font-size:11px;color:#8b949e;flex:1;";
|
|
1086
|
-
status.textContent =
|
|
1420
|
+
status.textContent = t8("pine.status.idle", "\u5C31\u7DD2");
|
|
1087
1421
|
const applyBtn = document.createElement("button");
|
|
1088
1422
|
applyBtn.type = "button";
|
|
1089
|
-
applyBtn.textContent =
|
|
1423
|
+
applyBtn.textContent = t8("pine.apply", "\u5957\u7528\u81F3\u5716\u8868");
|
|
1090
1424
|
applyBtn.style.cssText = "padding:4px 10px;background:#238636;color:#fff;border:1px solid #2ea043;border-radius:4px;cursor:pointer;font-size:11px;";
|
|
1091
1425
|
const host = document.createElement("div");
|
|
1092
1426
|
host.style.cssText = "flex:1;min-height:120px;max-height:160px;overflow:hidden;border-top:1px solid #30363d;";
|
|
@@ -1101,10 +1435,10 @@ function mountPineEditorPanel(parent, opts = {}) {
|
|
|
1101
1435
|
savePineScriptPreference(src);
|
|
1102
1436
|
if (r.ok) {
|
|
1103
1437
|
status.style.color = "#3fb950";
|
|
1104
|
-
status.textContent =
|
|
1438
|
+
status.textContent = t8("pine.status.ok", "\u8A9E\u6CD5\u6B63\u78BA");
|
|
1105
1439
|
} else {
|
|
1106
1440
|
status.style.color = "#f85149";
|
|
1107
|
-
status.textContent = r.errors[0] ??
|
|
1441
|
+
status.textContent = r.errors[0] ?? t8("pine.status.error", "\u8A9E\u6CD5\u932F\u8AA4");
|
|
1108
1442
|
}
|
|
1109
1443
|
};
|
|
1110
1444
|
const extensions = [
|
|
@@ -1122,7 +1456,7 @@ function mountPineEditorPanel(parent, opts = {}) {
|
|
|
1122
1456
|
status.textContent = msg;
|
|
1123
1457
|
}),
|
|
1124
1458
|
keymap.of([...defaultKeymap, ...historyKeymap]),
|
|
1125
|
-
placeholder(
|
|
1459
|
+
placeholder(t8("pine.placeholder", "// plot(sma(close, 20))")),
|
|
1126
1460
|
EditorView.updateListener.of((u) => {
|
|
1127
1461
|
if (!u.docChanged) return;
|
|
1128
1462
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
@@ -1155,23 +1489,32 @@ export {
|
|
|
1155
1489
|
GRID_SETTING_KEY,
|
|
1156
1490
|
PINE_SCRIPT_STORAGE_KEY,
|
|
1157
1491
|
RETURN_CURSOR_KEY,
|
|
1492
|
+
THEME_STORAGE_KEY,
|
|
1493
|
+
applyThemeToDocument,
|
|
1158
1494
|
attachChartContextMenu,
|
|
1159
1495
|
bindShortcutsModal,
|
|
1160
1496
|
createDemoLayoutOptions,
|
|
1497
|
+
createI18nProvider,
|
|
1498
|
+
createSymbolSearchDialog,
|
|
1499
|
+
createThemeProvider,
|
|
1161
1500
|
loadIndicatorConfig,
|
|
1162
1501
|
loadPineScriptPreference,
|
|
1163
1502
|
loadReturnToCursorPreference,
|
|
1164
1503
|
loadShowGridPreference,
|
|
1504
|
+
loadTheme,
|
|
1165
1505
|
mergeLayoutFeatures,
|
|
1166
1506
|
mountChartLayout,
|
|
1167
1507
|
mountCodeSnippetPanel,
|
|
1168
1508
|
mountCrosshairLegend,
|
|
1169
1509
|
mountDrawingPropertiesPanel,
|
|
1510
|
+
mountLogoSlot,
|
|
1170
1511
|
mountPineEditorPanel,
|
|
1171
1512
|
mountSettingsPanel as mountSettingsMenu,
|
|
1172
1513
|
mountSettingsPanel,
|
|
1173
1514
|
mountStatusBar,
|
|
1174
1515
|
mountSymbolSearch,
|
|
1516
|
+
mountSymbolSearchDialogTrigger,
|
|
1517
|
+
mountThemeToggle,
|
|
1175
1518
|
mountTopBar,
|
|
1176
1519
|
openDrawingContextMenu,
|
|
1177
1520
|
openShortcutsModal,
|
|
@@ -1180,6 +1523,7 @@ export {
|
|
|
1180
1523
|
saveIndicatorConfig,
|
|
1181
1524
|
savePineScriptPreference,
|
|
1182
1525
|
saveReturnToCursorPreference,
|
|
1183
|
-
saveShowGridPreference
|
|
1526
|
+
saveShowGridPreference,
|
|
1527
|
+
saveTheme
|
|
1184
1528
|
};
|
|
1185
1529
|
//# sourceMappingURL=index.js.map
|