@coderyo/ui-shell 1.0.0-rc.2 → 1.0.0-rc.4

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 CHANGED
@@ -1,7 +1,9 @@
1
+ import * as _coderyo_data from '@coderyo/data';
1
2
  import { Interval, SymbolSearchHit } from '@coderyo/data';
2
3
  import { IndicatorConfig } from '@coderyo/indicators';
3
4
  import * as _coderyo_drawings from '@coderyo/drawings';
4
5
  import { DrawingRecord } from '@coderyo/drawings';
6
+ import { StreamLanguage } from '@codemirror/language';
5
7
 
6
8
  declare const GRID_SETTING_KEY = "tradview:settings:showGrid";
7
9
  declare const RETURN_CURSOR_KEY = "tradview:settings:returnToCursorAfterDraw";
@@ -25,6 +27,8 @@ declare function mountSettingsPanel(parent: HTMLElement, opts?: SettingsPanelOpt
25
27
  type SymbolInputMode = 'manual' | 'search' | 'none';
26
28
  interface TopBarOptions {
27
29
  intervals?: Interval[];
30
+ /** Highlighted interval button (defaults to first in `intervals`). */
31
+ activeInterval?: Interval;
28
32
  initialSymbol?: string;
29
33
  onSymbolSearch?: (query: string) => Promise<SymbolSearchHit[]>;
30
34
  onSymbolSelect?: (symbol: string) => void;
@@ -36,7 +40,10 @@ interface TopBarOptions {
36
40
  symbolInput?: SymbolInputMode;
37
41
  showSettings?: boolean;
38
42
  }
39
- declare function mountTopBar(parent: HTMLElement, opts?: TopBarOptions): HTMLElement;
43
+ declare function mountTopBar(parent: HTMLElement, opts?: TopBarOptions): {
44
+ el: HTMLElement;
45
+ setActiveInterval: (interval: Interval) => void;
46
+ };
40
47
 
41
48
  interface ContextMenuAction {
42
49
  id: string;
@@ -153,6 +160,7 @@ declare function mountChartLayout(root: HTMLElement, opts?: ChartLayoutOptions):
153
160
  chartHost: HTMLElement;
154
161
  indicatorHost: HTMLElement;
155
162
  topBar: HTMLElement;
163
+ setActiveInterval: (interval: _coderyo_data.Interval) => void;
156
164
  statusBar: ReturnType<typeof mountStatusBar>;
157
165
  crosshairLegend: ReturnType<typeof mountCrosshairLegend>;
158
166
  detachContextMenu: () => void;
@@ -183,4 +191,23 @@ declare function openShortcutsModal(): void;
183
191
 
184
192
  declare function mountCodeSnippetPanel(parent: HTMLElement, getCode: () => string): HTMLElement;
185
193
 
186
- export { type ChartLayoutOptions, type ContextMenuAction, type ContextMenuOptions, type CrosshairLegendOptions, DEFAULT_LAYOUT_FEATURES, type DrawingContextMenuHandlers, type DrawingPropertiesPanelOptions, type DrawingToolId, GRID_SETTING_KEY, type LayoutFeatures, type OhlcvSnapshot, RETURN_CURSOR_KEY, type ResolvedLayoutFeatures, type SettingsPanelOptions as SettingsMenuOptions, type SettingsPanelOptions, type StatusBarOptions, type SymbolInputMode, type SymbolSearchOptions, type TopBarOptions, attachChartContextMenu, bindShortcutsModal, createDemoLayoutOptions, loadIndicatorConfig, loadReturnToCursorPreference, loadShowGridPreference, mergeLayoutFeatures, mountChartLayout, mountCodeSnippetPanel, mountCrosshairLegend, mountDrawingPropertiesPanel, mountSettingsPanel as mountSettingsMenu, mountSettingsPanel, mountStatusBar, mountSymbolSearch, mountTopBar, openDrawingContextMenu, openShortcutsModal, resolveLayoutFeatures, saveIndicatorConfig, saveReturnToCursorPreference, saveShowGridPreference };
194
+ declare const PINE_SCRIPT_STORAGE_KEY = "tradview:pine:script";
195
+ declare function loadPineScriptPreference(): string | null;
196
+ declare function savePineScriptPreference(script: string): void;
197
+ interface PineEditorPanelOptions {
198
+ initialScript?: string;
199
+ /** Debounced apply to chart (ms). Default 400. */
200
+ debounceMs?: number;
201
+ onApply?: (script: string, compileOk: boolean) => void;
202
+ }
203
+ declare function mountPineEditorPanel(parent: HTMLElement, opts?: PineEditorPanelOptions): {
204
+ el: HTMLElement;
205
+ getScript: () => string;
206
+ setScript: (script: string) => void;
207
+ applyNow: () => void;
208
+ destroy: () => void;
209
+ };
210
+
211
+ declare const pineLanguage: StreamLanguage<{}>;
212
+
213
+ export { type ChartLayoutOptions, type ContextMenuAction, type ContextMenuOptions, type CrosshairLegendOptions, DEFAULT_LAYOUT_FEATURES, type DrawingContextMenuHandlers, type DrawingPropertiesPanelOptions, type DrawingToolId, GRID_SETTING_KEY, type LayoutFeatures, type OhlcvSnapshot, PINE_SCRIPT_STORAGE_KEY, type PineEditorPanelOptions, RETURN_CURSOR_KEY, type ResolvedLayoutFeatures, type SettingsPanelOptions as SettingsMenuOptions, type SettingsPanelOptions, type StatusBarOptions, type SymbolInputMode, type SymbolSearchOptions, type TopBarOptions, attachChartContextMenu, bindShortcutsModal, createDemoLayoutOptions, loadIndicatorConfig, loadPineScriptPreference, loadReturnToCursorPreference, loadShowGridPreference, mergeLayoutFeatures, mountChartLayout, mountCodeSnippetPanel, mountCrosshairLegend, mountDrawingPropertiesPanel, mountPineEditorPanel, mountSettingsPanel as mountSettingsMenu, mountSettingsPanel, mountStatusBar, mountSymbolSearch, mountTopBar, openDrawingContextMenu, openShortcutsModal, pineLanguage, resolveLayoutFeatures, saveIndicatorConfig, savePineScriptPreference, saveReturnToCursorPreference, saveShowGridPreference };
package/dist/index.js CHANGED
@@ -168,6 +168,30 @@ function mountSettingsPanel(parent, opts = {}) {
168
168
  };
169
169
  src.appendChild(sel);
170
170
  content.appendChild(src);
171
+ content.appendChild(
172
+ checkbox(t("settings.ind.ema", "EMA \u758A\u52A0"), indicatorConfig.showEma, (v) => {
173
+ indicatorConfig = { ...indicatorConfig, showEma: v };
174
+ opts.onIndicatorConfigChange?.(indicatorConfig);
175
+ })
176
+ );
177
+ content.appendChild(
178
+ numberField("EMA", indicatorConfig.emaPeriod, (n) => {
179
+ indicatorConfig = { ...indicatorConfig, emaPeriod: n };
180
+ opts.onIndicatorConfigChange?.(indicatorConfig);
181
+ })
182
+ );
183
+ content.appendChild(
184
+ checkbox(t("settings.ind.boll", "BOLL \u901A\u9053"), indicatorConfig.showBoll, (v) => {
185
+ indicatorConfig = { ...indicatorConfig, showBoll: v };
186
+ opts.onIndicatorConfigChange?.(indicatorConfig);
187
+ })
188
+ );
189
+ content.appendChild(
190
+ numberField("BOLL", indicatorConfig.bollPeriod, (n) => {
191
+ indicatorConfig = { ...indicatorConfig, bollPeriod: n };
192
+ opts.onIndicatorConfigChange?.(indicatorConfig);
193
+ })
194
+ );
171
195
  content.appendChild(
172
196
  numberField("MA", indicatorConfig.maPeriod, (n) => {
173
197
  indicatorConfig = { ...indicatorConfig, maPeriod: n };
@@ -325,14 +349,30 @@ function mountTopBar(parent, opts = {}) {
325
349
  });
326
350
  }
327
351
  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;";
353
+ const btnActiveStyle = btnStyle.replace("#21262d", "#388bfd").replace("#e6edf3", "#fff");
354
+ const intervalButtons = /* @__PURE__ */ new Map();
355
+ let activeInterval = opts.activeInterval ?? intervals[0];
356
+ const paintIntervalButtons = () => {
357
+ for (const [iv, btn] of intervalButtons) {
358
+ btn.style.cssText = iv === activeInterval ? btnActiveStyle : btnStyle;
359
+ }
360
+ };
328
361
  for (const iv of intervals) {
329
362
  const btn = document.createElement("button");
330
363
  btn.type = "button";
331
364
  btn.textContent = t3(`interval.${iv}`, iv);
332
- btn.style.cssText = "background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;";
333
- btn.onclick = () => opts.onIntervalChange?.(iv);
365
+ btn.style.cssText = btnStyle;
366
+ btn.onclick = () => {
367
+ if (activeInterval === iv) return;
368
+ activeInterval = iv;
369
+ paintIntervalButtons();
370
+ opts.onIntervalChange?.(iv);
371
+ };
372
+ intervalButtons.set(iv, btn);
334
373
  bar.appendChild(btn);
335
374
  }
375
+ paintIntervalButtons();
336
376
  const spacer = document.createElement("div");
337
377
  spacer.style.flex = "1";
338
378
  bar.appendChild(spacer);
@@ -349,7 +389,14 @@ function mountTopBar(parent, opts = {}) {
349
389
  bar.appendChild(mkBtn("\u26F6", opts.onFullscreen));
350
390
  bar.appendChild(mkBtn("\u{1F4F7}", opts.onScreenshot));
351
391
  parent.prepend(bar);
352
- return bar;
392
+ return {
393
+ el: bar,
394
+ setActiveInterval: (interval) => {
395
+ if (!intervalButtons.has(interval)) return;
396
+ activeInterval = interval;
397
+ paintIntervalButtons();
398
+ }
399
+ };
353
400
  }
354
401
 
355
402
  // src/context-menu.ts
@@ -773,6 +820,8 @@ function mountChartLayout(root, opts = {}) {
773
820
  let topBar = document.createElement("div");
774
821
  topBar.style.display = "none";
775
822
  root.insertBefore(topBar, body);
823
+ let setActiveInterval = () => {
824
+ };
776
825
  const crosshairLegend = mountCrosshairLegend(chartHost, { symbol: opts.initialSymbol });
777
826
  let detachContextMenu = () => {
778
827
  };
@@ -805,11 +854,15 @@ function mountChartLayout(root, opts = {}) {
805
854
  const f = layoutFeatures;
806
855
  if (f.showTopBar) {
807
856
  topBar.remove();
808
- topBar = mountTopBar(root, {
809
- ...opts,
810
- symbolInput: f.symbolInput,
811
- showSettings: f.showSettings
812
- });
857
+ const mounted = mountTopBar(
858
+ root,
859
+ Object.assign(opts, {
860
+ symbolInput: f.symbolInput,
861
+ showSettings: f.showSettings
862
+ })
863
+ );
864
+ topBar = mounted.el;
865
+ setActiveInterval = mounted.setActiveInterval;
813
866
  } else {
814
867
  topBar.style.display = "none";
815
868
  }
@@ -834,6 +887,7 @@ function mountChartLayout(root, opts = {}) {
834
887
  chartHost,
835
888
  indicatorHost,
836
889
  topBar,
890
+ setActiveInterval,
837
891
  statusBar,
838
892
  crosshairLegend,
839
893
  detachContextMenu,
@@ -915,14 +969,197 @@ function mountCodeSnippetPanel(parent, getCode) {
915
969
  parent.appendChild(wrap);
916
970
  return wrap;
917
971
  }
972
+
973
+ // src/pine-editor-panel.ts
974
+ import { compilePineLite } from "@coderyo/pine-lite";
975
+ import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
976
+ import { syntaxHighlighting, defaultHighlightStyle, bracketMatching } from "@codemirror/language";
977
+ import { linter } from "@codemirror/lint";
978
+ import { EditorState } from "@codemirror/state";
979
+ import {
980
+ EditorView,
981
+ drawSelection,
982
+ highlightActiveLine,
983
+ highlightActiveLineGutter,
984
+ keymap,
985
+ lineNumbers,
986
+ placeholder
987
+ } from "@codemirror/view";
988
+ import { t as t9 } from "@coderyo/i18n";
989
+
990
+ // src/pine-language.ts
991
+ import { StreamLanguage } from "@codemirror/language";
992
+ var pineLanguage = StreamLanguage.define({
993
+ name: "pine-lite",
994
+ startState: () => ({}),
995
+ token(stream) {
996
+ if (stream.eatSpace()) return null;
997
+ if (stream.match(/\/\/.*/)) return "comment";
998
+ if (stream.match(/0x[\da-fA-F]+|\d+\.?\d*(?:[eE][+-]?\d+)?/)) return "number";
999
+ if (stream.match(/\b(?:var|plot|if|else|while|for|to|and|or|not|true|false)\b/)) return "keyword";
1000
+ if (stream.match(/\b(?:sma|ema|rsi|close|open|high|low|volume|hl2|hlc3)\b/)) return "variableName";
1001
+ if (stream.match(/:=|==|!=|<=|>=|[+\-*/<>=(){},]/)) return "operator";
1002
+ if (stream.match(/[A-Za-z_]\w*/)) return "name";
1003
+ stream.next();
1004
+ return null;
1005
+ }
1006
+ });
1007
+
1008
+ // src/pine-editor-panel.ts
1009
+ var PINE_SCRIPT_STORAGE_KEY = "tradview:pine:script";
1010
+ function loadPineScriptPreference() {
1011
+ try {
1012
+ return localStorage.getItem(PINE_SCRIPT_STORAGE_KEY);
1013
+ } catch {
1014
+ return null;
1015
+ }
1016
+ }
1017
+ function savePineScriptPreference(script) {
1018
+ try {
1019
+ localStorage.setItem(PINE_SCRIPT_STORAGE_KEY, script);
1020
+ } catch {
1021
+ }
1022
+ }
1023
+ function offsetAtLineCol(doc, line, col) {
1024
+ const lines = doc.split("\n");
1025
+ let off = 0;
1026
+ for (let i = 0; i < line - 1 && i < lines.length; i++) {
1027
+ off += lines[i].length + 1;
1028
+ }
1029
+ return off + Math.max(0, col - 1);
1030
+ }
1031
+ function pineDiagnosticsToCm(doc, diags) {
1032
+ return diags.map((d) => {
1033
+ const from = offsetAtLineCol(doc, d.line, d.col);
1034
+ const to = d.endCol != null ? offsetAtLineCol(doc, d.line, d.endCol) : from + 1;
1035
+ return {
1036
+ from,
1037
+ to: Math.max(from + 1, to),
1038
+ severity: d.severity,
1039
+ message: d.message
1040
+ };
1041
+ });
1042
+ }
1043
+ function createPineLinter(onStatus) {
1044
+ return linter((view) => {
1045
+ const src = view.state.doc.toString();
1046
+ const result = compilePineLite(src);
1047
+ if (result.ok) {
1048
+ onStatus?.(t9("pine.status.ok", "\u8A9E\u6CD5\u6B63\u78BA"), true);
1049
+ return [];
1050
+ }
1051
+ onStatus?.(result.errors[0] ?? t9("pine.status.error", "\u8A9E\u6CD5\u932F\u8AA4"), false);
1052
+ return pineDiagnosticsToCm(src, result.diagnostics ?? []);
1053
+ });
1054
+ }
1055
+ var darkTheme = EditorView.theme({
1056
+ "&": {
1057
+ backgroundColor: "#0d1117",
1058
+ color: "#e6edf3",
1059
+ fontSize: "12px"
1060
+ },
1061
+ ".cm-content": { caretColor: "#58a6ff", fontFamily: 'Consolas, "Cascadia Code", monospace' },
1062
+ ".cm-gutters": {
1063
+ backgroundColor: "#161b22",
1064
+ color: "#8b949e",
1065
+ borderRight: "1px solid #30363d"
1066
+ },
1067
+ ".cm-activeLine": { backgroundColor: "#161b22aa" },
1068
+ ".cm-activeLineGutter": { backgroundColor: "#21262d" },
1069
+ ".cm-selectionBackground, &.cm-focused .cm-selectionBackground": { backgroundColor: "#264f78" },
1070
+ ".cm-cursor": { borderLeftColor: "#58a6ff" },
1071
+ ".cm-lintRange-error": { backgroundImage: "none", borderBottom: "2px wavy #f85149" },
1072
+ ".cm-lintRange-warning": { backgroundImage: "none", borderBottom: "2px wavy #d29922" }
1073
+ });
1074
+ function mountPineEditorPanel(parent, opts = {}) {
1075
+ const wrap = document.createElement("details");
1076
+ wrap.className = "tv-pine-editor";
1077
+ wrap.open = true;
1078
+ wrap.style.cssText = "flex-shrink:0;border-top:1px solid #30363d;background:#161b22;max-height:220px;display:flex;flex-direction:column;";
1079
+ const summary = document.createElement("summary");
1080
+ summary.textContent = t9("pine.editor.title", "Pine \u8173\u672C\u7DE8\u8F2F\u5668");
1081
+ summary.style.cssText = "cursor:pointer;color:#58a6ff;user-select:none;padding:6px 12px;font-size:12px;flex-shrink:0;";
1082
+ const toolbar = document.createElement("div");
1083
+ toolbar.style.cssText = "display:flex;align-items:center;gap:8px;padding:0 12px 6px;flex-shrink:0;";
1084
+ const status = document.createElement("span");
1085
+ status.style.cssText = "font-size:11px;color:#8b949e;flex:1;";
1086
+ status.textContent = t9("pine.status.idle", "\u5C31\u7DD2");
1087
+ const applyBtn = document.createElement("button");
1088
+ applyBtn.type = "button";
1089
+ applyBtn.textContent = t9("pine.apply", "\u5957\u7528\u81F3\u5716\u8868");
1090
+ applyBtn.style.cssText = "padding:4px 10px;background:#238636;color:#fff;border:1px solid #2ea043;border-radius:4px;cursor:pointer;font-size:11px;";
1091
+ const host = document.createElement("div");
1092
+ host.style.cssText = "flex:1;min-height:120px;max-height:160px;overflow:hidden;border-top:1px solid #30363d;";
1093
+ toolbar.append(status, applyBtn);
1094
+ wrap.append(summary, toolbar, host);
1095
+ parent.appendChild(wrap);
1096
+ let debounceTimer = null;
1097
+ const debounceMs = opts.debounceMs ?? 400;
1098
+ const runApply = (src) => {
1099
+ const r = compilePineLite(src);
1100
+ opts.onApply?.(src, r.ok);
1101
+ savePineScriptPreference(src);
1102
+ if (r.ok) {
1103
+ status.style.color = "#3fb950";
1104
+ status.textContent = t9("pine.status.ok", "\u8A9E\u6CD5\u6B63\u78BA");
1105
+ } else {
1106
+ status.style.color = "#f85149";
1107
+ status.textContent = r.errors[0] ?? t9("pine.status.error", "\u8A9E\u6CD5\u932F\u8AA4");
1108
+ }
1109
+ };
1110
+ const extensions = [
1111
+ lineNumbers(),
1112
+ highlightActiveLineGutter(),
1113
+ highlightActiveLine(),
1114
+ drawSelection(),
1115
+ bracketMatching(),
1116
+ history(),
1117
+ pineLanguage,
1118
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
1119
+ darkTheme,
1120
+ createPineLinter((msg, ok) => {
1121
+ status.style.color = ok ? "#8b949e" : "#f85149";
1122
+ status.textContent = msg;
1123
+ }),
1124
+ keymap.of([...defaultKeymap, ...historyKeymap]),
1125
+ placeholder(t9("pine.placeholder", "// plot(sma(close, 20))")),
1126
+ EditorView.updateListener.of((u) => {
1127
+ if (!u.docChanged) return;
1128
+ if (debounceTimer) clearTimeout(debounceTimer);
1129
+ debounceTimer = setTimeout(() => runApply(u.state.doc.toString()), debounceMs);
1130
+ })
1131
+ ];
1132
+ const state = EditorState.create({
1133
+ doc: opts.initialScript ?? "",
1134
+ extensions
1135
+ });
1136
+ const view = new EditorView({ state, parent: host });
1137
+ applyBtn.onclick = () => runApply(view.state.doc.toString());
1138
+ return {
1139
+ el: wrap,
1140
+ getScript: () => view.state.doc.toString(),
1141
+ setScript: (script) => {
1142
+ view.dispatch({
1143
+ changes: { from: 0, to: view.state.doc.length, insert: script }
1144
+ });
1145
+ },
1146
+ applyNow: () => runApply(view.state.doc.toString()),
1147
+ destroy: () => {
1148
+ view.destroy();
1149
+ wrap.remove();
1150
+ }
1151
+ };
1152
+ }
918
1153
  export {
919
1154
  DEFAULT_LAYOUT_FEATURES,
920
1155
  GRID_SETTING_KEY,
1156
+ PINE_SCRIPT_STORAGE_KEY,
921
1157
  RETURN_CURSOR_KEY,
922
1158
  attachChartContextMenu,
923
1159
  bindShortcutsModal,
924
1160
  createDemoLayoutOptions,
925
1161
  loadIndicatorConfig,
1162
+ loadPineScriptPreference,
926
1163
  loadReturnToCursorPreference,
927
1164
  loadShowGridPreference,
928
1165
  mergeLayoutFeatures,
@@ -930,6 +1167,7 @@ export {
930
1167
  mountCodeSnippetPanel,
931
1168
  mountCrosshairLegend,
932
1169
  mountDrawingPropertiesPanel,
1170
+ mountPineEditorPanel,
933
1171
  mountSettingsPanel as mountSettingsMenu,
934
1172
  mountSettingsPanel,
935
1173
  mountStatusBar,
@@ -937,8 +1175,10 @@ export {
937
1175
  mountTopBar,
938
1176
  openDrawingContextMenu,
939
1177
  openShortcutsModal,
1178
+ pineLanguage,
940
1179
  resolveLayoutFeatures,
941
1180
  saveIndicatorConfig,
1181
+ savePineScriptPreference,
942
1182
  saveReturnToCursorPreference,
943
1183
  saveShowGridPreference
944
1184
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/top-bar.ts","../src/user-preferences.ts","../src/settings-panel.ts","../src/symbol-search.ts","../src/context-menu.ts","../src/crosshair-legend.ts","../src/drawing-properties-panel.ts","../src/indicator-pane-host.ts","../src/layout-features.ts","../src/status-bar.ts","../src/shortcuts-modal.ts","../src/chart-layout.ts","../src/drawing-context-menu.ts","../src/code-snippet-panel.ts"],"sourcesContent":["import { DEFAULT_INTERVALS, type Interval, type SymbolSearchHit } from '@coderyo/data';\nimport { t } from '@coderyo/i18n';\nimport { mountSettingsMenu, type SettingsMenuOptions } from './settings-menu.js';\nimport { mountSymbolSearch } from './symbol-search.js';\n\nexport type SymbolInputMode = 'manual' | 'search' | 'none';\n\nexport interface TopBarOptions {\n intervals?: Interval[];\n initialSymbol?: string;\n onSymbolSearch?: (query: string) => Promise<SymbolSearchHit[]>;\n onSymbolSelect?: (symbol: string) => void;\n onIntervalChange?: (interval: Interval) => void;\n onThemeToggle?: () => void;\n onFullscreen?: () => void;\n onScreenshot?: () => void;\n settings?: SettingsMenuOptions;\n symbolInput?: SymbolInputMode;\n showSettings?: boolean;\n}\n\nfunction mountManualSymbolInput(\n parent: HTMLElement,\n opts: { initialSymbol?: string; onSymbolSelect?: (symbol: string) => void },\n): void {\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display:flex;align-items:center;gap:4px;margin-right:8px;';\n const input = document.createElement('input');\n input.type = 'text';\n input.placeholder = t('symbol.search', 'Symbol');\n input.value = opts.initialSymbol ?? '';\n input.style.cssText =\n 'width:140px;background:#0d1117;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 8px;font-size:12px;';\n const apply = () => {\n const v = input.value.trim();\n if (v) opts.onSymbolSelect?.(v);\n };\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') apply();\n });\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = '↵';\n btn.title = 'Apply symbol';\n btn.style.cssText =\n 'background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 8px;cursor:pointer;font-size:12px;';\n btn.onclick = apply;\n wrap.append(input, btn);\n parent.appendChild(wrap);\n}\n\nexport function mountTopBar(parent: HTMLElement, opts: TopBarOptions = {}): HTMLElement {\n const bar = document.createElement('div');\n bar.className = 'tv-topbar';\n bar.style.cssText =\n 'display:flex;gap:8px;padding:8px 12px;align-items:center;border-bottom:1px solid #30363d;background:#161b22;';\n\n const symbolMode = opts.symbolInput ?? 'manual';\n if (symbolMode === 'search' && opts.onSymbolSearch && opts.onSymbolSelect) {\n mountSymbolSearch(bar, {\n initialSymbol: opts.initialSymbol,\n onSearch: opts.onSymbolSearch,\n onSelect: opts.onSymbolSelect,\n });\n } else if (symbolMode === 'manual' && opts.onSymbolSelect) {\n mountManualSymbolInput(bar, {\n initialSymbol: opts.initialSymbol,\n onSymbolSelect: opts.onSymbolSelect,\n });\n }\n\n const intervals = opts.intervals ?? DEFAULT_INTERVALS;\n for (const iv of intervals) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = t(`interval.${iv}`, iv);\n btn.style.cssText =\n 'background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;';\n btn.onclick = () => opts.onIntervalChange?.(iv);\n bar.appendChild(btn);\n }\n\n const spacer = document.createElement('div');\n spacer.style.flex = '1';\n bar.appendChild(spacer);\n\n const mkBtn = (label: string, fn?: () => void) => {\n const b = document.createElement('button');\n b.type = 'button';\n b.textContent = label;\n b.style.cssText =\n 'background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;';\n b.onclick = () => fn?.();\n return b;\n };\n\n bar.appendChild(mkBtn(t('theme.dark', '主題'), opts.onThemeToggle));\n if (opts.showSettings && opts.settings) mountSettingsMenu(bar, opts.settings);\n bar.appendChild(mkBtn('⛶', opts.onFullscreen));\n bar.appendChild(mkBtn('📷', opts.onScreenshot));\n\n parent.prepend(bar);\n return bar;\n}","import type { IndicatorConfig } from '@coderyo/indicators';\nimport { DEFAULT_INDICATOR_CONFIG, indicatorConfigStorageKey } from '@coderyo/indicators';\n\nexport const GRID_SETTING_KEY = 'tradview:settings:showGrid';\nexport const RETURN_CURSOR_KEY = 'tradview:settings:returnToCursorAfterDraw';\n\nexport function loadShowGridPreference(): boolean {\n try {\n return localStorage.getItem(GRID_SETTING_KEY) === '1';\n } catch {\n return false;\n }\n}\n\nexport function saveShowGridPreference(show: boolean): void {\n try {\n localStorage.setItem(GRID_SETTING_KEY, show ? '1' : '0');\n } catch {\n /* ignore */\n }\n}\n\nexport function loadReturnToCursorPreference(): boolean {\n try {\n return localStorage.getItem(RETURN_CURSOR_KEY) === '1';\n } catch {\n return false;\n }\n}\n\nexport function saveReturnToCursorPreference(v: boolean): void {\n try {\n localStorage.setItem(RETURN_CURSOR_KEY, v ? '1' : '0');\n } catch {\n /* ignore */\n }\n}\n\nexport function loadIndicatorConfig(symbol: string, interval: string): IndicatorConfig {\n try {\n const raw = localStorage.getItem(indicatorConfigStorageKey(symbol, interval));\n if (!raw) return { ...DEFAULT_INDICATOR_CONFIG };\n return { ...DEFAULT_INDICATOR_CONFIG, ...JSON.parse(raw) };\n } catch {\n return { ...DEFAULT_INDICATOR_CONFIG };\n }\n}\n\nexport function saveIndicatorConfig(\n symbol: string,\n interval: string,\n config: IndicatorConfig,\n): void {\n try {\n localStorage.setItem(indicatorConfigStorageKey(symbol, interval), JSON.stringify(config));\n } catch {\n /* ignore */\n }\n}","import { DEFAULT_INDICATOR_CONFIG, type IndicatorConfig } from '@coderyo/indicators';\nimport { t } from '@coderyo/i18n';\nimport {\n loadReturnToCursorPreference,\n loadShowGridPreference,\n saveReturnToCursorPreference,\n saveShowGridPreference,\n} from './user-preferences.js';\n\nexport interface SettingsPanelOptions {\n showGrid?: boolean;\n onShowGridChange?: (show: boolean) => void;\n returnToCursorAfterDraw?: boolean;\n onReturnToCursorChange?: (v: boolean) => void;\n indicatorConfig?: IndicatorConfig;\n onIndicatorConfigChange?: (config: IndicatorConfig) => void;\n}\n\nexport function mountSettingsPanel(parent: HTMLElement, opts: SettingsPanelOptions = {}): HTMLElement {\n let open = false;\n let tab: 'chart' | 'drawing' | 'indicator' = 'chart';\n let showGrid = opts.showGrid ?? loadShowGridPreference();\n let returnToCursor = opts.returnToCursorAfterDraw ?? loadReturnToCursorPreference();\n let indicatorConfig = { ...(opts.indicatorConfig ?? DEFAULT_INDICATOR_CONFIG) };\n\n const wrap = document.createElement('div');\n wrap.style.cssText = 'position:relative;';\n\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.title = t('settings.title', '設定');\n btn.textContent = '⚙';\n btn.style.cssText =\n 'background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:14px;';\n\n const panel = document.createElement('div');\n panel.style.cssText =\n 'display:none;position:absolute;right:0;top:100%;margin-top:4px;width:280px;max-height:70vh;overflow:auto;padding:0;background:#161b22;border:1px solid #30363d;border-radius:6px;box-shadow:0 8px 24px #01040988;z-index:30;';\n\n const tabs = document.createElement('div');\n tabs.style.cssText = 'display:flex;border-bottom:1px solid #30363d;';\n const content = document.createElement('div');\n content.style.cssText = 'padding:10px 12px;font-size:12px;';\n\n const tabIds = [\n ['chart', t('settings.tab.chart', '圖表')],\n ['drawing', t('settings.tab.drawing', '繪圖')],\n ['indicator', t('settings.tab.indicator', '指標')],\n ] as const;\n\n const renderTabs = () => {\n tabs.replaceChildren();\n for (const [id, label] of tabIds) {\n const b = document.createElement('button');\n b.type = 'button';\n b.textContent = label;\n b.style.cssText = `flex:1;padding:8px;border:none;cursor:pointer;font-size:12px;${\n tab === id ? 'background:#21262d;color:#e6edf3;' : 'background:transparent;color:#8b949e;'\n }`;\n b.onclick = (e) => {\n e.stopPropagation();\n tab = id;\n renderTabs();\n renderContent();\n };\n tabs.appendChild(b);\n }\n };\n\n const checkbox = (label: string, checked: boolean, onChange: (v: boolean) => void) => {\n const row = document.createElement('label');\n row.style.cssText =\n 'display:flex;align-items:center;gap:8px;margin-bottom:8px;cursor:pointer;color:#e6edf3;';\n const input = document.createElement('input');\n input.type = 'checkbox';\n input.checked = checked;\n input.onchange = () => onChange(input.checked);\n row.append(input, document.createTextNode(label));\n return row;\n };\n\n const numberField = (label: string, value: number, onChange: (n: number) => void) => {\n const row = document.createElement('label');\n row.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;';\n row.innerHTML = `<span style=\"color:#8b949e\">${label}</span>`;\n const input = document.createElement('input');\n input.type = 'number';\n input.value = String(value);\n input.style.cssText =\n 'width:72px;padding:2px 6px;border-radius:4px;border:1px solid #30363d;background:#0d1117;color:#e6edf3;';\n input.onchange = () => onChange(Number(input.value) || value);\n row.appendChild(input);\n return row;\n };\n\n const renderContent = () => {\n content.replaceChildren();\n if (tab === 'chart') {\n content.appendChild(\n checkbox(t('settings.showGrid', '顯示網格'), showGrid, (v) => {\n showGrid = v;\n saveShowGridPreference(v);\n opts.onShowGridChange?.(v);\n }),\n );\n } else if (tab === 'drawing') {\n content.appendChild(\n checkbox(t('settings.returnCursor', '畫完切回游標'), returnToCursor, (v) => {\n returnToCursor = v;\n saveReturnToCursorPreference(v);\n opts.onReturnToCursorChange?.(v);\n }),\n );\n } else {\n content.appendChild(\n checkbox(t('settings.ind.macd', 'MACD 窗格'), indicatorConfig.showMacd, (v) => {\n indicatorConfig = { ...indicatorConfig, showMacd: v };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n checkbox(t('settings.ind.rsi', 'RSI 窗格'), indicatorConfig.showRsi, (v) => {\n indicatorConfig = { ...indicatorConfig, showRsi: v };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n checkbox(t('settings.ind.kdj', 'KDJ 窗格'), indicatorConfig.showKdj, (v) => {\n indicatorConfig = { ...indicatorConfig, showKdj: v };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n const src = document.createElement('label');\n src.style.cssText = 'display:flex;justify-content:space-between;margin:8px 0 6px;';\n src.innerHTML = `<span style=\"color:#8b949e\">${t('settings.ind.source', '源')}</span>`;\n const sel = document.createElement('select');\n sel.style.cssText =\n 'background:#0d1117;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:2px 6px;';\n for (const v of ['close', 'hlc3'] as const) {\n const o = document.createElement('option');\n o.value = v;\n o.textContent = v;\n sel.appendChild(o);\n }\n sel.value = indicatorConfig.source;\n sel.onchange = () => {\n indicatorConfig = { ...indicatorConfig, source: sel.value as IndicatorConfig['source'] };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n };\n src.appendChild(sel);\n content.appendChild(src);\n content.appendChild(\n numberField('MA', indicatorConfig.maPeriod, (n) => {\n indicatorConfig = { ...indicatorConfig, maPeriod: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('MACD fast', indicatorConfig.macdFast, (n) => {\n indicatorConfig = { ...indicatorConfig, macdFast: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('MACD slow', indicatorConfig.macdSlow, (n) => {\n indicatorConfig = { ...indicatorConfig, macdSlow: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('RSI', indicatorConfig.rsiPeriod, (n) => {\n indicatorConfig = { ...indicatorConfig, rsiPeriod: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('KDJ', indicatorConfig.kdjPeriod, (n) => {\n indicatorConfig = { ...indicatorConfig, kdjPeriod: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n }\n };\n\n renderTabs();\n renderContent();\n panel.append(tabs, content);\n\n btn.onclick = (e) => {\n e.stopPropagation();\n open = !open;\n panel.style.display = open ? 'block' : 'none';\n };\n\n const close = () => {\n open = false;\n panel.style.display = 'none';\n };\n document.addEventListener('click', close);\n panel.onclick = (e) => e.stopPropagation();\n\n wrap.append(btn, panel);\n parent.appendChild(wrap);\n return wrap;\n}","import type { SymbolSearchHit } from '@coderyo/data';\nimport { t } from '@coderyo/i18n';\n\nexport interface SymbolSearchOptions {\n onSearch: (query: string) => Promise<SymbolSearchHit[]>;\n onSelect: (symbol: string) => void;\n initialSymbol?: string;\n}\n\nexport function mountSymbolSearch(parent: HTMLElement, opts: SymbolSearchOptions): HTMLElement {\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display:flex;align-items:center;gap:6px;margin-right:8px;';\n\n const input = document.createElement('input');\n input.type = 'search';\n input.placeholder = t('symbol.search', '搜尋商品');\n input.value = opts.initialSymbol ?? '';\n input.style.cssText =\n 'width:140px;padding:4px 8px;border-radius:4px;border:1px solid #30363d;background:#0d1117;color:#e6edf3;font-size:12px;';\n\n const list = document.createElement('div');\n list.style.cssText =\n 'display:none;position:absolute;top:100%;left:0;z-index:20;min-width:200px;max-height:200px;overflow:auto;background:#161b22;border:1px solid #30363d;border-radius:4px;';\n\n const box = document.createElement('div');\n box.style.position = 'relative';\n box.append(input, list);\n wrap.appendChild(box);\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const renderHits = (hits: SymbolSearchHit[]) => {\n list.replaceChildren();\n if (hits.length === 0) {\n list.style.display = 'none';\n return;\n }\n for (const hit of hits) {\n const row = document.createElement('button');\n row.type = 'button';\n row.textContent = hit.exchange ? `${hit.symbol} · ${hit.exchange}` : hit.symbol;\n row.style.cssText =\n 'display:block;width:100%;text-align:left;padding:6px 10px;border:none;background:transparent;color:#e6edf3;cursor:pointer;font-size:12px;';\n row.onmouseenter = () => {\n row.style.background = '#21262d';\n };\n row.onmouseleave = () => {\n row.style.background = 'transparent';\n };\n row.onclick = () => {\n input.value = hit.symbol;\n list.style.display = 'none';\n opts.onSelect(hit.symbol);\n };\n list.appendChild(row);\n }\n list.style.display = 'block';\n };\n\n input.addEventListener('input', () => {\n clearTimeout(timer);\n const q = input.value.trim();\n if (q.length < 1) {\n list.style.display = 'none';\n return;\n }\n timer = setTimeout(() => {\n void opts.onSearch(q).then(renderHits).catch(() => {\n list.style.display = 'none';\n });\n }, 200);\n });\n\n document.addEventListener('click', (e) => {\n if (!box.contains(e.target as Node)) list.style.display = 'none';\n });\n\n parent.appendChild(wrap);\n return wrap;\n}","import { t } from '@coderyo/i18n';\n\nexport interface ContextMenuAction {\n id: string;\n label: string;\n onClick: () => void;\n}\n\nexport interface ContextMenuOptions {\n actions?: ContextMenuAction[];\n}\n\nexport function attachChartContextMenu(\n chartHost: HTMLElement,\n opts: ContextMenuOptions = {},\n): () => void {\n let menu: HTMLDivElement | null = null;\n\n const close = () => {\n menu?.remove();\n menu = null;\n };\n\n const open = (x: number, y: number) => {\n close();\n menu = document.createElement('div');\n menu.style.cssText =\n 'position:fixed;z-index:50;min-width:160px;padding:4px 0;background:#161b22;border:1px solid #30363d;border-radius:6px;box-shadow:0 8px 24px #01040988;';\n\n const actions: ContextMenuAction[] = opts.actions ?? [\n {\n id: 'fit',\n label: t('context.fitContent', '適配畫面'),\n onClick: () => {},\n },\n ];\n\n for (const action of actions) {\n const row = document.createElement('button');\n row.type = 'button';\n row.textContent = action.label;\n row.style.cssText =\n 'display:block;width:100%;text-align:left;padding:8px 12px;border:none;background:transparent;color:#e6edf3;font-size:12px;cursor:pointer;';\n row.onmouseenter = () => {\n row.style.background = '#21262d';\n };\n row.onmouseleave = () => {\n row.style.background = 'transparent';\n };\n row.onclick = () => {\n action.onClick();\n close();\n };\n menu.appendChild(row);\n }\n\n menu.style.left = `${x}px`;\n menu.style.top = `${y}px`;\n document.body.appendChild(menu);\n };\n\n const onContext = (e: MouseEvent) => {\n e.preventDefault();\n open(e.clientX, e.clientY);\n };\n\n chartHost.addEventListener('contextmenu', onContext);\n document.addEventListener('click', close);\n document.addEventListener('scroll', close, true);\n\n return () => {\n chartHost.removeEventListener('contextmenu', onContext);\n document.removeEventListener('click', close);\n document.removeEventListener('scroll', close, true);\n close();\n };\n}","import type { OhlcvSnapshot } from './status-bar.js';\n\nexport interface CrosshairLegendOptions {\n symbol?: string;\n interval?: string;\n}\n\nexport function mountCrosshairLegend(\n chartHost: HTMLElement,\n opts: CrosshairLegendOptions = {},\n): {\n el: HTMLElement;\n update: (payload: { time?: number; ohlcv?: OhlcvSnapshot | null }) => void;\n setMeta: (meta: CrosshairLegendOptions) => void;\n hide: () => void;\n} {\n const box = document.createElement('div');\n box.className = 'tv-crosshair-legend';\n box.style.cssText =\n 'display:none;position:absolute;top:8px;left:8px;z-index:10;padding:6px 10px;border-radius:6px;background:#161b22e6;border:1px solid #30363d;font-size:11px;color:#e6edf3;pointer-events:none;line-height:1.5;';\n\n const title = document.createElement('div');\n title.style.cssText = 'color:#8b949e;margin-bottom:2px;';\n const body = document.createElement('div');\n box.append(title, body);\n chartHost.appendChild(box);\n\n const fmt = (n: number | undefined) =>\n n == null ? '—' : n.toLocaleString(undefined, { maximumFractionDigits: 2 });\n\n const render = (payload: { time?: number; ohlcv?: OhlcvSnapshot | null }) => {\n const parts = [opts.symbol, opts.interval].filter(Boolean);\n title.textContent = parts.length ? parts.join(' · ') : '';\n const o = payload.ohlcv;\n if (!o?.c && o?.o == null) {\n box.style.display = 'none';\n return;\n }\n const timeStr = payload.time != null ? new Date(payload.time).toLocaleString() : '';\n body.textContent = `${timeStr}\\nO ${fmt(o?.o)} H ${fmt(o?.h)} L ${fmt(o?.l)} C ${fmt(o?.c)}`;\n box.style.display = 'block';\n };\n\n return {\n el: box,\n update: render,\n setMeta: (meta) => {\n Object.assign(opts, meta);\n title.textContent = [opts.symbol, opts.interval].filter(Boolean).join(' · ');\n },\n hide: () => {\n box.style.display = 'none';\n },\n };\n}","import type { DrawingRecord } from '@coderyo/drawings';\nimport { t } from '@coderyo/i18n';\n\nexport interface DrawingPropertiesPanelOptions {\n onStyleChange?: (patch: { color?: string; lineWidth?: number; text?: string }) => void;\n}\n\nexport function mountDrawingPropertiesPanel(\n parent: HTMLElement,\n opts: DrawingPropertiesPanelOptions = {},\n): { el: HTMLElement; bind: (drawing: DrawingRecord | null) => void } {\n const panel = document.createElement('aside');\n panel.className = 'tv-drawing-props';\n panel.style.cssText =\n 'display:none;width:220px;flex-shrink:0;border-left:1px solid #30363d;background:#161b22;padding:10px 12px;font-size:12px;color:#e6edf3;overflow:auto;';\n\n const title = document.createElement('div');\n title.textContent = t('drawing.props.title', '繪圖屬性');\n title.style.cssText = 'font-weight:600;margin-bottom:10px;';\n\n const typeEl = document.createElement('div');\n typeEl.style.color = '#8b949e';\n typeEl.style.marginBottom = '10px';\n\n const mkRow = (label: string) => {\n const row = document.createElement('label');\n row.style.cssText = 'display:flex;flex-direction:column;gap:4px;margin-bottom:8px;';\n const span = document.createElement('span');\n span.textContent = label;\n span.style.color = '#8b949e';\n row.appendChild(span);\n return row;\n };\n\n const colorRow = mkRow(t('drawing.props.color', '顏色'));\n const colorInput = document.createElement('input');\n colorInput.type = 'color';\n colorInput.style.cssText = 'width:100%;height:28px;border:none;background:transparent;cursor:pointer;';\n colorRow.appendChild(colorInput);\n\n const widthRow = mkRow(t('drawing.props.lineWidth', '線寬'));\n const widthInput = document.createElement('input');\n widthInput.type = 'range';\n widthInput.min = '1';\n widthInput.max = '6';\n widthInput.style.width = '100%';\n widthRow.appendChild(widthInput);\n\n const textRow = mkRow(t('drawing.props.text', '文字'));\n const textInput = document.createElement('input');\n textInput.type = 'text';\n textInput.style.cssText =\n 'padding:4px 8px;border-radius:4px;border:1px solid #30363d;background:#0d1117;color:#e6edf3;';\n textRow.appendChild(textInput);\n\n const lockedHint = document.createElement('div');\n lockedHint.style.cssText = 'color:#f78166;font-size:11px;margin-top:6px;display:none;';\n lockedHint.textContent = t('drawing.props.locked', '已鎖定 — 解鎖後可編輯');\n\n panel.append(title, typeEl, colorRow, widthRow, textRow, lockedHint);\n parent.appendChild(panel);\n\n const emit = () => {\n opts.onStyleChange?.({\n color: colorInput.value,\n lineWidth: Number(widthInput.value),\n text: textInput.value,\n });\n };\n colorInput.oninput = emit;\n widthInput.oninput = emit;\n textInput.oninput = emit;\n\n const bind = (drawing: DrawingRecord | null) => {\n if (!drawing) {\n panel.style.display = 'none';\n return;\n }\n panel.style.display = 'block';\n typeEl.textContent = `${t('drawing.props.type', '類型')}: ${drawing.type}`;\n const meta = drawing.meta ?? {};\n colorInput.value = String(meta.color ?? '#58a6ff');\n widthInput.value = String(meta.lineWidth ?? 2);\n textInput.value = String(meta.text ?? 'Note');\n textRow.style.display = drawing.type === 'text' ? 'flex' : 'none';\n const locked = Boolean(meta.locked);\n lockedHint.style.display = locked ? 'block' : 'none';\n colorInput.disabled = locked;\n widthInput.disabled = locked;\n textInput.disabled = locked;\n };\n\n return { el: panel, bind };\n}","/** Host for MACD / RSI / KDJ indicator panes (PR-11). */\nexport function mountIndicatorPaneHost(parent: HTMLElement): HTMLElement {\n const host = document.createElement('div');\n host.dataset.tradviewIndicatorHost = '1';\n host.style.cssText =\n 'display:flex;flex-direction:column;flex:2;min-height:120px;border-top:1px solid #30363d;background:#0d1117;overflow:hidden;';\n parent.appendChild(host);\n return host;\n}","import type { ChartLayoutOptions } from './chart-layout.js';\n\nexport interface LayoutFeatures {\n showTopBar?: boolean;\n showLeftToolbar?: boolean;\n showBottomToolbar?: boolean;\n showCrosshairLegend?: boolean;\n showStatusBar?: boolean;\n showPropertiesPanel?: boolean;\n showContextMenu?: boolean;\n showSettings?: boolean;\n showShortcuts?: boolean;\n /** When TopBar is on and no search API: manual symbol input (default). */\n symbolInput?: 'manual' | 'search' | 'none';\n}\n\nexport interface ResolvedLayoutFeatures {\n showTopBar: boolean;\n showLeftToolbar: boolean;\n showBottomToolbar: boolean;\n showCrosshairLegend: boolean;\n showStatusBar: boolean;\n showPropertiesPanel: boolean;\n showContextMenu: boolean;\n showSettings: boolean;\n showShortcuts: boolean;\n symbolInput: 'manual' | 'search' | 'none';\n}\n\nexport const DEFAULT_LAYOUT_FEATURES: ResolvedLayoutFeatures = {\n showTopBar: false,\n showLeftToolbar: false,\n showBottomToolbar: false,\n showCrosshairLegend: false,\n showStatusBar: false,\n showPropertiesPanel: false,\n showContextMenu: false,\n showSettings: false,\n showShortcuts: false,\n symbolInput: 'manual',\n};\n\nexport function resolveLayoutFeatures(\n opts: ChartLayoutOptions = {},\n): ResolvedLayoutFeatures {\n const d = DEFAULT_LAYOUT_FEATURES;\n const top = opts.showTopBar ?? d.showTopBar;\n return {\n showTopBar: top,\n showLeftToolbar: opts.showLeftToolbar ?? d.showLeftToolbar,\n showBottomToolbar: opts.showBottomToolbar ?? d.showBottomToolbar,\n showCrosshairLegend: opts.showCrosshairLegend ?? d.showCrosshairLegend,\n showStatusBar: opts.showStatusBar ?? d.showStatusBar,\n showPropertiesPanel: opts.showPropertiesPanel ?? d.showPropertiesPanel,\n showContextMenu: opts.showContextMenu ?? d.showContextMenu,\n showSettings: opts.settings !== undefined ? (opts.showSettings ?? true) : (opts.showSettings ?? d.showSettings),\n showShortcuts: opts.showShortcuts ?? d.showShortcuts,\n symbolInput: opts.symbolInput ?? (opts.onSymbolSearch ? 'search' : d.symbolInput),\n };\n}\n\nexport function mergeLayoutFeatures(\n current: ResolvedLayoutFeatures,\n patch: LayoutFeatures,\n): ResolvedLayoutFeatures {\n return resolveLayoutFeatures({\n showTopBar: patch.showTopBar ?? current.showTopBar,\n showLeftToolbar: patch.showLeftToolbar ?? current.showLeftToolbar,\n showBottomToolbar: patch.showBottomToolbar ?? current.showBottomToolbar,\n showCrosshairLegend: patch.showCrosshairLegend ?? current.showCrosshairLegend,\n showStatusBar: patch.showStatusBar ?? current.showStatusBar,\n showPropertiesPanel: patch.showPropertiesPanel ?? current.showPropertiesPanel,\n showContextMenu: patch.showContextMenu ?? current.showContextMenu,\n showSettings: patch.showSettings ?? current.showSettings,\n showShortcuts: patch.showShortcuts ?? current.showShortcuts,\n symbolInput: patch.symbolInput ?? current.symbolInput,\n });\n}\n\n/** Playground: enable full TV shell. */\nexport function createDemoLayoutOptions(\n partial: ChartLayoutOptions = {},\n): ChartLayoutOptions {\n return {\n ...partial,\n showTopBar: partial.showTopBar ?? true,\n showLeftToolbar: partial.showLeftToolbar ?? true,\n showCrosshairLegend: partial.showCrosshairLegend ?? true,\n showStatusBar: partial.showStatusBar ?? true,\n showPropertiesPanel: partial.showPropertiesPanel ?? true,\n showContextMenu: partial.showContextMenu ?? true,\n showSettings: partial.showSettings ?? true,\n showShortcuts: partial.showShortcuts ?? true,\n showBottomToolbar: partial.showBottomToolbar ?? true,\n symbolInput: partial.symbolInput ?? (partial.onSymbolSearch ? 'search' : 'manual'),\n };\n}","import { getLocale, setLocale, t } from '@coderyo/i18n';\n\nexport interface OhlcvSnapshot {\n o?: number;\n h?: number;\n l?: number;\n c?: number;\n v?: number;\n}\n\nexport interface StatusBarOptions {\n connection?: string;\n symbol?: string;\n interval?: string;\n ohlcv?: OhlcvSnapshot | null;\n locale?: string;\n onLocaleChange?: (locale: string) => void;\n}\n\nfunction fmt(n: number | undefined, digits = 2): string {\n if (n == null || Number.isNaN(n)) return '—';\n return n.toLocaleString(undefined, { maximumFractionDigits: digits });\n}\n\nexport function mountStatusBar(parent: HTMLElement, opts: StatusBarOptions = {}): {\n el: HTMLElement;\n update: (patch: StatusBarOptions) => void;\n} {\n const bar = document.createElement('div');\n bar.className = 'tv-statusbar';\n bar.style.cssText =\n 'display:flex;align-items:center;gap:14px;padding:6px 12px;font-size:11px;color:#8b949e;border-top:1px solid #30363d;background:#161b22;flex-shrink:0;';\n\n const conn = document.createElement('span');\n const sym = document.createElement('span');\n const ohlcv = document.createElement('span');\n ohlcv.style.flex = '1';\n ohlcv.style.color = '#e6edf3';\n\n const localeWrap = document.createElement('label');\n localeWrap.style.cssText = 'display:flex;align-items:center;gap:4px;margin-left:auto;';\n const localeLabel = document.createElement('span');\n localeLabel.textContent = t('status.locale', '語系');\n const localeSelect = document.createElement('select');\n localeSelect.style.cssText =\n 'background:#0d1117;color:#e6edf3;border:1px solid #30363d;border-radius:4px;font-size:11px;padding:2px 4px;';\n for (const loc of ['zh-TW', 'en']) {\n const opt = document.createElement('option');\n opt.value = loc;\n opt.textContent = loc;\n localeSelect.appendChild(opt);\n }\n localeSelect.value = opts.locale ?? getLocale();\n localeSelect.onchange = () => {\n setLocale(localeSelect.value);\n opts.onLocaleChange?.(localeSelect.value);\n localeLabel.textContent = t('status.locale', '語系');\n render(opts);\n };\n localeWrap.append(localeLabel, localeSelect);\n\n bar.append(conn, sym, ohlcv, localeWrap);\n parent.appendChild(bar);\n\n const render = (state: StatusBarOptions) => {\n const merged = { ...opts, ...state };\n conn.textContent = `${t('status.connection', '連線')}:${merged.connection ?? '—'}`;\n const parts = [merged.symbol, merged.interval].filter(Boolean);\n sym.textContent = parts.length ? parts.join(' · ') : '';\n const o = merged.ohlcv;\n if (o && (o.o != null || o.c != null)) {\n ohlcv.textContent = `O ${fmt(o.o)} H ${fmt(o.h)} L ${fmt(o.l)} C ${fmt(o.c)} V ${fmt(o.v, 0)}`;\n } else {\n ohlcv.textContent = t('status.ohlcvHint', '移動十字線查看 OHLCV');\n }\n };\n\n render(opts);\n return {\n el: bar,\n update: (patch) => {\n Object.assign(opts, patch);\n render(opts);\n },\n };\n}","import { t } from '@coderyo/i18n';\n\nconst SHORTCUTS: Array<{ key: string; desc: string }> = [\n { key: '↖ / Esc', desc: '游標(選取/編輯繪圖)' },\n { key: 'Delete', desc: '刪除選中繪圖' },\n { key: 'R', desc: '適配畫面' },\n { key: 'End', desc: '跳到最新 K 線' },\n { key: 'F', desc: '全螢幕' },\n { key: 'S', desc: '截圖 PNG' },\n { key: 'L', desc: '對數價格軸' },\n { key: 'T', desc: '切換主題' },\n { key: '?', desc: '本說明' },\n];\n\nexport function bindShortcutsModal(): () => void {\n const handler = (e: KeyboardEvent) => {\n if (e.key !== '?' || e.ctrlKey || e.metaKey) return;\n const tag = (e.target as HTMLElement)?.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n e.preventDefault();\n openShortcutsModal();\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n}\n\nexport function openShortcutsModal(): void {\n const backdrop = document.createElement('div');\n backdrop.style.cssText =\n 'position:fixed;inset:0;z-index:100;background:#01040999;display:flex;align-items:center;justify-content:center;';\n\n const box = document.createElement('div');\n box.style.cssText =\n 'min-width:320px;max-width:90vw;padding:16px 20px;background:#161b22;border:1px solid #30363d;border-radius:8px;color:#e6edf3;';\n\n const h = document.createElement('h2');\n h.textContent = t('shortcuts.title', '快捷鍵');\n h.style.cssText = 'font-size:16px;margin:0 0 12px;';\n\n const list = document.createElement('div');\n list.style.cssText = 'font-size:13px;line-height:1.8;';\n for (const s of SHORTCUTS) {\n const row = document.createElement('div');\n row.innerHTML = `<kbd style=\"background:#21262d;padding:2px 6px;border-radius:4px;margin-right:8px;\">${s.key}</kbd>${s.desc}`;\n list.appendChild(row);\n }\n\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.textContent = t('shortcuts.close', '關閉');\n closeBtn.style.cssText =\n 'margin-top:14px;padding:6px 14px;background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;cursor:pointer;';\n const close = () => backdrop.remove();\n closeBtn.onclick = close;\n backdrop.onclick = (e) => {\n if (e.target === backdrop) close();\n };\n\n box.append(h, list, closeBtn);\n backdrop.appendChild(box);\n document.body.appendChild(backdrop);\n}","import { attachChartContextMenu, type ContextMenuAction } from './context-menu.js';\nimport { mountCrosshairLegend } from './crosshair-legend.js';\nimport { mountDrawingPropertiesPanel } from './drawing-properties-panel.js';\nimport { mountIndicatorPaneHost } from './indicator-pane-host.js';\nimport {\n mergeLayoutFeatures,\n resolveLayoutFeatures,\n type LayoutFeatures,\n type ResolvedLayoutFeatures,\n} from './layout-features.js';\nimport { mountStatusBar, type StatusBarOptions } from './status-bar.js';\nimport { mountTopBar, type TopBarOptions } from './top-bar.js';\nimport type { SettingsPanelOptions } from './settings-panel.js';\nimport { bindShortcutsModal } from './shortcuts-modal.js';\n\nexport type { LayoutFeatures, ResolvedLayoutFeatures } from './layout-features.js';\nexport { resolveLayoutFeatures, DEFAULT_LAYOUT_FEATURES, createDemoLayoutOptions } from './layout-features.js';\n\nexport type DrawingToolId =\n | 'cursor'\n | 'trendline'\n | 'hline'\n | 'vline'\n | 'rectangle'\n | 'fibonacci'\n | 'text';\n\nconst DRAWING_TOOLS: Array<{ id: DrawingToolId; label: string }> = [\n { id: 'cursor', label: '↖' },\n { id: 'trendline', label: '╱' },\n { id: 'hline', label: '─' },\n { id: 'vline', label: '│' },\n { id: 'rectangle', label: '▭' },\n { id: 'fibonacci', label: 'φ' },\n { id: 'text', label: 'T' },\n];\n\nconst MOBILE_MQ = '(max-width: 768px)';\n\nexport interface ChartLayoutOptions extends TopBarOptions {\n showTopBar?: boolean;\n showLeftToolbar?: boolean;\n showBottomToolbar?: boolean;\n activeDrawingTool?: DrawingToolId;\n onDrawingToolSelect?: (tool: DrawingToolId) => void;\n statusBar?: StatusBarOptions;\n contextMenuActions?: ContextMenuAction[];\n settings?: SettingsPanelOptions;\n showStatusBar?: boolean;\n showCrosshairLegend?: boolean;\n showPropertiesPanel?: boolean;\n showContextMenu?: boolean;\n showSettings?: boolean;\n showShortcuts?: boolean;\n symbolInput?: 'manual' | 'search' | 'none';\n onDrawingStyleChange?: (patch: { color?: string; lineWidth?: number; text?: string }) => void;\n onDrawingSelectionBind?: (bind: (drawing: import('@coderyo/drawings').DrawingRecord | null) => void) => void;\n}\n\nfunction mountToolButtons(\n parent: HTMLElement,\n activeTool: DrawingToolId,\n onSelect: (tool: DrawingToolId) => void,\n layout: 'column' | 'row',\n): { setActive: (tool: DrawingToolId) => void } {\n const btnStyle =\n layout === 'column'\n ? 'width:36px;height:32px;background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;cursor:pointer;font-size:14px;'\n : 'min-width:40px;height:36px;background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;cursor:pointer;font-size:14px;padding:0 8px;';\n const activeStyle = btnStyle.replace('#21262d', '#388bfd').replace('#e6edf3', '#fff');\n\n const buttons = new Map<DrawingToolId, HTMLButtonElement>();\n for (const tool of DRAWING_TOOLS) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.title = tool.id;\n btn.textContent = tool.label;\n btn.style.cssText = activeTool === tool.id ? activeStyle : btnStyle;\n btn.onclick = () => onSelect(tool.id);\n buttons.set(tool.id, btn);\n parent.appendChild(btn);\n }\n\n return {\n setActive: (tool) => {\n for (const [id, btn] of buttons) {\n btn.style.cssText = id === tool ? activeStyle : btnStyle;\n }\n },\n };\n}\n\nexport function mountChartLayout(root: HTMLElement, opts: ChartLayoutOptions = {}): {\n chartHost: HTMLElement;\n indicatorHost: HTMLElement;\n topBar: HTMLElement;\n statusBar: ReturnType<typeof mountStatusBar>;\n crosshairLegend: ReturnType<typeof mountCrosshairLegend>;\n detachContextMenu: () => void;\n setActiveDrawingTool: (tool: DrawingToolId) => void;\n propertiesPanel: ReturnType<typeof mountDrawingPropertiesPanel>;\n setLayoutFeatures: (patch: LayoutFeatures) => void;\n getLayoutFeatures: () => ResolvedLayoutFeatures;\n} {\n let layoutFeatures = resolveLayoutFeatures(opts);\n\n root.style.display = 'flex';\n root.style.flexDirection = 'column';\n root.style.height = '100%';\n root.style.background = '#0d1117';\n\n let activeTool: DrawingToolId = opts.activeDrawingTool ?? 'cursor';\n let setActiveDesktop: ((t: DrawingToolId) => void) | null = null;\n let setActiveMobile: ((t: DrawingToolId) => void) | null = null;\n\n const setActiveDrawingTool = (tool: DrawingToolId) => {\n activeTool = tool;\n setActiveDesktop?.(tool);\n setActiveMobile?.(tool);\n };\n\n const onToolSelect = (tool: DrawingToolId) => {\n setActiveDrawingTool(tool);\n opts.onDrawingToolSelect?.(tool);\n };\n\n const body = document.createElement('div');\n body.style.cssText = 'display:flex;flex:1;min-height:0;';\n\n const leftAside = document.createElement('aside');\n leftAside.style.cssText =\n '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;z-index:20;';\n\n const bottomBar = document.createElement('div');\n bottomBar.style.cssText =\n 'display:none;flex-shrink:0;gap:6px;padding:6px 8px;border-top:1px solid #30363d;background:#161b22;overflow-x:auto;';\n\n const chartColumn = document.createElement('div');\n chartColumn.style.cssText = 'display:flex;flex-direction:column;flex:1;min-height:0;';\n\n const chartHost = document.createElement('div');\n chartHost.style.cssText = 'flex:1;min-height:0;width:100%;height:100%;position:relative;overflow:hidden;';\n chartColumn.appendChild(chartHost);\n\n const indicatorHost = mountIndicatorPaneHost(chartColumn);\n\n const statusBar = mountStatusBar(chartColumn, opts.statusBar ?? {});\n\n body.appendChild(chartColumn);\n\n const propertiesPanel = mountDrawingPropertiesPanel(body, {\n onStyleChange: opts.onDrawingStyleChange,\n });\n\n opts.onDrawingSelectionBind?.(propertiesPanel.bind);\n\n root.appendChild(body);\n root.appendChild(bottomBar);\n\n let topBar: HTMLElement = document.createElement('div');\n topBar.style.display = 'none';\n root.insertBefore(topBar, body);\n\n const crosshairLegend = mountCrosshairLegend(chartHost, { symbol: opts.initialSymbol });\n\n let detachContextMenu = () => {};\n let shortcutsBound = false;\n\n const mountLeftToolbar = () => {\n if (leftAside.parentElement) return;\n const desktopTools = mountToolButtons(leftAside, activeTool, onToolSelect, 'column');\n setActiveDesktop = desktopTools.setActive;\n body.insertBefore(leftAside, chartColumn);\n\n bottomBar.style.flexDirection = 'row';\n const mobileTools = mountToolButtons(bottomBar, activeTool, onToolSelect, 'row');\n setActiveMobile = mobileTools.setActive;\n\n const mq = window.matchMedia(MOBILE_MQ);\n const applyLayout = () => {\n const mobile = mq.matches;\n leftAside.style.display = mobile ? 'none' : 'flex';\n const showBottom = layoutFeatures.showBottomToolbar !== false;\n bottomBar.style.display = mobile && showBottom ? 'flex' : 'none';\n };\n mq.addEventListener('change', applyLayout);\n applyLayout();\n };\n\n const unmountLeftToolbar = () => {\n leftAside.remove();\n bottomBar.innerHTML = '';\n setActiveDesktop = null;\n setActiveMobile = null;\n };\n\n const applyLayoutFeatures = () => {\n const f = layoutFeatures;\n\n if (f.showTopBar) {\n topBar.remove();\n topBar = mountTopBar(root, {\n ...opts,\n symbolInput: f.symbolInput,\n showSettings: f.showSettings,\n });\n } else {\n topBar.style.display = 'none';\n }\n\n if (f.showLeftToolbar) mountLeftToolbar();\n else unmountLeftToolbar();\n\n crosshairLegend.el.style.display = f.showCrosshairLegend ? '' : 'none';\n statusBar.el.style.display = f.showStatusBar ? '' : 'none';\n propertiesPanel.el.style.display = f.showPropertiesPanel ? '' : 'none';\n\n detachContextMenu();\n if (f.showContextMenu) {\n detachContextMenu = attachChartContextMenu(chartHost, {\n actions: opts.contextMenuActions,\n });\n }\n\n if (f.showShortcuts && !shortcutsBound) {\n bindShortcutsModal();\n shortcutsBound = true;\n }\n };\n\n applyLayoutFeatures();\n\n return {\n chartHost,\n indicatorHost,\n topBar,\n statusBar,\n crosshairLegend,\n detachContextMenu,\n setActiveDrawingTool,\n propertiesPanel,\n setLayoutFeatures: (patch) => {\n layoutFeatures = mergeLayoutFeatures(layoutFeatures, patch);\n applyLayoutFeatures();\n },\n getLayoutFeatures: () => ({ ...layoutFeatures }),\n };\n}","import type { DrawingRecord } from '@coderyo/drawings';\nimport { t } from '@coderyo/i18n';\n\nexport interface DrawingContextMenuHandlers {\n onDelete?: () => void;\n onCopy?: () => void;\n onToggleLock?: () => void;\n onDeselect?: () => void;\n onEditText?: () => void;\n}\n\nexport function openDrawingContextMenu(\n clientX: number,\n clientY: number,\n drawing: DrawingRecord | null,\n handlers: DrawingContextMenuHandlers,\n): () => void {\n const menu = document.createElement('div');\n menu.style.cssText =\n '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;';\n menu.style.left = `${clientX}px`;\n menu.style.top = `${clientY}px`;\n\n const add = (label: string, fn?: () => void, disabled = false) => {\n if (!fn) return;\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = label;\n btn.disabled = disabled;\n btn.style.cssText =\n 'display:block;width:100%;text-align:left;padding:8px 12px;border:none;background:transparent;color:#e6edf3;font-size:12px;cursor:pointer;';\n if (disabled) btn.style.opacity = '0.45';\n btn.onclick = () => {\n fn();\n close();\n };\n menu.appendChild(btn);\n };\n\n if (drawing) {\n const locked = Boolean(drawing.meta?.locked);\n add(t('drawing.ctx.delete', '刪除'), handlers.onDelete);\n add(t('drawing.ctx.copy', '複製'), handlers.onCopy);\n add(\n locked ? t('drawing.ctx.unlock', '解鎖') : t('drawing.ctx.lock', '鎖定'),\n handlers.onToggleLock,\n );\n if (drawing.type === 'text') {\n add(t('drawing.ctx.editText', '編輯文字'), handlers.onEditText);\n }\n add(t('drawing.ctx.deselect', '取消選取'), handlers.onDeselect);\n }\n\n document.body.appendChild(menu);\n\n const close = () => {\n menu.remove();\n document.removeEventListener('click', close);\n };\n setTimeout(() => document.addEventListener('click', close), 0);\n return close;\n}","export function mountCodeSnippetPanel(parent: HTMLElement, getCode: () => string): HTMLElement {\n const wrap = document.createElement('details');\n wrap.style.cssText =\n 'flex-shrink:0;border-top:1px solid #30363d;background:#161b22;padding:6px 12px;font-size:11px;color:#8b949e;';\n\n const summary = document.createElement('summary');\n summary.textContent = '嵌入程式碼(整合方)';\n summary.style.cssText = 'cursor:pointer;color:#58a6ff;user-select:none;';\n\n const pre = document.createElement('pre');\n pre.style.cssText =\n 'margin:8px 0 0;padding:10px;background:#0d1117;border:1px solid #30363d;border-radius:6px;color:#e6edf3;font-size:11px;overflow:auto;max-height:160px;white-space:pre-wrap;';\n\n const copyBtn = document.createElement('button');\n copyBtn.type = 'button';\n copyBtn.textContent = '複製';\n copyBtn.style.cssText =\n 'margin-top:6px;padding:4px 10px;background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;cursor:pointer;font-size:11px;';\n copyBtn.onclick = () => {\n const code = getCode();\n pre.textContent = code;\n void navigator.clipboard.writeText(code);\n };\n\n wrap.ontoggle = () => {\n if (wrap.open) pre.textContent = getCode();\n };\n\n wrap.append(summary, pre, copyBtn);\n parent.appendChild(wrap);\n return wrap;\n}"],"mappings":"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","names":["t","DEFAULT_INDICATOR_CONFIG","DEFAULT_INDICATOR_CONFIG","t","t","t","fmt","t","t","t","t"]}
1
+ {"version":3,"sources":["../src/top-bar.ts","../src/user-preferences.ts","../src/settings-panel.ts","../src/symbol-search.ts","../src/context-menu.ts","../src/crosshair-legend.ts","../src/drawing-properties-panel.ts","../src/indicator-pane-host.ts","../src/layout-features.ts","../src/status-bar.ts","../src/shortcuts-modal.ts","../src/chart-layout.ts","../src/drawing-context-menu.ts","../src/code-snippet-panel.ts","../src/pine-editor-panel.ts","../src/pine-language.ts"],"sourcesContent":["import { DEFAULT_INTERVALS, type Interval, type SymbolSearchHit } from '@coderyo/data';\nimport { t } from '@coderyo/i18n';\nimport { mountSettingsMenu, type SettingsMenuOptions } from './settings-menu.js';\nimport { mountSymbolSearch } from './symbol-search.js';\n\nexport type SymbolInputMode = 'manual' | 'search' | 'none';\n\nexport interface TopBarOptions {\n intervals?: Interval[];\n /** Highlighted interval button (defaults to first in `intervals`). */\n activeInterval?: Interval;\n initialSymbol?: string;\n onSymbolSearch?: (query: string) => Promise<SymbolSearchHit[]>;\n onSymbolSelect?: (symbol: string) => void;\n onIntervalChange?: (interval: Interval) => void;\n onThemeToggle?: () => void;\n onFullscreen?: () => void;\n onScreenshot?: () => void;\n settings?: SettingsMenuOptions;\n symbolInput?: SymbolInputMode;\n showSettings?: boolean;\n}\n\nfunction mountManualSymbolInput(\n parent: HTMLElement,\n opts: { initialSymbol?: string; onSymbolSelect?: (symbol: string) => void },\n): void {\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display:flex;align-items:center;gap:4px;margin-right:8px;';\n const input = document.createElement('input');\n input.type = 'text';\n input.placeholder = t('symbol.search', 'Symbol');\n input.value = opts.initialSymbol ?? '';\n input.style.cssText =\n 'width:140px;background:#0d1117;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 8px;font-size:12px;';\n const apply = () => {\n const v = input.value.trim();\n if (v) opts.onSymbolSelect?.(v);\n };\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') apply();\n });\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = '↵';\n btn.title = 'Apply symbol';\n btn.style.cssText =\n 'background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 8px;cursor:pointer;font-size:12px;';\n btn.onclick = apply;\n wrap.append(input, btn);\n parent.appendChild(wrap);\n}\n\nexport function mountTopBar(\n parent: HTMLElement,\n opts: TopBarOptions = {},\n): { el: HTMLElement; setActiveInterval: (interval: Interval) => void } {\n const bar = document.createElement('div');\n bar.className = 'tv-topbar';\n bar.style.cssText =\n 'display:flex;gap:8px;padding:8px 12px;align-items:center;border-bottom:1px solid #30363d;background:#161b22;';\n\n const symbolMode = opts.symbolInput ?? 'manual';\n if (symbolMode === 'search' && opts.onSymbolSearch && opts.onSymbolSelect) {\n mountSymbolSearch(bar, {\n initialSymbol: opts.initialSymbol,\n onSearch: opts.onSymbolSearch,\n onSelect: opts.onSymbolSelect,\n });\n } else if (symbolMode === 'manual' && opts.onSymbolSelect) {\n mountManualSymbolInput(bar, {\n initialSymbol: opts.initialSymbol,\n onSymbolSelect: opts.onSymbolSelect,\n });\n }\n\n const intervals = opts.intervals ?? DEFAULT_INTERVALS;\n const btnStyle =\n 'background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;';\n const btnActiveStyle = btnStyle.replace('#21262d', '#388bfd').replace('#e6edf3', '#fff');\n const intervalButtons = new Map<Interval, HTMLButtonElement>();\n let activeInterval = opts.activeInterval ?? intervals[0];\n\n const paintIntervalButtons = () => {\n for (const [iv, btn] of intervalButtons) {\n btn.style.cssText = iv === activeInterval ? btnActiveStyle : btnStyle;\n }\n };\n\n for (const iv of intervals) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = t(`interval.${iv}`, iv);\n btn.style.cssText = btnStyle;\n btn.onclick = () => {\n if (activeInterval === iv) return;\n activeInterval = iv;\n paintIntervalButtons();\n opts.onIntervalChange?.(iv);\n };\n intervalButtons.set(iv, btn);\n bar.appendChild(btn);\n }\n paintIntervalButtons();\n\n const spacer = document.createElement('div');\n spacer.style.flex = '1';\n bar.appendChild(spacer);\n\n const mkBtn = (label: string, fn?: () => void) => {\n const b = document.createElement('button');\n b.type = 'button';\n b.textContent = label;\n b.style.cssText =\n 'background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;';\n b.onclick = () => fn?.();\n return b;\n };\n\n bar.appendChild(mkBtn(t('theme.dark', '主題'), opts.onThemeToggle));\n if (opts.showSettings && opts.settings) mountSettingsMenu(bar, opts.settings);\n bar.appendChild(mkBtn('⛶', opts.onFullscreen));\n bar.appendChild(mkBtn('📷', opts.onScreenshot));\n\n parent.prepend(bar);\n return {\n el: bar,\n setActiveInterval: (interval) => {\n if (!intervalButtons.has(interval)) return;\n activeInterval = interval;\n paintIntervalButtons();\n },\n };\n}","import type { IndicatorConfig } from '@coderyo/indicators';\nimport { DEFAULT_INDICATOR_CONFIG, indicatorConfigStorageKey } from '@coderyo/indicators';\n\nexport const GRID_SETTING_KEY = 'tradview:settings:showGrid';\nexport const RETURN_CURSOR_KEY = 'tradview:settings:returnToCursorAfterDraw';\n\nexport function loadShowGridPreference(): boolean {\n try {\n return localStorage.getItem(GRID_SETTING_KEY) === '1';\n } catch {\n return false;\n }\n}\n\nexport function saveShowGridPreference(show: boolean): void {\n try {\n localStorage.setItem(GRID_SETTING_KEY, show ? '1' : '0');\n } catch {\n /* ignore */\n }\n}\n\nexport function loadReturnToCursorPreference(): boolean {\n try {\n return localStorage.getItem(RETURN_CURSOR_KEY) === '1';\n } catch {\n return false;\n }\n}\n\nexport function saveReturnToCursorPreference(v: boolean): void {\n try {\n localStorage.setItem(RETURN_CURSOR_KEY, v ? '1' : '0');\n } catch {\n /* ignore */\n }\n}\n\nexport function loadIndicatorConfig(symbol: string, interval: string): IndicatorConfig {\n try {\n const raw = localStorage.getItem(indicatorConfigStorageKey(symbol, interval));\n if (!raw) return { ...DEFAULT_INDICATOR_CONFIG };\n return { ...DEFAULT_INDICATOR_CONFIG, ...JSON.parse(raw) };\n } catch {\n return { ...DEFAULT_INDICATOR_CONFIG };\n }\n}\n\nexport function saveIndicatorConfig(\n symbol: string,\n interval: string,\n config: IndicatorConfig,\n): void {\n try {\n localStorage.setItem(indicatorConfigStorageKey(symbol, interval), JSON.stringify(config));\n } catch {\n /* ignore */\n }\n}","import { DEFAULT_INDICATOR_CONFIG, type IndicatorConfig } from '@coderyo/indicators';\nimport { t } from '@coderyo/i18n';\nimport {\n loadReturnToCursorPreference,\n loadShowGridPreference,\n saveReturnToCursorPreference,\n saveShowGridPreference,\n} from './user-preferences.js';\n\nexport interface SettingsPanelOptions {\n showGrid?: boolean;\n onShowGridChange?: (show: boolean) => void;\n returnToCursorAfterDraw?: boolean;\n onReturnToCursorChange?: (v: boolean) => void;\n indicatorConfig?: IndicatorConfig;\n onIndicatorConfigChange?: (config: IndicatorConfig) => void;\n}\n\nexport function mountSettingsPanel(parent: HTMLElement, opts: SettingsPanelOptions = {}): HTMLElement {\n let open = false;\n let tab: 'chart' | 'drawing' | 'indicator' = 'chart';\n let showGrid = opts.showGrid ?? loadShowGridPreference();\n let returnToCursor = opts.returnToCursorAfterDraw ?? loadReturnToCursorPreference();\n let indicatorConfig = { ...(opts.indicatorConfig ?? DEFAULT_INDICATOR_CONFIG) };\n\n const wrap = document.createElement('div');\n wrap.style.cssText = 'position:relative;';\n\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.title = t('settings.title', '設定');\n btn.textContent = '⚙';\n btn.style.cssText =\n 'background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:14px;';\n\n const panel = document.createElement('div');\n panel.style.cssText =\n 'display:none;position:absolute;right:0;top:100%;margin-top:4px;width:280px;max-height:70vh;overflow:auto;padding:0;background:#161b22;border:1px solid #30363d;border-radius:6px;box-shadow:0 8px 24px #01040988;z-index:30;';\n\n const tabs = document.createElement('div');\n tabs.style.cssText = 'display:flex;border-bottom:1px solid #30363d;';\n const content = document.createElement('div');\n content.style.cssText = 'padding:10px 12px;font-size:12px;';\n\n const tabIds = [\n ['chart', t('settings.tab.chart', '圖表')],\n ['drawing', t('settings.tab.drawing', '繪圖')],\n ['indicator', t('settings.tab.indicator', '指標')],\n ] as const;\n\n const renderTabs = () => {\n tabs.replaceChildren();\n for (const [id, label] of tabIds) {\n const b = document.createElement('button');\n b.type = 'button';\n b.textContent = label;\n b.style.cssText = `flex:1;padding:8px;border:none;cursor:pointer;font-size:12px;${\n tab === id ? 'background:#21262d;color:#e6edf3;' : 'background:transparent;color:#8b949e;'\n }`;\n b.onclick = (e) => {\n e.stopPropagation();\n tab = id;\n renderTabs();\n renderContent();\n };\n tabs.appendChild(b);\n }\n };\n\n const checkbox = (label: string, checked: boolean, onChange: (v: boolean) => void) => {\n const row = document.createElement('label');\n row.style.cssText =\n 'display:flex;align-items:center;gap:8px;margin-bottom:8px;cursor:pointer;color:#e6edf3;';\n const input = document.createElement('input');\n input.type = 'checkbox';\n input.checked = checked;\n input.onchange = () => onChange(input.checked);\n row.append(input, document.createTextNode(label));\n return row;\n };\n\n const numberField = (label: string, value: number, onChange: (n: number) => void) => {\n const row = document.createElement('label');\n row.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;';\n row.innerHTML = `<span style=\"color:#8b949e\">${label}</span>`;\n const input = document.createElement('input');\n input.type = 'number';\n input.value = String(value);\n input.style.cssText =\n 'width:72px;padding:2px 6px;border-radius:4px;border:1px solid #30363d;background:#0d1117;color:#e6edf3;';\n input.onchange = () => onChange(Number(input.value) || value);\n row.appendChild(input);\n return row;\n };\n\n const renderContent = () => {\n content.replaceChildren();\n if (tab === 'chart') {\n content.appendChild(\n checkbox(t('settings.showGrid', '顯示網格'), showGrid, (v) => {\n showGrid = v;\n saveShowGridPreference(v);\n opts.onShowGridChange?.(v);\n }),\n );\n } else if (tab === 'drawing') {\n content.appendChild(\n checkbox(t('settings.returnCursor', '畫完切回游標'), returnToCursor, (v) => {\n returnToCursor = v;\n saveReturnToCursorPreference(v);\n opts.onReturnToCursorChange?.(v);\n }),\n );\n } else {\n content.appendChild(\n checkbox(t('settings.ind.macd', 'MACD 窗格'), indicatorConfig.showMacd, (v) => {\n indicatorConfig = { ...indicatorConfig, showMacd: v };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n checkbox(t('settings.ind.rsi', 'RSI 窗格'), indicatorConfig.showRsi, (v) => {\n indicatorConfig = { ...indicatorConfig, showRsi: v };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n checkbox(t('settings.ind.kdj', 'KDJ 窗格'), indicatorConfig.showKdj, (v) => {\n indicatorConfig = { ...indicatorConfig, showKdj: v };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n const src = document.createElement('label');\n src.style.cssText = 'display:flex;justify-content:space-between;margin:8px 0 6px;';\n src.innerHTML = `<span style=\"color:#8b949e\">${t('settings.ind.source', '源')}</span>`;\n const sel = document.createElement('select');\n sel.style.cssText =\n 'background:#0d1117;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:2px 6px;';\n for (const v of ['close', 'hlc3'] as const) {\n const o = document.createElement('option');\n o.value = v;\n o.textContent = v;\n sel.appendChild(o);\n }\n sel.value = indicatorConfig.source;\n sel.onchange = () => {\n indicatorConfig = { ...indicatorConfig, source: sel.value as IndicatorConfig['source'] };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n };\n src.appendChild(sel);\n content.appendChild(src);\n content.appendChild(\n checkbox(t('settings.ind.ema', 'EMA 疊加'), indicatorConfig.showEma, (v) => {\n indicatorConfig = { ...indicatorConfig, showEma: v };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('EMA', indicatorConfig.emaPeriod, (n) => {\n indicatorConfig = { ...indicatorConfig, emaPeriod: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n checkbox(t('settings.ind.boll', 'BOLL 通道'), indicatorConfig.showBoll, (v) => {\n indicatorConfig = { ...indicatorConfig, showBoll: v };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('BOLL', indicatorConfig.bollPeriod, (n) => {\n indicatorConfig = { ...indicatorConfig, bollPeriod: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('MA', indicatorConfig.maPeriod, (n) => {\n indicatorConfig = { ...indicatorConfig, maPeriod: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('MACD fast', indicatorConfig.macdFast, (n) => {\n indicatorConfig = { ...indicatorConfig, macdFast: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('MACD slow', indicatorConfig.macdSlow, (n) => {\n indicatorConfig = { ...indicatorConfig, macdSlow: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('RSI', indicatorConfig.rsiPeriod, (n) => {\n indicatorConfig = { ...indicatorConfig, rsiPeriod: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n content.appendChild(\n numberField('KDJ', indicatorConfig.kdjPeriod, (n) => {\n indicatorConfig = { ...indicatorConfig, kdjPeriod: n };\n opts.onIndicatorConfigChange?.(indicatorConfig);\n }),\n );\n }\n };\n\n renderTabs();\n renderContent();\n panel.append(tabs, content);\n\n btn.onclick = (e) => {\n e.stopPropagation();\n open = !open;\n panel.style.display = open ? 'block' : 'none';\n };\n\n const close = () => {\n open = false;\n panel.style.display = 'none';\n };\n document.addEventListener('click', close);\n panel.onclick = (e) => e.stopPropagation();\n\n wrap.append(btn, panel);\n parent.appendChild(wrap);\n return wrap;\n}","import type { SymbolSearchHit } from '@coderyo/data';\nimport { t } from '@coderyo/i18n';\n\nexport interface SymbolSearchOptions {\n onSearch: (query: string) => Promise<SymbolSearchHit[]>;\n onSelect: (symbol: string) => void;\n initialSymbol?: string;\n}\n\nexport function mountSymbolSearch(parent: HTMLElement, opts: SymbolSearchOptions): HTMLElement {\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display:flex;align-items:center;gap:6px;margin-right:8px;';\n\n const input = document.createElement('input');\n input.type = 'search';\n input.placeholder = t('symbol.search', '搜尋商品');\n input.value = opts.initialSymbol ?? '';\n input.style.cssText =\n 'width:140px;padding:4px 8px;border-radius:4px;border:1px solid #30363d;background:#0d1117;color:#e6edf3;font-size:12px;';\n\n const list = document.createElement('div');\n list.style.cssText =\n 'display:none;position:absolute;top:100%;left:0;z-index:20;min-width:200px;max-height:200px;overflow:auto;background:#161b22;border:1px solid #30363d;border-radius:4px;';\n\n const box = document.createElement('div');\n box.style.position = 'relative';\n box.append(input, list);\n wrap.appendChild(box);\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const renderHits = (hits: SymbolSearchHit[]) => {\n list.replaceChildren();\n if (hits.length === 0) {\n list.style.display = 'none';\n return;\n }\n for (const hit of hits) {\n const row = document.createElement('button');\n row.type = 'button';\n row.textContent = hit.exchange ? `${hit.symbol} · ${hit.exchange}` : hit.symbol;\n row.style.cssText =\n 'display:block;width:100%;text-align:left;padding:6px 10px;border:none;background:transparent;color:#e6edf3;cursor:pointer;font-size:12px;';\n row.onmouseenter = () => {\n row.style.background = '#21262d';\n };\n row.onmouseleave = () => {\n row.style.background = 'transparent';\n };\n row.onclick = () => {\n input.value = hit.symbol;\n list.style.display = 'none';\n opts.onSelect(hit.symbol);\n };\n list.appendChild(row);\n }\n list.style.display = 'block';\n };\n\n input.addEventListener('input', () => {\n clearTimeout(timer);\n const q = input.value.trim();\n if (q.length < 1) {\n list.style.display = 'none';\n return;\n }\n timer = setTimeout(() => {\n void opts.onSearch(q).then(renderHits).catch(() => {\n list.style.display = 'none';\n });\n }, 200);\n });\n\n document.addEventListener('click', (e) => {\n if (!box.contains(e.target as Node)) list.style.display = 'none';\n });\n\n parent.appendChild(wrap);\n return wrap;\n}","import { t } from '@coderyo/i18n';\n\nexport interface ContextMenuAction {\n id: string;\n label: string;\n onClick: () => void;\n}\n\nexport interface ContextMenuOptions {\n actions?: ContextMenuAction[];\n}\n\nexport function attachChartContextMenu(\n chartHost: HTMLElement,\n opts: ContextMenuOptions = {},\n): () => void {\n let menu: HTMLDivElement | null = null;\n\n const close = () => {\n menu?.remove();\n menu = null;\n };\n\n const open = (x: number, y: number) => {\n close();\n menu = document.createElement('div');\n menu.style.cssText =\n 'position:fixed;z-index:50;min-width:160px;padding:4px 0;background:#161b22;border:1px solid #30363d;border-radius:6px;box-shadow:0 8px 24px #01040988;';\n\n const actions: ContextMenuAction[] = opts.actions ?? [\n {\n id: 'fit',\n label: t('context.fitContent', '適配畫面'),\n onClick: () => {},\n },\n ];\n\n for (const action of actions) {\n const row = document.createElement('button');\n row.type = 'button';\n row.textContent = action.label;\n row.style.cssText =\n 'display:block;width:100%;text-align:left;padding:8px 12px;border:none;background:transparent;color:#e6edf3;font-size:12px;cursor:pointer;';\n row.onmouseenter = () => {\n row.style.background = '#21262d';\n };\n row.onmouseleave = () => {\n row.style.background = 'transparent';\n };\n row.onclick = () => {\n action.onClick();\n close();\n };\n menu.appendChild(row);\n }\n\n menu.style.left = `${x}px`;\n menu.style.top = `${y}px`;\n document.body.appendChild(menu);\n };\n\n const onContext = (e: MouseEvent) => {\n e.preventDefault();\n open(e.clientX, e.clientY);\n };\n\n chartHost.addEventListener('contextmenu', onContext);\n document.addEventListener('click', close);\n document.addEventListener('scroll', close, true);\n\n return () => {\n chartHost.removeEventListener('contextmenu', onContext);\n document.removeEventListener('click', close);\n document.removeEventListener('scroll', close, true);\n close();\n };\n}","import type { OhlcvSnapshot } from './status-bar.js';\n\nexport interface CrosshairLegendOptions {\n symbol?: string;\n interval?: string;\n}\n\nexport function mountCrosshairLegend(\n chartHost: HTMLElement,\n opts: CrosshairLegendOptions = {},\n): {\n el: HTMLElement;\n update: (payload: { time?: number; ohlcv?: OhlcvSnapshot | null }) => void;\n setMeta: (meta: CrosshairLegendOptions) => void;\n hide: () => void;\n} {\n const box = document.createElement('div');\n box.className = 'tv-crosshair-legend';\n box.style.cssText =\n 'display:none;position:absolute;top:8px;left:8px;z-index:10;padding:6px 10px;border-radius:6px;background:#161b22e6;border:1px solid #30363d;font-size:11px;color:#e6edf3;pointer-events:none;line-height:1.5;';\n\n const title = document.createElement('div');\n title.style.cssText = 'color:#8b949e;margin-bottom:2px;';\n const body = document.createElement('div');\n box.append(title, body);\n chartHost.appendChild(box);\n\n const fmt = (n: number | undefined) =>\n n == null ? '—' : n.toLocaleString(undefined, { maximumFractionDigits: 2 });\n\n const render = (payload: { time?: number; ohlcv?: OhlcvSnapshot | null }) => {\n const parts = [opts.symbol, opts.interval].filter(Boolean);\n title.textContent = parts.length ? parts.join(' · ') : '';\n const o = payload.ohlcv;\n if (!o?.c && o?.o == null) {\n box.style.display = 'none';\n return;\n }\n const timeStr = payload.time != null ? new Date(payload.time).toLocaleString() : '';\n body.textContent = `${timeStr}\\nO ${fmt(o?.o)} H ${fmt(o?.h)} L ${fmt(o?.l)} C ${fmt(o?.c)}`;\n box.style.display = 'block';\n };\n\n return {\n el: box,\n update: render,\n setMeta: (meta) => {\n Object.assign(opts, meta);\n title.textContent = [opts.symbol, opts.interval].filter(Boolean).join(' · ');\n },\n hide: () => {\n box.style.display = 'none';\n },\n };\n}","import type { DrawingRecord } from '@coderyo/drawings';\nimport { t } from '@coderyo/i18n';\n\nexport interface DrawingPropertiesPanelOptions {\n onStyleChange?: (patch: { color?: string; lineWidth?: number; text?: string }) => void;\n}\n\nexport function mountDrawingPropertiesPanel(\n parent: HTMLElement,\n opts: DrawingPropertiesPanelOptions = {},\n): { el: HTMLElement; bind: (drawing: DrawingRecord | null) => void } {\n const panel = document.createElement('aside');\n panel.className = 'tv-drawing-props';\n panel.style.cssText =\n 'display:none;width:220px;flex-shrink:0;border-left:1px solid #30363d;background:#161b22;padding:10px 12px;font-size:12px;color:#e6edf3;overflow:auto;';\n\n const title = document.createElement('div');\n title.textContent = t('drawing.props.title', '繪圖屬性');\n title.style.cssText = 'font-weight:600;margin-bottom:10px;';\n\n const typeEl = document.createElement('div');\n typeEl.style.color = '#8b949e';\n typeEl.style.marginBottom = '10px';\n\n const mkRow = (label: string) => {\n const row = document.createElement('label');\n row.style.cssText = 'display:flex;flex-direction:column;gap:4px;margin-bottom:8px;';\n const span = document.createElement('span');\n span.textContent = label;\n span.style.color = '#8b949e';\n row.appendChild(span);\n return row;\n };\n\n const colorRow = mkRow(t('drawing.props.color', '顏色'));\n const colorInput = document.createElement('input');\n colorInput.type = 'color';\n colorInput.style.cssText = 'width:100%;height:28px;border:none;background:transparent;cursor:pointer;';\n colorRow.appendChild(colorInput);\n\n const widthRow = mkRow(t('drawing.props.lineWidth', '線寬'));\n const widthInput = document.createElement('input');\n widthInput.type = 'range';\n widthInput.min = '1';\n widthInput.max = '6';\n widthInput.style.width = '100%';\n widthRow.appendChild(widthInput);\n\n const textRow = mkRow(t('drawing.props.text', '文字'));\n const textInput = document.createElement('input');\n textInput.type = 'text';\n textInput.style.cssText =\n 'padding:4px 8px;border-radius:4px;border:1px solid #30363d;background:#0d1117;color:#e6edf3;';\n textRow.appendChild(textInput);\n\n const lockedHint = document.createElement('div');\n lockedHint.style.cssText = 'color:#f78166;font-size:11px;margin-top:6px;display:none;';\n lockedHint.textContent = t('drawing.props.locked', '已鎖定 — 解鎖後可編輯');\n\n panel.append(title, typeEl, colorRow, widthRow, textRow, lockedHint);\n parent.appendChild(panel);\n\n const emit = () => {\n opts.onStyleChange?.({\n color: colorInput.value,\n lineWidth: Number(widthInput.value),\n text: textInput.value,\n });\n };\n colorInput.oninput = emit;\n widthInput.oninput = emit;\n textInput.oninput = emit;\n\n const bind = (drawing: DrawingRecord | null) => {\n if (!drawing) {\n panel.style.display = 'none';\n return;\n }\n panel.style.display = 'block';\n typeEl.textContent = `${t('drawing.props.type', '類型')}: ${drawing.type}`;\n const meta = drawing.meta ?? {};\n colorInput.value = String(meta.color ?? '#58a6ff');\n widthInput.value = String(meta.lineWidth ?? 2);\n textInput.value = String(meta.text ?? 'Note');\n textRow.style.display = drawing.type === 'text' ? 'flex' : 'none';\n const locked = Boolean(meta.locked);\n lockedHint.style.display = locked ? 'block' : 'none';\n colorInput.disabled = locked;\n widthInput.disabled = locked;\n textInput.disabled = locked;\n };\n\n return { el: panel, bind };\n}","/** Host for MACD / RSI / KDJ indicator panes (PR-11). */\nexport function mountIndicatorPaneHost(parent: HTMLElement): HTMLElement {\n const host = document.createElement('div');\n host.dataset.tradviewIndicatorHost = '1';\n host.style.cssText =\n 'display:flex;flex-direction:column;flex:2;min-height:120px;border-top:1px solid #30363d;background:#0d1117;overflow:hidden;';\n parent.appendChild(host);\n return host;\n}","import type { ChartLayoutOptions } from './chart-layout.js';\n\nexport interface LayoutFeatures {\n showTopBar?: boolean;\n showLeftToolbar?: boolean;\n showBottomToolbar?: boolean;\n showCrosshairLegend?: boolean;\n showStatusBar?: boolean;\n showPropertiesPanel?: boolean;\n showContextMenu?: boolean;\n showSettings?: boolean;\n showShortcuts?: boolean;\n /** When TopBar is on and no search API: manual symbol input (default). */\n symbolInput?: 'manual' | 'search' | 'none';\n}\n\nexport interface ResolvedLayoutFeatures {\n showTopBar: boolean;\n showLeftToolbar: boolean;\n showBottomToolbar: boolean;\n showCrosshairLegend: boolean;\n showStatusBar: boolean;\n showPropertiesPanel: boolean;\n showContextMenu: boolean;\n showSettings: boolean;\n showShortcuts: boolean;\n symbolInput: 'manual' | 'search' | 'none';\n}\n\nexport const DEFAULT_LAYOUT_FEATURES: ResolvedLayoutFeatures = {\n showTopBar: false,\n showLeftToolbar: false,\n showBottomToolbar: false,\n showCrosshairLegend: false,\n showStatusBar: false,\n showPropertiesPanel: false,\n showContextMenu: false,\n showSettings: false,\n showShortcuts: false,\n symbolInput: 'manual',\n};\n\nexport function resolveLayoutFeatures(\n opts: ChartLayoutOptions = {},\n): ResolvedLayoutFeatures {\n const d = DEFAULT_LAYOUT_FEATURES;\n const top = opts.showTopBar ?? d.showTopBar;\n return {\n showTopBar: top,\n showLeftToolbar: opts.showLeftToolbar ?? d.showLeftToolbar,\n showBottomToolbar: opts.showBottomToolbar ?? d.showBottomToolbar,\n showCrosshairLegend: opts.showCrosshairLegend ?? d.showCrosshairLegend,\n showStatusBar: opts.showStatusBar ?? d.showStatusBar,\n showPropertiesPanel: opts.showPropertiesPanel ?? d.showPropertiesPanel,\n showContextMenu: opts.showContextMenu ?? d.showContextMenu,\n showSettings: opts.settings !== undefined ? (opts.showSettings ?? true) : (opts.showSettings ?? d.showSettings),\n showShortcuts: opts.showShortcuts ?? d.showShortcuts,\n symbolInput: opts.symbolInput ?? (opts.onSymbolSearch ? 'search' : d.symbolInput),\n };\n}\n\nexport function mergeLayoutFeatures(\n current: ResolvedLayoutFeatures,\n patch: LayoutFeatures,\n): ResolvedLayoutFeatures {\n return resolveLayoutFeatures({\n showTopBar: patch.showTopBar ?? current.showTopBar,\n showLeftToolbar: patch.showLeftToolbar ?? current.showLeftToolbar,\n showBottomToolbar: patch.showBottomToolbar ?? current.showBottomToolbar,\n showCrosshairLegend: patch.showCrosshairLegend ?? current.showCrosshairLegend,\n showStatusBar: patch.showStatusBar ?? current.showStatusBar,\n showPropertiesPanel: patch.showPropertiesPanel ?? current.showPropertiesPanel,\n showContextMenu: patch.showContextMenu ?? current.showContextMenu,\n showSettings: patch.showSettings ?? current.showSettings,\n showShortcuts: patch.showShortcuts ?? current.showShortcuts,\n symbolInput: patch.symbolInput ?? current.symbolInput,\n });\n}\n\n/** Playground: enable full TV shell. */\nexport function createDemoLayoutOptions(\n partial: ChartLayoutOptions = {},\n): ChartLayoutOptions {\n return {\n ...partial,\n showTopBar: partial.showTopBar ?? true,\n showLeftToolbar: partial.showLeftToolbar ?? true,\n showCrosshairLegend: partial.showCrosshairLegend ?? true,\n showStatusBar: partial.showStatusBar ?? true,\n showPropertiesPanel: partial.showPropertiesPanel ?? true,\n showContextMenu: partial.showContextMenu ?? true,\n showSettings: partial.showSettings ?? true,\n showShortcuts: partial.showShortcuts ?? true,\n showBottomToolbar: partial.showBottomToolbar ?? true,\n symbolInput: partial.symbolInput ?? (partial.onSymbolSearch ? 'search' : 'manual'),\n };\n}","import { getLocale, setLocale, t } from '@coderyo/i18n';\n\nexport interface OhlcvSnapshot {\n o?: number;\n h?: number;\n l?: number;\n c?: number;\n v?: number;\n}\n\nexport interface StatusBarOptions {\n connection?: string;\n symbol?: string;\n interval?: string;\n ohlcv?: OhlcvSnapshot | null;\n locale?: string;\n onLocaleChange?: (locale: string) => void;\n}\n\nfunction fmt(n: number | undefined, digits = 2): string {\n if (n == null || Number.isNaN(n)) return '—';\n return n.toLocaleString(undefined, { maximumFractionDigits: digits });\n}\n\nexport function mountStatusBar(parent: HTMLElement, opts: StatusBarOptions = {}): {\n el: HTMLElement;\n update: (patch: StatusBarOptions) => void;\n} {\n const bar = document.createElement('div');\n bar.className = 'tv-statusbar';\n bar.style.cssText =\n 'display:flex;align-items:center;gap:14px;padding:6px 12px;font-size:11px;color:#8b949e;border-top:1px solid #30363d;background:#161b22;flex-shrink:0;';\n\n const conn = document.createElement('span');\n const sym = document.createElement('span');\n const ohlcv = document.createElement('span');\n ohlcv.style.flex = '1';\n ohlcv.style.color = '#e6edf3';\n\n const localeWrap = document.createElement('label');\n localeWrap.style.cssText = 'display:flex;align-items:center;gap:4px;margin-left:auto;';\n const localeLabel = document.createElement('span');\n localeLabel.textContent = t('status.locale', '語系');\n const localeSelect = document.createElement('select');\n localeSelect.style.cssText =\n 'background:#0d1117;color:#e6edf3;border:1px solid #30363d;border-radius:4px;font-size:11px;padding:2px 4px;';\n for (const loc of ['zh-TW', 'en']) {\n const opt = document.createElement('option');\n opt.value = loc;\n opt.textContent = loc;\n localeSelect.appendChild(opt);\n }\n localeSelect.value = opts.locale ?? getLocale();\n localeSelect.onchange = () => {\n setLocale(localeSelect.value);\n opts.onLocaleChange?.(localeSelect.value);\n localeLabel.textContent = t('status.locale', '語系');\n render(opts);\n };\n localeWrap.append(localeLabel, localeSelect);\n\n bar.append(conn, sym, ohlcv, localeWrap);\n parent.appendChild(bar);\n\n const render = (state: StatusBarOptions) => {\n const merged = { ...opts, ...state };\n conn.textContent = `${t('status.connection', '連線')}:${merged.connection ?? '—'}`;\n const parts = [merged.symbol, merged.interval].filter(Boolean);\n sym.textContent = parts.length ? parts.join(' · ') : '';\n const o = merged.ohlcv;\n if (o && (o.o != null || o.c != null)) {\n ohlcv.textContent = `O ${fmt(o.o)} H ${fmt(o.h)} L ${fmt(o.l)} C ${fmt(o.c)} V ${fmt(o.v, 0)}`;\n } else {\n ohlcv.textContent = t('status.ohlcvHint', '移動十字線查看 OHLCV');\n }\n };\n\n render(opts);\n return {\n el: bar,\n update: (patch) => {\n Object.assign(opts, patch);\n render(opts);\n },\n };\n}","import { t } from '@coderyo/i18n';\n\nconst SHORTCUTS: Array<{ key: string; desc: string }> = [\n { key: '↖ / Esc', desc: '游標(選取/編輯繪圖)' },\n { key: 'Delete', desc: '刪除選中繪圖' },\n { key: 'R', desc: '適配畫面' },\n { key: 'End', desc: '跳到最新 K 線' },\n { key: 'F', desc: '全螢幕' },\n { key: 'S', desc: '截圖 PNG' },\n { key: 'L', desc: '對數價格軸' },\n { key: 'T', desc: '切換主題' },\n { key: '?', desc: '本說明' },\n];\n\nexport function bindShortcutsModal(): () => void {\n const handler = (e: KeyboardEvent) => {\n if (e.key !== '?' || e.ctrlKey || e.metaKey) return;\n const tag = (e.target as HTMLElement)?.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n e.preventDefault();\n openShortcutsModal();\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n}\n\nexport function openShortcutsModal(): void {\n const backdrop = document.createElement('div');\n backdrop.style.cssText =\n 'position:fixed;inset:0;z-index:100;background:#01040999;display:flex;align-items:center;justify-content:center;';\n\n const box = document.createElement('div');\n box.style.cssText =\n 'min-width:320px;max-width:90vw;padding:16px 20px;background:#161b22;border:1px solid #30363d;border-radius:8px;color:#e6edf3;';\n\n const h = document.createElement('h2');\n h.textContent = t('shortcuts.title', '快捷鍵');\n h.style.cssText = 'font-size:16px;margin:0 0 12px;';\n\n const list = document.createElement('div');\n list.style.cssText = 'font-size:13px;line-height:1.8;';\n for (const s of SHORTCUTS) {\n const row = document.createElement('div');\n row.innerHTML = `<kbd style=\"background:#21262d;padding:2px 6px;border-radius:4px;margin-right:8px;\">${s.key}</kbd>${s.desc}`;\n list.appendChild(row);\n }\n\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.textContent = t('shortcuts.close', '關閉');\n closeBtn.style.cssText =\n 'margin-top:14px;padding:6px 14px;background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;cursor:pointer;';\n const close = () => backdrop.remove();\n closeBtn.onclick = close;\n backdrop.onclick = (e) => {\n if (e.target === backdrop) close();\n };\n\n box.append(h, list, closeBtn);\n backdrop.appendChild(box);\n document.body.appendChild(backdrop);\n}","import { attachChartContextMenu, type ContextMenuAction } from './context-menu.js';\nimport { mountCrosshairLegend } from './crosshair-legend.js';\nimport { mountDrawingPropertiesPanel } from './drawing-properties-panel.js';\nimport { mountIndicatorPaneHost } from './indicator-pane-host.js';\nimport {\n mergeLayoutFeatures,\n resolveLayoutFeatures,\n type LayoutFeatures,\n type ResolvedLayoutFeatures,\n} from './layout-features.js';\nimport { mountStatusBar, type StatusBarOptions } from './status-bar.js';\nimport { mountTopBar, type TopBarOptions } from './top-bar.js';\nimport type { SettingsPanelOptions } from './settings-panel.js';\nimport { bindShortcutsModal } from './shortcuts-modal.js';\n\nexport type { LayoutFeatures, ResolvedLayoutFeatures } from './layout-features.js';\nexport { resolveLayoutFeatures, DEFAULT_LAYOUT_FEATURES, createDemoLayoutOptions } from './layout-features.js';\n\nexport type DrawingToolId =\n | 'cursor'\n | 'trendline'\n | 'hline'\n | 'vline'\n | 'rectangle'\n | 'fibonacci'\n | 'text';\n\nconst DRAWING_TOOLS: Array<{ id: DrawingToolId; label: string }> = [\n { id: 'cursor', label: '↖' },\n { id: 'trendline', label: '╱' },\n { id: 'hline', label: '─' },\n { id: 'vline', label: '│' },\n { id: 'rectangle', label: '▭' },\n { id: 'fibonacci', label: 'φ' },\n { id: 'text', label: 'T' },\n];\n\nconst MOBILE_MQ = '(max-width: 768px)';\n\nexport interface ChartLayoutOptions extends TopBarOptions {\n showTopBar?: boolean;\n showLeftToolbar?: boolean;\n showBottomToolbar?: boolean;\n activeDrawingTool?: DrawingToolId;\n onDrawingToolSelect?: (tool: DrawingToolId) => void;\n statusBar?: StatusBarOptions;\n contextMenuActions?: ContextMenuAction[];\n settings?: SettingsPanelOptions;\n showStatusBar?: boolean;\n showCrosshairLegend?: boolean;\n showPropertiesPanel?: boolean;\n showContextMenu?: boolean;\n showSettings?: boolean;\n showShortcuts?: boolean;\n symbolInput?: 'manual' | 'search' | 'none';\n onDrawingStyleChange?: (patch: { color?: string; lineWidth?: number; text?: string }) => void;\n onDrawingSelectionBind?: (bind: (drawing: import('@coderyo/drawings').DrawingRecord | null) => void) => void;\n}\n\nfunction mountToolButtons(\n parent: HTMLElement,\n activeTool: DrawingToolId,\n onSelect: (tool: DrawingToolId) => void,\n layout: 'column' | 'row',\n): { setActive: (tool: DrawingToolId) => void } {\n const btnStyle =\n layout === 'column'\n ? 'width:36px;height:32px;background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;cursor:pointer;font-size:14px;'\n : 'min-width:40px;height:36px;background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;cursor:pointer;font-size:14px;padding:0 8px;';\n const activeStyle = btnStyle.replace('#21262d', '#388bfd').replace('#e6edf3', '#fff');\n\n const buttons = new Map<DrawingToolId, HTMLButtonElement>();\n for (const tool of DRAWING_TOOLS) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.title = tool.id;\n btn.textContent = tool.label;\n btn.style.cssText = activeTool === tool.id ? activeStyle : btnStyle;\n btn.onclick = () => onSelect(tool.id);\n buttons.set(tool.id, btn);\n parent.appendChild(btn);\n }\n\n return {\n setActive: (tool) => {\n for (const [id, btn] of buttons) {\n btn.style.cssText = id === tool ? activeStyle : btnStyle;\n }\n },\n };\n}\n\nexport function mountChartLayout(root: HTMLElement, opts: ChartLayoutOptions = {}): {\n chartHost: HTMLElement;\n indicatorHost: HTMLElement;\n topBar: HTMLElement;\n setActiveInterval: (interval: import('@coderyo/data').Interval) => void;\n statusBar: ReturnType<typeof mountStatusBar>;\n crosshairLegend: ReturnType<typeof mountCrosshairLegend>;\n detachContextMenu: () => void;\n setActiveDrawingTool: (tool: DrawingToolId) => void;\n propertiesPanel: ReturnType<typeof mountDrawingPropertiesPanel>;\n setLayoutFeatures: (patch: LayoutFeatures) => void;\n getLayoutFeatures: () => ResolvedLayoutFeatures;\n} {\n let layoutFeatures = resolveLayoutFeatures(opts);\n\n root.style.display = 'flex';\n root.style.flexDirection = 'column';\n root.style.height = '100%';\n root.style.background = '#0d1117';\n\n let activeTool: DrawingToolId = opts.activeDrawingTool ?? 'cursor';\n let setActiveDesktop: ((t: DrawingToolId) => void) | null = null;\n let setActiveMobile: ((t: DrawingToolId) => void) | null = null;\n\n const setActiveDrawingTool = (tool: DrawingToolId) => {\n activeTool = tool;\n setActiveDesktop?.(tool);\n setActiveMobile?.(tool);\n };\n\n const onToolSelect = (tool: DrawingToolId) => {\n setActiveDrawingTool(tool);\n opts.onDrawingToolSelect?.(tool);\n };\n\n const body = document.createElement('div');\n body.style.cssText = 'display:flex;flex:1;min-height:0;';\n\n const leftAside = document.createElement('aside');\n leftAside.style.cssText =\n '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;z-index:20;';\n\n const bottomBar = document.createElement('div');\n bottomBar.style.cssText =\n 'display:none;flex-shrink:0;gap:6px;padding:6px 8px;border-top:1px solid #30363d;background:#161b22;overflow-x:auto;';\n\n const chartColumn = document.createElement('div');\n chartColumn.style.cssText = 'display:flex;flex-direction:column;flex:1;min-height:0;';\n\n const chartHost = document.createElement('div');\n chartHost.style.cssText = 'flex:1;min-height:0;width:100%;height:100%;position:relative;overflow:hidden;';\n chartColumn.appendChild(chartHost);\n\n const indicatorHost = mountIndicatorPaneHost(chartColumn);\n\n const statusBar = mountStatusBar(chartColumn, opts.statusBar ?? {});\n\n body.appendChild(chartColumn);\n\n const propertiesPanel = mountDrawingPropertiesPanel(body, {\n onStyleChange: opts.onDrawingStyleChange,\n });\n\n opts.onDrawingSelectionBind?.(propertiesPanel.bind);\n\n root.appendChild(body);\n root.appendChild(bottomBar);\n\n let topBar: HTMLElement = document.createElement('div');\n topBar.style.display = 'none';\n root.insertBefore(topBar, body);\n let setActiveInterval: (interval: import('@coderyo/data').Interval) => void = () => {};\n\n const crosshairLegend = mountCrosshairLegend(chartHost, { symbol: opts.initialSymbol });\n\n let detachContextMenu = () => {};\n let shortcutsBound = false;\n\n const mountLeftToolbar = () => {\n if (leftAside.parentElement) return;\n const desktopTools = mountToolButtons(leftAside, activeTool, onToolSelect, 'column');\n setActiveDesktop = desktopTools.setActive;\n body.insertBefore(leftAside, chartColumn);\n\n bottomBar.style.flexDirection = 'row';\n const mobileTools = mountToolButtons(bottomBar, activeTool, onToolSelect, 'row');\n setActiveMobile = mobileTools.setActive;\n\n const mq = window.matchMedia(MOBILE_MQ);\n const applyLayout = () => {\n const mobile = mq.matches;\n leftAside.style.display = mobile ? 'none' : 'flex';\n const showBottom = layoutFeatures.showBottomToolbar !== false;\n bottomBar.style.display = mobile && showBottom ? 'flex' : 'none';\n };\n mq.addEventListener('change', applyLayout);\n applyLayout();\n };\n\n const unmountLeftToolbar = () => {\n leftAside.remove();\n bottomBar.innerHTML = '';\n setActiveDesktop = null;\n setActiveMobile = null;\n };\n\n const applyLayoutFeatures = () => {\n const f = layoutFeatures;\n\n if (f.showTopBar) {\n topBar.remove();\n // Mutate opts in place so late-assigned callbacks (e.g. onIntervalChange) stay wired.\n const mounted = mountTopBar(\n root,\n Object.assign(opts, {\n symbolInput: f.symbolInput,\n showSettings: f.showSettings,\n }),\n );\n topBar = mounted.el;\n setActiveInterval = mounted.setActiveInterval;\n } else {\n topBar.style.display = 'none';\n }\n\n if (f.showLeftToolbar) mountLeftToolbar();\n else unmountLeftToolbar();\n\n crosshairLegend.el.style.display = f.showCrosshairLegend ? '' : 'none';\n statusBar.el.style.display = f.showStatusBar ? '' : 'none';\n propertiesPanel.el.style.display = f.showPropertiesPanel ? '' : 'none';\n\n detachContextMenu();\n if (f.showContextMenu) {\n detachContextMenu = attachChartContextMenu(chartHost, {\n actions: opts.contextMenuActions,\n });\n }\n\n if (f.showShortcuts && !shortcutsBound) {\n bindShortcutsModal();\n shortcutsBound = true;\n }\n };\n\n applyLayoutFeatures();\n\n return {\n chartHost,\n indicatorHost,\n topBar,\n setActiveInterval,\n statusBar,\n crosshairLegend,\n detachContextMenu,\n setActiveDrawingTool,\n propertiesPanel,\n setLayoutFeatures: (patch) => {\n layoutFeatures = mergeLayoutFeatures(layoutFeatures, patch);\n applyLayoutFeatures();\n },\n getLayoutFeatures: () => ({ ...layoutFeatures }),\n };\n}","import type { DrawingRecord } from '@coderyo/drawings';\nimport { t } from '@coderyo/i18n';\n\nexport interface DrawingContextMenuHandlers {\n onDelete?: () => void;\n onCopy?: () => void;\n onToggleLock?: () => void;\n onDeselect?: () => void;\n onEditText?: () => void;\n}\n\nexport function openDrawingContextMenu(\n clientX: number,\n clientY: number,\n drawing: DrawingRecord | null,\n handlers: DrawingContextMenuHandlers,\n): () => void {\n const menu = document.createElement('div');\n menu.style.cssText =\n '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;';\n menu.style.left = `${clientX}px`;\n menu.style.top = `${clientY}px`;\n\n const add = (label: string, fn?: () => void, disabled = false) => {\n if (!fn) return;\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = label;\n btn.disabled = disabled;\n btn.style.cssText =\n 'display:block;width:100%;text-align:left;padding:8px 12px;border:none;background:transparent;color:#e6edf3;font-size:12px;cursor:pointer;';\n if (disabled) btn.style.opacity = '0.45';\n btn.onclick = () => {\n fn();\n close();\n };\n menu.appendChild(btn);\n };\n\n if (drawing) {\n const locked = Boolean(drawing.meta?.locked);\n add(t('drawing.ctx.delete', '刪除'), handlers.onDelete);\n add(t('drawing.ctx.copy', '複製'), handlers.onCopy);\n add(\n locked ? t('drawing.ctx.unlock', '解鎖') : t('drawing.ctx.lock', '鎖定'),\n handlers.onToggleLock,\n );\n if (drawing.type === 'text') {\n add(t('drawing.ctx.editText', '編輯文字'), handlers.onEditText);\n }\n add(t('drawing.ctx.deselect', '取消選取'), handlers.onDeselect);\n }\n\n document.body.appendChild(menu);\n\n const close = () => {\n menu.remove();\n document.removeEventListener('click', close);\n };\n setTimeout(() => document.addEventListener('click', close), 0);\n return close;\n}","export function mountCodeSnippetPanel(parent: HTMLElement, getCode: () => string): HTMLElement {\n const wrap = document.createElement('details');\n wrap.style.cssText =\n 'flex-shrink:0;border-top:1px solid #30363d;background:#161b22;padding:6px 12px;font-size:11px;color:#8b949e;';\n\n const summary = document.createElement('summary');\n summary.textContent = '嵌入程式碼(整合方)';\n summary.style.cssText = 'cursor:pointer;color:#58a6ff;user-select:none;';\n\n const pre = document.createElement('pre');\n pre.style.cssText =\n 'margin:8px 0 0;padding:10px;background:#0d1117;border:1px solid #30363d;border-radius:6px;color:#e6edf3;font-size:11px;overflow:auto;max-height:160px;white-space:pre-wrap;';\n\n const copyBtn = document.createElement('button');\n copyBtn.type = 'button';\n copyBtn.textContent = '複製';\n copyBtn.style.cssText =\n 'margin-top:6px;padding:4px 10px;background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;cursor:pointer;font-size:11px;';\n copyBtn.onclick = () => {\n const code = getCode();\n pre.textContent = code;\n void navigator.clipboard.writeText(code);\n };\n\n wrap.ontoggle = () => {\n if (wrap.open) pre.textContent = getCode();\n };\n\n wrap.append(summary, pre, copyBtn);\n parent.appendChild(wrap);\n return wrap;\n}","import { compilePineLite } from '@coderyo/pine-lite';\n\ninterface PineDiagnostic {\n line: number;\n col: number;\n message: string;\n severity: 'error' | 'warning';\n endCol?: number;\n}\nimport { defaultKeymap, history, historyKeymap } from '@codemirror/commands';\nimport { syntaxHighlighting, defaultHighlightStyle, bracketMatching } from '@codemirror/language';\nimport { linter, type Diagnostic as CmDiagnostic } from '@codemirror/lint';\nimport { EditorState, type Extension } from '@codemirror/state';\nimport {\n EditorView,\n drawSelection,\n highlightActiveLine,\n highlightActiveLineGutter,\n keymap,\n lineNumbers,\n placeholder,\n} from '@codemirror/view';\nimport { t } from '@coderyo/i18n';\nimport { pineLanguage } from './pine-language.js';\n\nexport const PINE_SCRIPT_STORAGE_KEY = 'tradview:pine:script';\n\nexport function loadPineScriptPreference(): string | null {\n try {\n return localStorage.getItem(PINE_SCRIPT_STORAGE_KEY);\n } catch {\n return null;\n }\n}\n\nexport function savePineScriptPreference(script: string): void {\n try {\n localStorage.setItem(PINE_SCRIPT_STORAGE_KEY, script);\n } catch {\n /* ignore */\n }\n}\n\nfunction offsetAtLineCol(doc: string, line: number, col: number): number {\n const lines = doc.split('\\n');\n let off = 0;\n for (let i = 0; i < line - 1 && i < lines.length; i++) {\n off += lines[i]!.length + 1;\n }\n return off + Math.max(0, col - 1);\n}\n\nfunction pineDiagnosticsToCm(doc: string, diags: PineDiagnostic[]): CmDiagnostic[] {\n return diags.map((d) => {\n const from = offsetAtLineCol(doc, d.line, d.col);\n const to = d.endCol != null ? offsetAtLineCol(doc, d.line, d.endCol) : from + 1;\n return {\n from,\n to: Math.max(from + 1, to),\n severity: d.severity,\n message: d.message,\n };\n });\n}\n\nfunction createPineLinter(onStatus?: (msg: string, ok: boolean) => void) {\n return linter((view) => {\n const src = view.state.doc.toString();\n const result = compilePineLite(src);\n if (result.ok) {\n onStatus?.(t('pine.status.ok', '語法正確'), true);\n return [];\n }\n onStatus?.(result.errors[0] ?? t('pine.status.error', '語法錯誤'), false);\n return pineDiagnosticsToCm(src, result.diagnostics ?? []);\n });\n}\n\nconst darkTheme = EditorView.theme({\n '&': {\n backgroundColor: '#0d1117',\n color: '#e6edf3',\n fontSize: '12px',\n },\n '.cm-content': { caretColor: '#58a6ff', fontFamily: 'Consolas, \"Cascadia Code\", monospace' },\n '.cm-gutters': {\n backgroundColor: '#161b22',\n color: '#8b949e',\n borderRight: '1px solid #30363d',\n },\n '.cm-activeLine': { backgroundColor: '#161b22aa' },\n '.cm-activeLineGutter': { backgroundColor: '#21262d' },\n '.cm-selectionBackground, &.cm-focused .cm-selectionBackground': { backgroundColor: '#264f78' },\n '.cm-cursor': { borderLeftColor: '#58a6ff' },\n '.cm-lintRange-error': { backgroundImage: 'none', borderBottom: '2px wavy #f85149' },\n '.cm-lintRange-warning': { backgroundImage: 'none', borderBottom: '2px wavy #d29922' },\n});\n\nexport interface PineEditorPanelOptions {\n initialScript?: string;\n /** Debounced apply to chart (ms). Default 400. */\n debounceMs?: number;\n onApply?: (script: string, compileOk: boolean) => void;\n}\n\nexport function mountPineEditorPanel(\n parent: HTMLElement,\n opts: PineEditorPanelOptions = {},\n): {\n el: HTMLElement;\n getScript: () => string;\n setScript: (script: string) => void;\n applyNow: () => void;\n destroy: () => void;\n} {\n const wrap = document.createElement('details');\n wrap.className = 'tv-pine-editor';\n wrap.open = true;\n wrap.style.cssText =\n 'flex-shrink:0;border-top:1px solid #30363d;background:#161b22;max-height:220px;display:flex;flex-direction:column;';\n\n const summary = document.createElement('summary');\n summary.textContent = t('pine.editor.title', 'Pine 腳本編輯器');\n summary.style.cssText =\n 'cursor:pointer;color:#58a6ff;user-select:none;padding:6px 12px;font-size:12px;flex-shrink:0;';\n\n const toolbar = document.createElement('div');\n toolbar.style.cssText =\n 'display:flex;align-items:center;gap:8px;padding:0 12px 6px;flex-shrink:0;';\n\n const status = document.createElement('span');\n status.style.cssText = 'font-size:11px;color:#8b949e;flex:1;';\n status.textContent = t('pine.status.idle', '就緒');\n\n const applyBtn = document.createElement('button');\n applyBtn.type = 'button';\n applyBtn.textContent = t('pine.apply', '套用至圖表');\n applyBtn.style.cssText =\n 'padding:4px 10px;background:#238636;color:#fff;border:1px solid #2ea043;border-radius:4px;cursor:pointer;font-size:11px;';\n\n const host = document.createElement('div');\n host.style.cssText = 'flex:1;min-height:120px;max-height:160px;overflow:hidden;border-top:1px solid #30363d;';\n\n toolbar.append(status, applyBtn);\n wrap.append(summary, toolbar, host);\n parent.appendChild(wrap);\n\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n const debounceMs = opts.debounceMs ?? 400;\n\n const runApply = (src: string) => {\n const r = compilePineLite(src);\n opts.onApply?.(src, r.ok);\n savePineScriptPreference(src);\n if (r.ok) {\n status.style.color = '#3fb950';\n status.textContent = t('pine.status.ok', '語法正確');\n } else {\n status.style.color = '#f85149';\n status.textContent = r.errors[0] ?? t('pine.status.error', '語法錯誤');\n }\n };\n\n const extensions: Extension[] = [\n lineNumbers(),\n highlightActiveLineGutter(),\n highlightActiveLine(),\n drawSelection(),\n bracketMatching(),\n history(),\n pineLanguage,\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n darkTheme,\n createPineLinter((msg, ok) => {\n status.style.color = ok ? '#8b949e' : '#f85149';\n status.textContent = msg;\n }),\n keymap.of([...defaultKeymap, ...historyKeymap]),\n placeholder(t('pine.placeholder', '// plot(sma(close, 20))')),\n EditorView.updateListener.of((u) => {\n if (!u.docChanged) return;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => runApply(u.state.doc.toString()), debounceMs);\n }),\n ];\n\n const state = EditorState.create({\n doc: opts.initialScript ?? '',\n extensions,\n });\n\n const view = new EditorView({ state, parent: host });\n\n applyBtn.onclick = () => runApply(view.state.doc.toString());\n\n return {\n el: wrap,\n getScript: () => view.state.doc.toString(),\n setScript: (script) => {\n view.dispatch({\n changes: { from: 0, to: view.state.doc.length, insert: script },\n });\n },\n applyNow: () => runApply(view.state.doc.toString()),\n destroy: () => {\n view.destroy();\n wrap.remove();\n },\n };\n}","import { StreamLanguage } from '@codemirror/language';\n\nexport const pineLanguage = StreamLanguage.define({\n name: 'pine-lite',\n startState: () => ({}),\n token(stream) {\n if (stream.eatSpace()) return null;\n if (stream.match(/\\/\\/.*/)) return 'comment';\n if (stream.match(/0x[\\da-fA-F]+|\\d+\\.?\\d*(?:[eE][+-]?\\d+)?/)) return 'number';\n if (stream.match(/\\b(?:var|plot|if|else|while|for|to|and|or|not|true|false)\\b/)) return 'keyword';\n if (stream.match(/\\b(?:sma|ema|rsi|close|open|high|low|volume|hl2|hlc3)\\b/)) return 'variableName';\n if (stream.match(/:=|==|!=|<=|>=|[+\\-*/<>=(){},]/)) return 'operator';\n if (stream.match(/[A-Za-z_]\\w*/)) return 'name';\n stream.next();\n return null;\n },\n});"],"mappings":"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","names":["t","DEFAULT_INDICATOR_CONFIG","DEFAULT_INDICATOR_CONFIG","t","t","t","fmt","t","t","t","t","t","t"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coderyo/ui-shell",
3
- "version": "1.0.0-rc.2",
3
+ "version": "1.0.0-rc.4",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "exports": {
@@ -10,12 +10,22 @@
10
10
  }
11
11
  },
12
12
  "dependencies": {
13
- "@coderyo/data": "1.0.0-rc.2",
14
- "@coderyo/drawings": "1.0.0-rc.2",
15
- "@coderyo/i18n": "1.0.0-rc.2",
16
- "@coderyo/indicators": "1.0.0-rc.2"
13
+ "@codemirror/autocomplete": "^6.20.2",
14
+ "@codemirror/commands": "^6.10.3",
15
+ "@codemirror/language": "^6.12.3",
16
+ "@codemirror/lint": "^6.9.6",
17
+ "@codemirror/search": "^6.7.0",
18
+ "@codemirror/state": "^6.6.0",
19
+ "@codemirror/view": "^6.43.0",
20
+ "@lezer/highlight": "^1.2.3",
21
+ "@coderyo/data": "1.0.0-rc.4",
22
+ "@coderyo/drawings": "1.0.0-rc.4",
23
+ "@coderyo/i18n": "1.0.0-rc.4",
24
+ "@coderyo/pine-lite": "1.0.0-rc.4",
25
+ "@coderyo/indicators": "1.0.0-rc.4"
17
26
  },
18
27
  "devDependencies": {
28
+ "happy-dom": "^20.9.0",
19
29
  "tsup": "^8.5.0",
20
30
  "typescript": "^5.8.3",
21
31
  "vitest": "^3.2.4",