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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
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';
@@ -25,6 +26,8 @@ declare function mountSettingsPanel(parent: HTMLElement, opts?: SettingsPanelOpt
25
26
  type SymbolInputMode = 'manual' | 'search' | 'none';
26
27
  interface TopBarOptions {
27
28
  intervals?: Interval[];
29
+ /** Highlighted interval button (defaults to first in `intervals`). */
30
+ activeInterval?: Interval;
28
31
  initialSymbol?: string;
29
32
  onSymbolSearch?: (query: string) => Promise<SymbolSearchHit[]>;
30
33
  onSymbolSelect?: (symbol: string) => void;
@@ -36,7 +39,10 @@ interface TopBarOptions {
36
39
  symbolInput?: SymbolInputMode;
37
40
  showSettings?: boolean;
38
41
  }
39
- declare function mountTopBar(parent: HTMLElement, opts?: TopBarOptions): HTMLElement;
42
+ declare function mountTopBar(parent: HTMLElement, opts?: TopBarOptions): {
43
+ el: HTMLElement;
44
+ setActiveInterval: (interval: Interval) => void;
45
+ };
40
46
 
41
47
  interface ContextMenuAction {
42
48
  id: string;
@@ -153,6 +159,7 @@ declare function mountChartLayout(root: HTMLElement, opts?: ChartLayoutOptions):
153
159
  chartHost: HTMLElement;
154
160
  indicatorHost: HTMLElement;
155
161
  topBar: HTMLElement;
162
+ setActiveInterval: (interval: _coderyo_data.Interval) => void;
156
163
  statusBar: ReturnType<typeof mountStatusBar>;
157
164
  crosshairLegend: ReturnType<typeof mountCrosshairLegend>;
158
165
  detachContextMenu: () => void;
package/dist/index.js CHANGED
@@ -325,14 +325,30 @@ function mountTopBar(parent, opts = {}) {
325
325
  });
326
326
  }
327
327
  const intervals = opts.intervals ?? DEFAULT_INTERVALS;
328
+ const btnStyle = "background:#21262d;color:#e6edf3;border:1px solid #30363d;border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px;";
329
+ const btnActiveStyle = btnStyle.replace("#21262d", "#388bfd").replace("#e6edf3", "#fff");
330
+ const intervalButtons = /* @__PURE__ */ new Map();
331
+ let activeInterval = opts.activeInterval ?? intervals[0];
332
+ const paintIntervalButtons = () => {
333
+ for (const [iv, btn] of intervalButtons) {
334
+ btn.style.cssText = iv === activeInterval ? btnActiveStyle : btnStyle;
335
+ }
336
+ };
328
337
  for (const iv of intervals) {
329
338
  const btn = document.createElement("button");
330
339
  btn.type = "button";
331
340
  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);
341
+ btn.style.cssText = btnStyle;
342
+ btn.onclick = () => {
343
+ if (activeInterval === iv) return;
344
+ activeInterval = iv;
345
+ paintIntervalButtons();
346
+ opts.onIntervalChange?.(iv);
347
+ };
348
+ intervalButtons.set(iv, btn);
334
349
  bar.appendChild(btn);
335
350
  }
351
+ paintIntervalButtons();
336
352
  const spacer = document.createElement("div");
337
353
  spacer.style.flex = "1";
338
354
  bar.appendChild(spacer);
@@ -349,7 +365,14 @@ function mountTopBar(parent, opts = {}) {
349
365
  bar.appendChild(mkBtn("\u26F6", opts.onFullscreen));
350
366
  bar.appendChild(mkBtn("\u{1F4F7}", opts.onScreenshot));
351
367
  parent.prepend(bar);
352
- return bar;
368
+ return {
369
+ el: bar,
370
+ setActiveInterval: (interval) => {
371
+ if (!intervalButtons.has(interval)) return;
372
+ activeInterval = interval;
373
+ paintIntervalButtons();
374
+ }
375
+ };
353
376
  }
354
377
 
355
378
  // src/context-menu.ts
@@ -773,6 +796,8 @@ function mountChartLayout(root, opts = {}) {
773
796
  let topBar = document.createElement("div");
774
797
  topBar.style.display = "none";
775
798
  root.insertBefore(topBar, body);
799
+ let setActiveInterval = () => {
800
+ };
776
801
  const crosshairLegend = mountCrosshairLegend(chartHost, { symbol: opts.initialSymbol });
777
802
  let detachContextMenu = () => {
778
803
  };
@@ -805,11 +830,15 @@ function mountChartLayout(root, opts = {}) {
805
830
  const f = layoutFeatures;
806
831
  if (f.showTopBar) {
807
832
  topBar.remove();
808
- topBar = mountTopBar(root, {
809
- ...opts,
810
- symbolInput: f.symbolInput,
811
- showSettings: f.showSettings
812
- });
833
+ const mounted = mountTopBar(
834
+ root,
835
+ Object.assign(opts, {
836
+ symbolInput: f.symbolInput,
837
+ showSettings: f.showSettings
838
+ })
839
+ );
840
+ topBar = mounted.el;
841
+ setActiveInterval = mounted.setActiveInterval;
813
842
  } else {
814
843
  topBar.style.display = "none";
815
844
  }
@@ -834,6 +863,7 @@ function mountChartLayout(root, opts = {}) {
834
863
  chartHost,
835
864
  indicatorHost,
836
865
  topBar,
866
+ setActiveInterval,
837
867
  statusBar,
838
868
  crosshairLegend,
839
869
  detachContextMenu,
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"],"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 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}"],"mappings":"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","names":["t","DEFAULT_INDICATOR_CONFIG","DEFAULT_INDICATOR_CONFIG","t","t","t","fmt","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.3",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "exports": {
@@ -10,12 +10,13 @@
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
+ "@coderyo/data": "1.0.0-rc.3",
14
+ "@coderyo/i18n": "1.0.0-rc.3",
15
+ "@coderyo/indicators": "1.0.0-rc.3",
16
+ "@coderyo/drawings": "1.0.0-rc.3"
17
17
  },
18
18
  "devDependencies": {
19
+ "happy-dom": "^20.9.0",
19
20
  "tsup": "^8.5.0",
20
21
  "typescript": "^5.8.3",
21
22
  "vitest": "^3.2.4",