dsh-hyperliquid 0.3.0 → 0.4.1

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/client.js CHANGED
@@ -148,7 +148,7 @@ var styles_default = `/**
148
148
  .dhl-chat-heading span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary); font-size: 10.5px; }
149
149
  .dhl-chat-ticket-toggle { flex: none; border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary); padding: 5px 9px; font: inherit; font-size: 11px; cursor: pointer; }
150
150
  .dhl-chat-ticket-toggle:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
151
- .dhl-chat-prompts { flex: none; display: flex; gap: 6px; padding: 8px 12px; border-bottom: 1px solid var(--dsw-alias-border-l1); }
151
+ .dhl-chat-prompts { flex: none; display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 12px; border-bottom: 1px solid var(--dsw-alias-border-l1); }
152
152
  .dhl-chat-prompts button { border: 1px solid var(--dsw-alias-border-l1); border-radius: 999px; background: transparent; color: var(--dsw-alias-label-secondary); padding: 4px 9px; font: inherit; font-size: 10.5px; cursor: pointer; }
153
153
  .dhl-chat-prompts button:hover { color: var(--dsw-alias-brand-primary); border-color: var(--dsw-alias-brand-primary); }
154
154
  .dhl-chat-unavailable { margin: 18px; padding: 13px 14px; border: 1px solid var(--dsw-alias-state-warn-primary); border-radius: 8px; color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 1.6; }
@@ -471,7 +471,7 @@ body:has(.dhl-divider.dragging) * { cursor: col-resize !important; }
471
471
  .dhl-chart-tools { min-height: 42px; }
472
472
  .dhl-chartbox { height: 410px; }
473
473
  /* Above Lightweight Charts' own canvases (z-index 1\u20132, later in the DOM) so drawings receive the pointer. */
474
- .dhl-chart-drawing-overlay { position: absolute; inset: 0; z-index: 4; width: 100%; height: 100%; overflow: hidden; pointer-events: none; color: #4c82d8; }
474
+ .dhl-chart-drawing-overlay { position: absolute; inset: 0; z-index: 4; width: 100%; height: 100%; overflow: hidden; pointer-events: none; color: #4c82d8; contain: layout paint; transform: translateZ(0); }
475
475
  .dhl-chart-drawing-overlay .role-user { color: #4c82d8; }
476
476
  .dhl-chart-drawing-overlay .role-support { color: var(--dsw-alias-state-success-primary); }
477
477
  .dhl-chart-drawing-overlay .role-resistance { color: var(--dsw-alias-state-error-primary); }
@@ -707,7 +707,7 @@ body:has(.dhl-divider.dragging) * { cursor: col-resize !important; }
707
707
  /* Direct manipulation: a transparent, wide hit layer on strokes (empty chart space still pans). */
708
708
  .dhl-drawing-hit * { stroke: transparent !important; fill: transparent !important; stroke-dasharray: none !important; stroke-width: 12px !important; pointer-events: stroke; cursor: move; }
709
709
  .dhl-drawing-hit .dhl-text-box, .dhl-drawing-hit .dhl-position rect, .dhl-drawing-hit text { pointer-events: all; }
710
- .dhl-chart-drawing-overlay.drawing .dhl-drawing-hit { display: none; }
710
+ .dhl-chart-drawing-overlay.drawing .dhl-drawing-hit, .dhl-canvas-gesture .dhl-drawing-hit { display: none; }
711
711
  .dhl-drawing.locked .dhl-drawing-hit * { cursor: default; }
712
712
  .dhl-drawing:has(> .dhl-drawing-hit:hover) > line, .dhl-drawing:has(> .dhl-drawing-hit:hover) > g > line, .dhl-drawing.selected > line, .dhl-drawing.selected > g > line { stroke-width: calc(var(--dhl-line-width, 1.8) + 1); }
713
713
  .dhl-drawing.selected { filter: drop-shadow(0 0 3px color-mix(in srgb, currentColor 55%, transparent)); }
@@ -908,7 +908,7 @@ body:has(.dhl-divider.dragging) * { cursor: col-resize !important; }
908
908
 
909
909
  /* Fixed range volume profile: up/down volume per price row, value area brighter, POC in the alert red. */
910
910
  .dhl-vprofile-range { fill: color-mix(in srgb, currentColor 5%, transparent); stroke: none; }
911
- .dhl-vprofile rect.dhl-vprofile-up, .dhl-vprofile rect.dhl-vprofile-down { stroke: none; }
911
+ .dhl-vprofile .dhl-vprofile-up, .dhl-vprofile .dhl-vprofile-down { stroke: none; }
912
912
  .dhl-vprofile-up { fill: color-mix(in srgb, #4c82d8 62%, transparent); }
913
913
  .dhl-vprofile-down { fill: color-mix(in srgb, #e2a943 62%, transparent); }
914
914
  .dhl-vprofile-out .dhl-vprofile-up { fill: color-mix(in srgb, #4c82d8 26%, transparent); }
@@ -919,6 +919,33 @@ body:has(.dhl-divider.dragging) * { cursor: col-resize !important; }
919
919
  /* Magnet feedback: a ring on the O/H/L/C value the anchor will snap to. */
920
920
  .dhl-snap-ring { fill: none; stroke: var(--dsw-alias-brand-primary, #4c82d8); stroke-width: 1.5; pointer-events: none; }
921
921
  .dhl-draw-toolbar .dhl-select > select { height: 26px; font-size: 11px; }
922
+ .dhl-draw-toolbar .dhl-fib-levels { width: 190px; }
923
+
924
+ .dhl-badge.managed { color: var(--dsw-alias-brand-primary, #4c82d8); border-color: color-mix(in srgb, var(--dsw-alias-brand-primary, #4c82d8) 40%, transparent); background: color-mix(in srgb, var(--dsw-alias-brand-primary, #4c82d8) 10%, transparent); }
925
+ .dhl-managed-list { margin: 6px 0 10px; padding-left: 20px; line-height: 1.7; }
926
+
927
+ /* Trading limits dialog: tabs keep it short; when the viewport is still too small the card caps at the
928
+ overlay's padding box (DSH Modal contract: max-height 100%) and only the body scrolls \u2014 header and
929
+ footer (the save button) stay in view. */
930
+ .dhl-limits-modal { max-height: 100%; }
931
+ .dhl-limits-content { flex: 1 1 auto; min-height: 0; }
932
+ .dhl-limits-content > :has(> .dhl-limits-scroll) { flex: 1 1 auto; min-height: 0; overflow: hidden; margin-top: 12px; }
933
+ .dhl-limits-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; margin: 0 -24px; padding: 14px 24px 2px; scrollbar-gutter: stable; }
934
+ .dhl-limits-tabs { flex: none; display: flex; gap: 4px; border-bottom: 1px solid var(--dsw-alias-border-l1); }
935
+ .dhl-limits-tabs button { position: relative; height: 34px; padding: 0 12px; border: 0; background: transparent; color: var(--dsw-alias-label-secondary); font: inherit; font-size: 13px; cursor: pointer; }
936
+ .dhl-limits-tabs button:hover { color: var(--dsw-alias-label-primary); }
937
+ .dhl-limits-tabs button.on { color: var(--dsw-alias-label-primary); font-weight: 600; }
938
+ .dhl-limits-tabs button.on::after { content: ''; position: absolute; left: 10px; right: 10px; bottom: -1px; height: 2px; border-radius: 1px; background: var(--dsw-alias-label-primary); }
939
+ .dhl-limits-tabs button:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: -2px; border-radius: 6px; }
940
+ .dhl-limits-section + .dhl-limits-section { margin-top: 0; padding-top: 0; border-top: 0; }
941
+ .dhl-limits-ledger-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 0 0 12px; color: var(--dsw-alias-label-secondary); font-size: 12px; }
942
+ .dhl-limits-scroll .dhl-limits-note { margin-top: 10px; font-size: 12px; line-height: 18px; }
943
+ .dhl-limits-facts { display: grid; grid-template-columns: auto 1fr; gap: 10px 14px; margin: 0; font-size: 13px; line-height: 20px; }
944
+ .dhl-limits-facts dt { color: var(--dsw-alias-label-secondary); white-space: nowrap; }
945
+ .dhl-limits-facts dd { margin: 0; color: var(--dsw-alias-label-primary); }
946
+ .dhl-limits-hint { display: block; margin-top: 2px; color: var(--dsw-alias-label-secondary); font-size: 12px; }
947
+ .dhl-limits-scroll .dhl-risk-error { margin: 10px 0 0; }
948
+ .dhl-wallet-form ~ .dhl-limits-facts { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--dsw-alias-border-l1); }
922
949
  `;
923
950
 
924
951
  // src/client/locale.ts
@@ -1297,8 +1324,15 @@ function t(key3, fallback) {
1297
1324
  return localeT(key3, fallback);
1298
1325
  }
1299
1326
  function tr(zh, en) {
1300
- return ctxRef?.get?.("locale")?.getLocale?.()?.active === "en" ? en : zh;
1327
+ const now = Date.now();
1328
+ if (now - localeReadAt > 250) {
1329
+ localeReadAt = now;
1330
+ english = ctxRef?.get?.("locale")?.getLocale?.()?.active === "en";
1331
+ }
1332
+ return english ? en : zh;
1301
1333
  }
1334
+ var localeReadAt = -Infinity;
1335
+ var english = false;
1302
1336
  async function hyperCall(method, request = null) {
1303
1337
  if (!conn) throw new Error(t("error.noConnection", "\u65E0\u8FDE\u63A5"));
1304
1338
  if (typeof conn.rpc?.call !== "function") throw new Error(t("error.callFailed", "\u8FDC\u7AEF\u8C03\u7528\u5931\u8D25") + `: hyper.${method}`);
@@ -1647,23 +1681,23 @@ function selectChartCandle(candles, time, active, hit, maLines = [], settings) {
1647
1681
  const priorVolumeMean20 = previous.length === 20 ? previous.reduce((sum, value) => sum + value, 0) / 20 : null;
1648
1682
  return { candle, hit, values, priorVolumeMean20, settings };
1649
1683
  }
1650
- function formatChartReference(selection, coin, interval, english = false) {
1684
+ function formatChartReference(selection, coin, interval, english2 = false) {
1651
1685
  const { candle, hit, values, priorVolumeMean20 } = selection;
1652
1686
  const number = (value) => String(value);
1653
1687
  const computed = (value) => String(Number(value.toPrecision(10)));
1654
1688
  const time = new Date(candle.t).toISOString().replace("T", " ").replace(".000Z", " UTC");
1655
1689
  const hitLabel = {
1656
- volume: english ? "Volume" : "\u6210\u4EA4\u91CF",
1690
+ volume: english2 ? "Volume" : "\u6210\u4EA4\u91CF",
1657
1691
  sma20: "SMA20",
1658
1692
  sma60: "MA60",
1659
1693
  ema20: "EMA20",
1660
1694
  ema50: "EMA50",
1661
- bollingerUpper: english ? "BOLL upper" : "BOLL\u4E0A\u8F68",
1662
- bollingerLower: english ? "BOLL lower" : "BOLL\u4E0B\u8F68",
1695
+ bollingerUpper: english2 ? "BOLL upper" : "BOLL\u4E0A\u8F68",
1696
+ bollingerLower: english2 ? "BOLL lower" : "BOLL\u4E0B\u8F68",
1663
1697
  rsi14: `RSI${selection.settings?.rsi.period ?? 14}`,
1664
1698
  macd: "MACD",
1665
- macdSignal: english ? "MACD signal" : "\u4FE1\u53F7\u7EBF",
1666
- macdHistogram: english ? "MACD histogram" : "MACD\u67F1",
1699
+ macdSignal: english2 ? "MACD signal" : "\u4FE1\u53F7\u7EBF",
1700
+ macdHistogram: english2 ? "MACD histogram" : "MACD\u67F1",
1667
1701
  atr14: "ATR14",
1668
1702
  vwap: "VWAP (UTC HLC3)",
1669
1703
  stochastic: "Stochastic",
@@ -1671,17 +1705,17 @@ function formatChartReference(selection, coin, interval, english = false) {
1671
1705
  obv: "OBV"
1672
1706
  };
1673
1707
  const lines = [
1674
- english ? `[Chart point] ${coin} ${interval} candle opened ${time}` : `\u3010\u56FE\u8868\u70B9\u4F4D\u3011${coin} ${interval} K \u7EBF\u5F00\u76D8 ${time}`,
1675
- english ? `OHLC: ${number(candle.o)} / ${number(candle.h)} / ${number(candle.l)} / ${number(candle.c)}` : `\u5F00/\u9AD8/\u4F4E/\u6536\uFF1A${number(candle.o)} / ${number(candle.h)} / ${number(candle.l)} / ${number(candle.c)}`,
1676
- english ? `Volume: ${number(candle.v)}` : `\u6210\u4EA4\u91CF\uFF1A${number(candle.v)}`
1708
+ english2 ? `[Chart point] ${coin} ${interval} candle opened ${time}` : `\u3010\u56FE\u8868\u70B9\u4F4D\u3011${coin} ${interval} K \u7EBF\u5F00\u76D8 ${time}`,
1709
+ english2 ? `OHLC: ${number(candle.o)} / ${number(candle.h)} / ${number(candle.l)} / ${number(candle.c)}` : `\u5F00/\u9AD8/\u4F4E/\u6536\uFF1A${number(candle.o)} / ${number(candle.h)} / ${number(candle.l)} / ${number(candle.c)}`,
1710
+ english2 ? `Volume: ${number(candle.v)}` : `\u6210\u4EA4\u91CF\uFF1A${number(candle.v)}`
1677
1711
  ];
1678
1712
  if (priorVolumeMean20 != null && priorVolumeMean20 > 0) {
1679
- lines.push(english ? `Previous 20-candle mean volume: ${computed(priorVolumeMean20)}; current/mean: ${(candle.v / priorVolumeMean20).toFixed(2)}x` : `\u524D 20 \u6839\u5747\u91CF\uFF1A${computed(priorVolumeMean20)}\uFF1B\u5F53\u524D/\u5747\u91CF\uFF1A${(candle.v / priorVolumeMean20).toFixed(2)} \u500D`);
1713
+ lines.push(english2 ? `Previous 20-candle mean volume: ${computed(priorVolumeMean20)}; current/mean: ${(candle.v / priorVolumeMean20).toFixed(2)}x` : `\u524D 20 \u6839\u5747\u91CF\uFF1A${computed(priorVolumeMean20)}\uFF1B\u5F53\u524D/\u5747\u91CF\uFF1A${(candle.v / priorVolumeMean20).toFixed(2)} \u500D`);
1680
1714
  }
1681
- if (selection.settings) lines.push(`${english ? "Study parameters" : "\u6307\u6807\u53C2\u6570"}\uFF1A${JSON.stringify(selection.settings)}`);
1682
- if (values.some((row) => row.label === "OBV")) lines.push(english ? "OBV starts at zero on the first loaded candle; its absolute value depends on the window." : "OBV \u4EE5\u5DF2\u52A0\u8F7D\u9996\u6839\u4E3A\u96F6\u70B9\uFF0C\u7EDD\u5BF9\u503C\u53D6\u51B3\u4E8E\u5386\u53F2\u7A97\u53E3\u3002");
1683
- if (values.length) lines.push(`${english ? "Displayed studies" : "\u5DF2\u663E\u793A\u6307\u6807"}\uFF1A${values.map(({ label, value }) => `${label}=${computed(value)}`).join("\uFF0C")}`);
1684
- if (hit && hit !== "volume") lines.push(`${english ? "Selected study" : "\u70B9\u51FB\u6307\u6807"}\uFF1A${hitLabel[hit] ?? hit}`);
1715
+ if (selection.settings) lines.push(`${english2 ? "Study parameters" : "\u6307\u6807\u53C2\u6570"}\uFF1A${JSON.stringify(selection.settings)}`);
1716
+ if (values.some((row) => row.label === "OBV")) lines.push(english2 ? "OBV starts at zero on the first loaded candle; its absolute value depends on the window." : "OBV \u4EE5\u5DF2\u52A0\u8F7D\u9996\u6839\u4E3A\u96F6\u70B9\uFF0C\u7EDD\u5BF9\u503C\u53D6\u51B3\u4E8E\u5386\u53F2\u7A97\u53E3\u3002");
1717
+ if (values.length) lines.push(`${english2 ? "Displayed studies" : "\u5DF2\u663E\u793A\u6307\u6807"}\uFF1A${values.map(({ label, value }) => `${label}=${computed(value)}`).join("\uFF0C")}`);
1718
+ if (hit && hit !== "volume") lines.push(`${english2 ? "Selected study" : "\u70B9\u51FB\u6307\u6807"}\uFF1A${hitLabel[hit] ?? hit}`);
1685
1719
  return lines.join("\n");
1686
1720
  }
1687
1721
  var CHART_REFERENCE_SOURCE = "hyperliquid-chart";
@@ -1851,6 +1885,11 @@ function resetModeView(mode) {
1851
1885
  });
1852
1886
  void Promise.allSettled([loadStatus(), loadAccount(), loadTradeHistory(true)]);
1853
1887
  }
1888
+ function resetWalletView() {
1889
+ networkEpoch += 1;
1890
+ store.patch({ account: null, accountAt: 0, accountError: null, tradeHistory: null, tradeHistoryAt: 0, tradeHistoryError: null });
1891
+ void Promise.allSettled([loadStatus(), loadAccount(), loadTradeHistory(true)]);
1892
+ }
1854
1893
  async function switchTradingMode(mode) {
1855
1894
  const result = await hyperCall("switchTradingMode", { mode, acknowledgeLive: mode === "live" });
1856
1895
  if (result?.mode !== mode) throw new Error(tr("\u4EA4\u6613\u6A21\u5F0F\u5207\u6362\u7ED3\u679C\u4E0D\u4E00\u81F4", "Trading mode switch response mismatch"));
@@ -1895,6 +1934,10 @@ function mergeCandle(rows, next) {
1895
1934
  if (next.t === last.t && JSON.stringify(next) !== JSON.stringify(last)) return [...rows.slice(0, -1), next];
1896
1935
  return rows;
1897
1936
  }
1937
+ async function setCustody(mode, acknowledge = false) {
1938
+ await hyperCall("setCustody", { mode, acknowledge });
1939
+ await loadStatus();
1940
+ }
1898
1941
  async function loadStatus() {
1899
1942
  const epoch = networkEpoch;
1900
1943
  try {
@@ -2119,6 +2162,8 @@ async function pollEvents() {
2119
2162
  if (ev.kind === "network.changed" && (ev.data?.to === "mainnet" || ev.data?.to === "testnet") && ev.data.to !== store.get().status?.network) resetNetworkView(ev.data.to);
2120
2163
  if (ev.kind === "mode.changed" && (ev.data?.to === "live" || ev.data?.to === "paper") && ev.data.to !== store.get().status?.tradingMode) resetModeView(ev.data.to);
2121
2164
  if (ev.kind === "risk.changed") void loadStatus();
2165
+ if (ev.kind === "wallet.changed") resetWalletView();
2166
+ if (ev.kind === "custody.changed") void loadStatus();
2122
2167
  if (ev.kind === "paper.fill" || ev.kind?.startsWith("live.order.")) {
2123
2168
  void loadAccount();
2124
2169
  void loadTradeHistory(true);
@@ -2423,10 +2468,16 @@ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primit
2423
2468
  // src/client/format.ts
2424
2469
  var INTERVALS = ["1m", "5m", "15m", "1h", "4h", "1d"];
2425
2470
  var MORE_INTERVALS = ["3m", "30m", "2h", "8h", "12h", "3d", "1w", "1M"];
2471
+ var numberFormats = /* @__PURE__ */ new Map();
2426
2472
  function fmt(n2, d = 2) {
2427
2473
  const v = Number(n2);
2428
2474
  if (!Number.isFinite(v)) return "\u2014";
2429
- return v.toLocaleString("en-US", { minimumFractionDigits: d, maximumFractionDigits: d });
2475
+ let formatter = numberFormats.get(d);
2476
+ if (!formatter) {
2477
+ formatter = new Intl.NumberFormat("en-US", { minimumFractionDigits: d, maximumFractionDigits: d });
2478
+ numberFormats.set(d, formatter);
2479
+ }
2480
+ return formatter.format(v);
2430
2481
  }
2431
2482
  function fmtPrice(n2) {
2432
2483
  const v = Number(n2);
@@ -17651,6 +17702,7 @@ function isSingleEmoji(value) {
17651
17702
  if (typeof value !== "string" || !value || value.length > 32 || !/\p{Extended_Pictographic}|\p{Regional_Indicator}/u.test(value)) return false;
17652
17703
  return [...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(value)].length === 1;
17653
17704
  }
17705
+ var FIB_LEVEL_KINDS = ["fib", "fibext", "fibchannel"];
17654
17706
  function volumeProfile(bars, { rows = 48, valueArea = 0.7 } = {}) {
17655
17707
  const valid = bars.filter((bar) => [bar.open, bar.high, bar.low, bar.close, bar.volume].every(Number.isFinite) && bar.high >= bar.low && bar.volume >= 0);
17656
17708
  if (!valid.length) return null;
@@ -17935,7 +17987,7 @@ function guide(a, b, value, key3, text) {
17935
17987
  }
17936
17988
  var BADGE_PATHS = { alert: "M6 16V11a6 6 0 0 1 12 0v5l2 2H4zM10 20a2 2 0 0 0 4 0", shared: "M4 9h13l-3-3M20 15H7l3 3" };
17937
17989
  var DASH = { solid: void 0, dashed: "7 5", dotted: "1.5 4" };
17938
- var DrawingShape = React7.memo(function DrawingShape2({ kind, points: points2, label, text: textValue, className, view, anchors = false, interactive = false, color, lineWidth, lineStyle, id, alert, shared, rows, valueArea }) {
17990
+ var DrawingShape = React7.memo(function DrawingShape2({ kind, points: points2, label, text: textValue, className, view, anchors = false, interactive = false, color, lineWidth, lineStyle, id, alert, shared, rows, valueArea, levels }) {
17939
17991
  const badges = [...alert ? ["alert"] : [], ...shared ? ["shared"] : []];
17940
17992
  const { width: w, height: h } = view;
17941
17993
  const px = points2.map((point) => view.project(point.time, point.price));
@@ -17988,7 +18040,7 @@ var DrawingShape = React7.memo(function DrawingShape2({ kind, points: points2, l
17988
18040
  if (!p[1]) break;
17989
18041
  const [start, end] = points2;
17990
18042
  const x0 = Math.min(p[0].x, p[1].x), x1 = Math.max(p[0].x, p[1].x, x0 + 40);
17991
- const rows2 = FIB_LEVELS.map((level) => ({ level, price: end.price + (start.price - end.price) * level })).map((row) => ({ ...row, y: view.project(end.time, row.price)?.y })).filter((row) => row.y != null);
18043
+ const rows2 = (levels ?? FIB_LEVELS).map((level) => ({ level, price: end.price + (start.price - end.price) * level })).map((row) => ({ ...row, y: view.project(end.time, row.price)?.y })).filter((row) => row.y != null);
17992
18044
  body = /* @__PURE__ */ React7.createElement(React7.Fragment, null, rows2.slice(1).map((row, index) => /* @__PURE__ */ React7.createElement("rect", { key: `b${row.level}`, className: `dhl-fib-band band-${index % 3}`, x: x0, width: x1 - x0, y: Math.min(row.y, rows2[index].y), height: Math.abs(row.y - rows2[index].y) })), rows2.map((row) => /* @__PURE__ */ React7.createElement("g", { key: row.level }, /* @__PURE__ */ React7.createElement("line", { className: row.level === 0.5 || row.level === 0.618 ? "dhl-fib-key" : void 0, x1: x0, x2: x1, y1: row.y, y2: row.y }), text(x0 + 4, row.y - 3, `${row.level} (${fmtPrice(row.price)})`))), /* @__PURE__ */ React7.createElement("line", { className: "dhl-drawing-mid", x1: p[0].x, y1: p[0].y, x2: p[1].x, y2: p[1].y }));
17993
18045
  break;
17994
18046
  }
@@ -18070,7 +18122,7 @@ var DrawingShape = React7.memo(function DrawingShape2({ kind, points: points2, l
18070
18122
  }
18071
18123
  const [a, b, c] = points2;
18072
18124
  const x0 = p[2].x, x1 = Math.max(x0 + Math.abs(p[1].x - p[0].x), x0 + 60);
18073
- const rows2 = FIB_EXT_LEVELS.map((level) => ({ level, price: c.price + (b.price - a.price) * level })).map((row) => ({ ...row, y: view.project(c.time, row.price)?.y })).filter((row) => row.y != null);
18125
+ const rows2 = (levels ?? FIB_EXT_LEVELS).map((level) => ({ level, price: c.price + (b.price - a.price) * level })).map((row) => ({ ...row, y: view.project(c.time, row.price)?.y })).filter((row) => row.y != null);
18074
18126
  body = /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("polyline", { className: "dhl-drawing-mid", points: `${p[0].x},${p[0].y} ${p[1].x},${p[1].y} ${p[2].x},${p[2].y}` }), rows2.map((row) => /* @__PURE__ */ React7.createElement("g", { key: row.level }, /* @__PURE__ */ React7.createElement("line", { className: row.level === 1 || row.level === 1.618 ? "dhl-fib-key" : void 0, x1: x0, x2: x1, y1: row.y, y2: row.y }), text(x0 + 4, row.y - 3, `${row.level} (${fmtPrice(row.price)})`))));
18075
18127
  break;
18076
18128
  }
@@ -18082,7 +18134,7 @@ var DrawingShape = React7.memo(function DrawingShape2({ kind, points: points2, l
18082
18134
  }
18083
18135
  const k = (points2[2].time - points2[0].time) / (points2[1].time - points2[0].time || 1);
18084
18136
  const offset = points2[2].price - (points2[0].price + (points2[1].price - points2[0].price) * k);
18085
- body = /* @__PURE__ */ React7.createElement(React7.Fragment, null, FIB_LEVELS.map((level) => {
18137
+ body = /* @__PURE__ */ React7.createElement(React7.Fragment, null, (levels ?? FIB_LEVELS).map((level) => {
18086
18138
  const a2 = view.project(points2[0].time, points2[0].price + offset * level), b2 = view.project(points2[1].time, points2[1].price + offset * level);
18087
18139
  const seg = a2 && b2 ? clipLine(a2, b2, w, h, 0) : null;
18088
18140
  return seg ? /* @__PURE__ */ React7.createElement("g", { key: level }, /* @__PURE__ */ React7.createElement("line", { className: level === 0.5 || level === 0.618 ? "dhl-fib-key" : void 0, x1: seg[0].x, y1: seg[0].y, x2: seg[1].x, y2: seg[1].y }), text(a2.x + 4, a2.y - 3, String(level))) : null;
@@ -18152,9 +18204,9 @@ var DrawingShape = React7.memo(function DrawingShape2({ kind, points: points2, l
18152
18204
  case "gannsquare": {
18153
18205
  if (!p[1]) break;
18154
18206
  const x0 = Math.min(p[0].x, p[1].x), x1 = Math.max(p[0].x, p[1].x), y0 = Math.min(p[0].y, p[1].y), y1 = Math.max(p[0].y, p[1].y);
18155
- const levels = kind === "gannbox" ? GANN_BOX_LEVELS : [0, 0.25, 0.5, 0.75, 1];
18156
- const xs = levels.map((level) => x0 + (x1 - x0) * level), ys = levels.map((level) => y0 + (y1 - y0) * level);
18157
- body = /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("rect", { className: "dhl-drawing-fill", x: x0, y: y0, width: x1 - x0, height: y1 - y0 }), xs.slice(1, -1).map((x, i) => /* @__PURE__ */ React7.createElement("line", { key: `v${i}`, className: "dhl-drawing-mid", x1: x, x2: x, y1: y0, y2: y1 })), ys.slice(1, -1).map((y, i) => /* @__PURE__ */ React7.createElement("line", { key: `h${i}`, className: "dhl-drawing-mid", x1: x0, x2: x1, y1: y, y2: y })), /* @__PURE__ */ React7.createElement("line", { x1: x0, y1, x2: x1, y2: y0 }), /* @__PURE__ */ React7.createElement("line", { x1: x0, y1: y0, x2: x1, y2: y1 }), kind === "gannsquare" ? [[x0, y1, x1, (y0 + y1) / 2], [x0, y1, (x0 + x1) / 2, y0], [x0, y0, x1, (y0 + y1) / 2], [x0, y0, (x0 + x1) / 2, y1]].map(([ax, ay, bx, by], i) => /* @__PURE__ */ React7.createElement("line", { key: `f${i}`, className: "dhl-drawing-mid", x1: ax, y1: ay, x2: bx, y2: by })) : null, kind === "gannbox" ? levels.map((level, i) => /* @__PURE__ */ React7.createElement("g", { key: `l${level}` }, text(xs[i], y0 - 4, String(level), "middle"), text(x0 - 4, ys[i] + 3, String(level), "end"))) : null);
18207
+ const levels2 = kind === "gannbox" ? GANN_BOX_LEVELS : [0, 0.25, 0.5, 0.75, 1];
18208
+ const xs = levels2.map((level) => x0 + (x1 - x0) * level), ys = levels2.map((level) => y0 + (y1 - y0) * level);
18209
+ body = /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("rect", { className: "dhl-drawing-fill", x: x0, y: y0, width: x1 - x0, height: y1 - y0 }), xs.slice(1, -1).map((x, i) => /* @__PURE__ */ React7.createElement("line", { key: `v${i}`, className: "dhl-drawing-mid", x1: x, x2: x, y1: y0, y2: y1 })), ys.slice(1, -1).map((y, i) => /* @__PURE__ */ React7.createElement("line", { key: `h${i}`, className: "dhl-drawing-mid", x1: x0, x2: x1, y1: y, y2: y })), /* @__PURE__ */ React7.createElement("line", { x1: x0, y1, x2: x1, y2: y0 }), /* @__PURE__ */ React7.createElement("line", { x1: x0, y1: y0, x2: x1, y2: y1 }), kind === "gannsquare" ? [[x0, y1, x1, (y0 + y1) / 2], [x0, y1, (x0 + x1) / 2, y0], [x0, y0, x1, (y0 + y1) / 2], [x0, y0, (x0 + x1) / 2, y1]].map(([ax, ay, bx, by], i) => /* @__PURE__ */ React7.createElement("line", { key: `f${i}`, className: "dhl-drawing-mid", x1: ax, y1: ay, x2: bx, y2: by })) : null, kind === "gannbox" ? levels2.map((level, i) => /* @__PURE__ */ React7.createElement("g", { key: `l${level}` }, text(xs[i], y0 - 4, String(level), "middle"), text(x0 - 4, ys[i] + 3, String(level), "end"))) : null);
18158
18210
  break;
18159
18211
  }
18160
18212
  case "icon": {
@@ -18192,7 +18244,7 @@ var DrawingShape = React7.memo(function DrawingShape2({ kind, points: points2, l
18192
18244
  const x0 = Math.min(p[0].x, p[1].x), x1 = Math.max(p[0].x, p[1].x);
18193
18245
  const settings = { rows: rows ?? 48, valueArea: valueArea ?? 0.7 };
18194
18246
  const profile = view.profile ? view.profile(from.time, to.time, settings) : volumeProfile(view.candles?.(from.time, to.time) ?? [], settings);
18195
- const partial = view.firstTime != null && view.barMs != null && view.firstTime - from.time > view.barMs;
18247
+ const partial = view.firstTime != null && view.barMs != null && view.firstTime - from.time >= view.barMs;
18196
18248
  if (!profile) {
18197
18249
  body = /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("rect", { className: "dhl-drawing-fill", x: x0, y: 0, width: Math.max(1, x1 - x0), height: h }), text(x0 + 4, 16, tr("\u533A\u95F4\u5185\u6682\u65E0\u6210\u4EA4\u91CF\u6570\u636E", "No volume in this range"), "start"));
18198
18250
  handles = [p[0], p[1]];
@@ -18202,13 +18254,17 @@ var DrawingShape = React7.memo(function DrawingShape2({ kind, points: points2, l
18202
18254
  const maxRow = Math.max(...profile.rows.map((row) => row.up + row.down)) || 1;
18203
18255
  const span = Math.max(24, (x1 - x0) * 0.3);
18204
18256
  const yTop = y(profile.rows.at(-1).high), yBottom = y(profile.rows[0].low), yPoc = y(profile.poc), yVah = y(profile.vah), yVal = y(profile.val);
18205
- body = /* @__PURE__ */ React7.createElement("g", { className: "dhl-vprofile" }, yTop != null && yBottom != null ? /* @__PURE__ */ React7.createElement("rect", { className: "dhl-vprofile-range", x: x0, y: yTop, width: Math.max(1, x1 - x0), height: Math.max(1, yBottom - yTop) }) : null, profile.rows.map((row, index) => {
18257
+ const paths2 = { valueUp: "", valueDown: "", outUp: "", outDown: "" };
18258
+ const rectPath = (x, top, width, height) => `M${x.toFixed(2)} ${top.toFixed(2)}h${width.toFixed(2)}v${height.toFixed(2)}h${(-width).toFixed(2)}z`;
18259
+ profile.rows.forEach((row, index) => {
18206
18260
  const top = y(row.high), bottom = y(row.low);
18207
- if (top == null || bottom == null) return null;
18261
+ if (top == null || bottom == null) return;
18208
18262
  const height = Math.max(1, bottom - top - 0.5), inValue = index >= profile.valueLow && index <= profile.valueHigh;
18209
18263
  const upWidth = span * row.up / maxRow, downWidth = span * row.down / maxRow;
18210
- return /* @__PURE__ */ React7.createElement("g", { key: index, className: inValue ? "dhl-vprofile-va" : "dhl-vprofile-out" }, /* @__PURE__ */ React7.createElement("rect", { className: "dhl-vprofile-up", x: x0, y: top, width: upWidth, height }), /* @__PURE__ */ React7.createElement("rect", { className: "dhl-vprofile-down", x: x0 + upWidth, y: top, width: downWidth, height }));
18211
- }), yPoc != null ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("line", { className: "dhl-vprofile-poc", x1: x0, y1: yPoc, x2: x1, y2: yPoc }), text(x1 - 4, yPoc - 4, `POC ${fmtPrice(profile.poc)}`, "end")) : null, yVah != null ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("line", { className: "dhl-drawing-mid", x1: x0, y1: yVah, x2: x1, y2: yVah }), text(x1 - 4, yVah - 4, `VAH ${fmtPrice(profile.vah)}`, "end")) : null, yVal != null ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("line", { className: "dhl-drawing-mid", x1: x0, y1: yVal, x2: x1, y2: yVal }), text(x1 - 4, yVal + 12, `VAL ${fmtPrice(profile.val)}`, "end")) : null, yBottom != null ? text(x0 + 4, Math.min(h - 4, yBottom + 14), partial ? tr("K \u7EBF\u4F30\u7B97 \xB7 \u533A\u95F4\u524D\u6BB5\u5386\u53F2\u672A\u52A0\u8F7D", "Estimated from bars \xB7 early range not loaded") : tr("K \u7EBF\u4F30\u7B97", "Estimated from bars"), "start", partial ? "dhl-drawing-text dhl-vprofile-warn" : "dhl-drawing-text") : null);
18264
+ paths2[inValue ? "valueUp" : "outUp"] += rectPath(x0, top, upWidth, height);
18265
+ paths2[inValue ? "valueDown" : "outDown"] += rectPath(x0 + upWidth, top, downWidth, height);
18266
+ });
18267
+ body = /* @__PURE__ */ React7.createElement("g", { className: "dhl-vprofile", "data-rows": profile.rows.length }, yTop != null && yBottom != null ? /* @__PURE__ */ React7.createElement("rect", { className: "dhl-vprofile-range", x: x0, y: yTop, width: Math.max(1, x1 - x0), height: Math.max(1, yBottom - yTop) }) : null, /* @__PURE__ */ React7.createElement("g", { className: "dhl-vprofile-va" }, /* @__PURE__ */ React7.createElement("path", { className: "dhl-vprofile-up", d: paths2.valueUp }), /* @__PURE__ */ React7.createElement("path", { className: "dhl-vprofile-down", d: paths2.valueDown })), /* @__PURE__ */ React7.createElement("g", { className: "dhl-vprofile-out" }, /* @__PURE__ */ React7.createElement("path", { className: "dhl-vprofile-up", d: paths2.outUp }), /* @__PURE__ */ React7.createElement("path", { className: "dhl-vprofile-down", d: paths2.outDown })), yPoc != null ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("line", { className: "dhl-vprofile-poc", x1: x0, y1: yPoc, x2: x1, y2: yPoc }), text(x1 - 4, yPoc - 4, `POC ${fmtPrice(profile.poc)}`, "end")) : null, yVah != null ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("line", { className: "dhl-drawing-mid", x1: x0, y1: yVah, x2: x1, y2: yVah }), text(x1 - 4, yVah - 4, `VAH ${fmtPrice(profile.vah)}`, "end")) : null, yVal != null ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("line", { className: "dhl-drawing-mid", x1: x0, y1: yVal, x2: x1, y2: yVal }), text(x1 - 4, yVal + 12, `VAL ${fmtPrice(profile.val)}`, "end")) : null, yBottom != null ? text(x0 + 4, Math.min(h - 4, yBottom + 14), partial ? tr("K \u7EBF\u4F30\u7B97 \xB7 \u533A\u95F4\u524D\u6BB5\u5386\u53F2\u672A\u52A0\u8F7D", "Estimated from bars \xB7 early range not loaded") : tr("K \u7EBF\u4F30\u7B97", "Estimated from bars"), "start", partial ? "dhl-drawing-text dhl-vprofile-warn" : "dhl-drawing-text") : null);
18212
18268
  handles = [p[0], p[1]];
18213
18269
  break;
18214
18270
  }
@@ -18289,6 +18345,21 @@ function DrawToolbar({ mark, globallyLocked, onEdit, onRemove, onClose, onClone,
18289
18345
  setText(mark.label);
18290
18346
  }, [mark.id, mark.label]);
18291
18347
  const locked = Boolean(mark.locked) || globallyLocked;
18348
+ const defaultLevels = (mark.levels ?? (mark.kind === "fibext" ? FIB_EXT_LEVELS : FIB_LEVELS)).join(", ");
18349
+ const [levelsText, setLevelsText] = React8.useState(defaultLevels);
18350
+ React8.useEffect(() => {
18351
+ setLevelsText(defaultLevels);
18352
+ }, [mark.id, defaultLevels]);
18353
+ const applyLevels = () => {
18354
+ if (levelsText === defaultLevels) return;
18355
+ if (!levelsText.trim()) {
18356
+ onEdit({ levels: null });
18357
+ return;
18358
+ }
18359
+ const values = levelsText.split(/[,,\s]+/).filter(Boolean).map(Number);
18360
+ if (values.length && values.every(Number.isFinite)) onEdit({ levels: values });
18361
+ else setLevelsText(defaultLevels);
18362
+ };
18292
18363
  const width = mark.lineWidth ?? 2;
18293
18364
  const style = mark.lineStyle ?? "solid";
18294
18365
  const tool = toolOf(mark.kind);
@@ -18348,7 +18419,22 @@ function DrawToolbar({ mark, globallyLocked, onEdit, onRemove, onClose, onClone,
18348
18419
  onClick: () => onEdit({ lineStyle: value })
18349
18420
  },
18350
18421
  /* @__PURE__ */ React8.createElement("svg", { viewBox: "0 0 18 18", width: "16", height: "16", "aria-hidden": "true" }, /* @__PURE__ */ React8.createElement("line", { x1: "2", x2: "16", y1: "9", y2: "9", stroke: "currentColor", strokeWidth: "2", strokeDasharray: value === "dashed" ? "4 3" : value === "dotted" ? "1 3" : void 0, strokeLinecap: "round" }))
18351
- ))) : null, mark.kind === "vprofile" ? /* @__PURE__ */ React8.createElement("span", { className: "dhl-draw-toolbar-group", role: "group", "aria-label": tr("\u6210\u4EA4\u91CF\u5206\u5E03\u8BBE\u7F6E", "Volume profile settings") }, /* @__PURE__ */ React8.createElement(Select, { "aria-label": tr("\u4EF7\u683C\u884C\u6570", "Rows"), title: tr("\u4EF7\u683C\u884C\u6570", "Rows"), value: String(mark.rows ?? 48), disabled: locked, onChange: (event) => onEdit({ rows: Number(event.target.value) }) }, [24, 48, 96, 150].map((value) => /* @__PURE__ */ React8.createElement("option", { key: value, value }, tr(`${value} \u884C`, `${value} rows`)))), /* @__PURE__ */ React8.createElement(Select, { "aria-label": tr("\u4EF7\u503C\u533A\u6BD4\u4F8B", "Value area"), title: tr("\u4EF7\u503C\u533A\u6210\u4EA4\u91CF\u5360\u6BD4", "Value area volume"), value: String(mark.valueArea ?? 0.7), disabled: locked, onChange: (event) => onEdit({ valueArea: Number(event.target.value) }) }, [0.68, 0.7, 0.8].map((value) => /* @__PURE__ */ React8.createElement("option", { key: value, value }, `VA ${Math.round(value * 100)}%`)))) : null, /* @__PURE__ */ React8.createElement("span", { className: "dhl-draw-toolbar-group" }, /* @__PURE__ */ React8.createElement("button", { type: "button", disabled: locked, title: tr("\u7CBE\u786E\u7F16\u8F91\u65F6\u95F4\u3001\u4EF7\u683C\u4E0E\u540D\u79F0", "Edit exact time, price and name"), "aria-label": tr("\u7F16\u8F91\u753B\u7EBF\u5750\u6807", "Edit drawing coordinates"), onClick: onCoordinates }, /* @__PURE__ */ React8.createElement(UiIcon, { name: "settings", size: 15 })), /* @__PURE__ */ React8.createElement(
18422
+ ))) : null, FIB_LEVEL_KINDS.includes(mark.kind) ? /* @__PURE__ */ React8.createElement("span", { className: "dhl-draw-toolbar-group", role: "group", "aria-label": tr("\u6590\u6CE2\u90A3\u5951\u5C42\u7EA7", "Fibonacci levels") }, /* @__PURE__ */ React8.createElement(
18423
+ "input",
18424
+ {
18425
+ className: "dhl-draw-toolbar-text dhl-fib-levels",
18426
+ value: levelsText,
18427
+ disabled: locked,
18428
+ "aria-label": tr("\u6590\u6CE2\u90A3\u5951\u5C42\u7EA7\uFF08\u9017\u53F7\u5206\u9694\uFF09", "Fibonacci levels (comma separated)"),
18429
+ title: tr("\u9017\u53F7\u5206\u9694\uFF0C\u56DE\u8F66\u5E94\u7528\uFF1B\u6E05\u7A7A\u6062\u590D\u9ED8\u8BA4", "Comma separated, Enter applies; empty restores defaults"),
18430
+ onChange: (event) => setLevelsText(event.target.value),
18431
+ onKeyDown: (event) => {
18432
+ if (event.key === "Enter") applyLevels();
18433
+ if (event.key === "Escape") setLevelsText(defaultLevels);
18434
+ },
18435
+ onBlur: applyLevels
18436
+ }
18437
+ )) : null, mark.kind === "vprofile" ? /* @__PURE__ */ React8.createElement("span", { className: "dhl-draw-toolbar-group", role: "group", "aria-label": tr("\u6210\u4EA4\u91CF\u5206\u5E03\u8BBE\u7F6E", "Volume profile settings") }, /* @__PURE__ */ React8.createElement(Select, { "aria-label": tr("\u4EF7\u683C\u884C\u6570", "Rows"), title: tr("\u4EF7\u683C\u884C\u6570", "Rows"), value: String(mark.rows ?? 48), disabled: locked, onChange: (event) => onEdit({ rows: Number(event.target.value) }) }, [24, 48, 96, 150].map((value) => /* @__PURE__ */ React8.createElement("option", { key: value, value }, tr(`${value} \u884C`, `${value} rows`)))), /* @__PURE__ */ React8.createElement(Select, { "aria-label": tr("\u4EF7\u503C\u533A\u6BD4\u4F8B", "Value area"), title: tr("\u4EF7\u503C\u533A\u6210\u4EA4\u91CF\u5360\u6BD4", "Value area volume"), value: String(mark.valueArea ?? 0.7), disabled: locked, onChange: (event) => onEdit({ valueArea: Number(event.target.value) }) }, [0.68, 0.7, 0.8].map((value) => /* @__PURE__ */ React8.createElement("option", { key: value, value }, `VA ${Math.round(value * 100)}%`)))) : null, /* @__PURE__ */ React8.createElement("span", { className: "dhl-draw-toolbar-group" }, /* @__PURE__ */ React8.createElement("button", { type: "button", disabled: locked, title: tr("\u7CBE\u786E\u7F16\u8F91\u65F6\u95F4\u3001\u4EF7\u683C\u4E0E\u540D\u79F0", "Edit exact time, price and name"), "aria-label": tr("\u7F16\u8F91\u753B\u7EBF\u5750\u6807", "Edit drawing coordinates"), onClick: onCoordinates }, /* @__PURE__ */ React8.createElement(UiIcon, { name: "settings", size: 15 })), /* @__PURE__ */ React8.createElement(
18352
18438
  "button",
18353
18439
  {
18354
18440
  type: "button",
@@ -18548,6 +18634,24 @@ async function chartImage(chart, box, title) {
18548
18634
  return new Promise((resolve, reject) => canvas.toBlob((blob) => blob ? resolve(blob) : reject(new Error("PNG export failed")), "image/png"));
18549
18635
  }
18550
18636
 
18637
+ // src/client/chart-feed.ts
18638
+ var fedShape = /* @__PURE__ */ new WeakMap();
18639
+ var fields = ["time", "value", "open", "high", "low", "close", "color"];
18640
+ function samePrefix(previous, data, length) {
18641
+ for (let i = 0; i < length; i++) for (const key3 of fields) if (previous[i][key3] !== data[i][key3]) return false;
18642
+ return true;
18643
+ }
18644
+ function feed(series, data, sig = "") {
18645
+ const prev = fedShape.get(series);
18646
+ const len = data.length, first = data[0]?.time, last = data.at(-1)?.time;
18647
+ const tailOnly = prev && prev.sig === sig && len > 0 && first === prev.data[0]?.time && (len === prev.data.length ? last === prev.data.at(-1)?.time : len === prev.data.length + 1 && data.at(-2)?.time === prev.data.at(-1)?.time) && samePrefix(prev.data, data, prev.data.length - 1);
18648
+ if (tailOnly) {
18649
+ if (len === prev.data.length + 1) series.update(data.at(-2));
18650
+ series.update(data.at(-1));
18651
+ } else series.setData(data);
18652
+ fedShape.set(series, { data, sig });
18653
+ }
18654
+
18551
18655
  // src/client/components/candle-chart.tsx
18552
18656
  var MAGNET_PX = 14;
18553
18657
  var MA_PALETTE = ["#29b6f6", "#ff7eb6", "#c6e377", "#ffb74d", "#b39ddb", "#4dd0e1", "#f48fb1", "#aed581"];
@@ -18582,6 +18686,37 @@ function cssVar(el, name, fallback) {
18582
18686
  function chartData(points2) {
18583
18687
  return points2.map((point) => ({ time: point.time, value: point.value }));
18584
18688
  }
18689
+ var FINITE_KINDS = /* @__PURE__ */ new Set([
18690
+ "trend",
18691
+ "rect",
18692
+ "measure",
18693
+ "fib",
18694
+ "long",
18695
+ "short",
18696
+ "text",
18697
+ "arrow",
18698
+ "triangle",
18699
+ "brush",
18700
+ "infoline",
18701
+ "trendangle",
18702
+ "pricerange",
18703
+ "daterange",
18704
+ "elliott",
18705
+ "elliottabc",
18706
+ "xabcd",
18707
+ "abcd",
18708
+ "gannbox",
18709
+ "gannsquare",
18710
+ "icon",
18711
+ "vprofile",
18712
+ "regression"
18713
+ ]);
18714
+ function offscreen(points2, view) {
18715
+ if (!points2.length) return false;
18716
+ const xs = points2.map((point) => view.project(point.time, point.price)?.x);
18717
+ if (xs.some((x) => x == null)) return false;
18718
+ return xs.every((x) => x < -320) || xs.every((x) => x > view.width + 320);
18719
+ }
18585
18720
  var profileCache = /* @__PURE__ */ new WeakMap();
18586
18721
  function cachedProfile(candles, from, to, options = {}) {
18587
18722
  let byRange = profileCache.get(candles);
@@ -18651,11 +18786,25 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
18651
18786
  const [selectedTime, setSelectedTime] = React10.useState(null);
18652
18787
  const [themeVersion, setThemeVersion] = React10.useState(0);
18653
18788
  const [viewportVersion, setViewportVersion] = React10.useState(0);
18789
+ const projectionStamp = React10.useRef([]);
18790
+ const readProjection = (r, box) => {
18791
+ const base = candlesRef.current[0]?.c ?? 1;
18792
+ return [
18793
+ r.chart.timeScale().logicalToCoordinate(0),
18794
+ r.chart.timeScale().logicalToCoordinate(1),
18795
+ r.candles.priceToCoordinate(base),
18796
+ r.candles.priceToCoordinate(base * 1.01),
18797
+ box.clientWidth,
18798
+ box.clientHeight,
18799
+ base
18800
+ ];
18801
+ };
18654
18802
  const [pendingPoints, setPendingPoints] = React10.useState([]);
18655
18803
  const [drawingHover, setDrawingHover] = React10.useState(null);
18656
18804
  const showVolume = indicators.has("volume");
18657
18805
  const studies = React10.useMemo(() => chartIndicators(candles, settings), [candles, settings]);
18658
18806
  const maComputed = React10.useMemo(() => maLines.map((line, index) => ({ line, color: maColor(line, index), ...maSeries(candles, line) })), [candles, maLines]);
18807
+ const axisKey = `${candles.length}:${candles[0]?.t ?? 0}:${candles.at(-1)?.t ?? 0}`;
18659
18808
  const timeline = React10.useMemo(() => {
18660
18809
  const times = [...new Set(candles.map((candle) => Math.floor(candle.t / 1e3)))].sort((a, b) => a - b);
18661
18810
  const step = times.length > 1 ? times[times.length - 1] - times[times.length - 2] : 60;
@@ -18680,7 +18829,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
18680
18829
  return times[i] * 1e3;
18681
18830
  };
18682
18831
  return { toLogical, toTime };
18683
- }, [candles]);
18832
+ }, [axisKey]);
18684
18833
  const timelineRef = React10.useRef(timeline);
18685
18834
  timelineRef.current = timeline;
18686
18835
  React10.useEffect(() => {
@@ -18807,7 +18956,21 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
18807
18956
  }
18808
18957
  }
18809
18958
  }
18810
- if (event.shiftKey && previous) price = previous.price;
18959
+ if (event.shiftKey && previous) {
18960
+ const px = chart.timeScale().logicalToCoordinate(timelineRef.current.toLogical(previous.time));
18961
+ const py = candleSeries.priceToCoordinate(previous.price);
18962
+ const x = event.clientX - rect.left;
18963
+ if (px == null || py == null) return { time, price: previous.price };
18964
+ const dx = x - px, dy = y - py;
18965
+ const angle = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
18966
+ if (Math.abs(Math.abs(angle) - Math.PI / 2) < 1e-6 || Math.abs(angle) < 1e-6 || Math.abs(Math.abs(angle) - Math.PI) < 1e-6) return { time, price: previous.price };
18967
+ const along = dx * Math.cos(angle) + dy * Math.sin(angle);
18968
+ const tx = px + Math.cos(angle) * along, ty = py + Math.sin(angle) * along;
18969
+ const snappedLogical = chart.timeScale().coordinateToLogical(tx);
18970
+ const snappedPrice = Number(candleSeries.coordinateToPrice(ty));
18971
+ if (snappedLogical == null || !Number.isFinite(snappedPrice) || snappedPrice <= 0) return { time, price: previous.price };
18972
+ return { time: timelineRef.current.toTime(Number(snappedLogical)), price: snappedPrice };
18973
+ }
18811
18974
  return { time, price };
18812
18975
  };
18813
18976
  snapRef.current = snapPoint;
@@ -18877,17 +19040,31 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
18877
19040
  let viewportFrame = 0;
18878
19041
  const refreshOverlay = () => {
18879
19042
  if (viewportFrame) cancelAnimationFrame(viewportFrame);
18880
- viewportFrame = requestAnimationFrame(() => setViewportVersion((value) => value + 1));
19043
+ viewportFrame = requestAnimationFrame(() => React10.startTransition(() => setViewportVersion((value) => value + 1)));
18881
19044
  };
18882
19045
  chart.timeScale().subscribeVisibleLogicalRangeChange(refreshOverlay);
19046
+ let scalingPrice = false;
19047
+ const endCanvasGesture = () => box.classList.remove("dhl-canvas-gesture");
19048
+ const beginCanvasGesture = (event) => {
19049
+ if (event.target instanceof HTMLCanvasElement) box.classList.add("dhl-canvas-gesture");
19050
+ };
19051
+ box.addEventListener("pointerdown", beginCanvasGesture, { passive: true });
19052
+ window.addEventListener("pointerup", endCanvasGesture);
19053
+ window.addEventListener("pointercancel", endCanvasGesture);
19054
+ window.addEventListener("blur", endCanvasGesture);
19055
+ const scaleDown = (event) => {
19056
+ scalingPrice = event.target instanceof HTMLCanvasElement && event.clientX >= box.getBoundingClientRect().right - chart.priceScale("right").width();
19057
+ };
18883
19058
  const scaleDrag = (event) => {
18884
- if (event.buttons) refreshOverlay();
19059
+ if (event.buttons && scalingPrice) refreshOverlay();
18885
19060
  };
19061
+ box.addEventListener("pointerdown", scaleDown, { passive: true });
18886
19062
  box.addEventListener("pointermove", scaleDrag, { passive: true });
18887
19063
  box.addEventListener("wheel", refreshOverlay, { passive: true });
18888
19064
  box.addEventListener("dblclick", refreshOverlay);
18889
19065
  const sizeObserver = new ResizeObserver(refreshOverlay);
18890
19066
  sizeObserver.observe(box);
19067
+ box.querySelectorAll("canvas").forEach((canvas) => sizeObserver.observe(canvas));
18891
19068
  chart.timeScale().subscribeVisibleTimeRangeChange((range) => {
18892
19069
  if (!fitted.current || restoring.current || !range || typeof range.from !== "number" || typeof range.to !== "number") return;
18893
19070
  if (rangeTimer) clearTimeout(rangeTimer);
@@ -18930,6 +19107,12 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
18930
19107
  box.removeEventListener("click", chartClick);
18931
19108
  chart.timeScale().unsubscribeVisibleLogicalRangeChange(refreshOverlay);
18932
19109
  box.removeEventListener("pointermove", scaleDrag);
19110
+ box.removeEventListener("pointerdown", beginCanvasGesture);
19111
+ window.removeEventListener("pointerup", endCanvasGesture);
19112
+ window.removeEventListener("pointercancel", endCanvasGesture);
19113
+ window.removeEventListener("blur", endCanvasGesture);
19114
+ endCanvasGesture();
19115
+ box.removeEventListener("pointerdown", scaleDown);
18933
19116
  box.removeEventListener("wheel", refreshOverlay);
18934
19117
  box.removeEventListener("dblclick", refreshOverlay);
18935
19118
  sizeObserver.disconnect();
@@ -18989,26 +19172,26 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
18989
19172
  const prevFirst = firstBarTime.current;
18990
19173
  const prepended = prevFirst != null && bars[0].time < prevFirst ? Math.max(0, bars.findIndex((bar) => bar.time >= prevFirst)) : 0;
18991
19174
  firstBarTime.current = bars[0].time;
18992
- r.candles.setData(chartType === "line" || chartType === "area" ? bars.map((bar) => ({ time: bar.time, value: bar.close })) : bars);
18993
- r.volume.setData(vols.map((v) => ({ ...v, color: v.color === FALLBACK_UP ? th.up : th.down })));
18994
- r.sma.setData(chartData(studies.sma20));
18995
- r.basis.setData(chartData(studies.basis));
18996
- r.basis.applyOptions({ visible: indicators.has("bollinger20") });
18997
- r.sma60.setData(chartData(studies.sma60));
18998
- r.ema20.setData(chartData(studies.ema20));
18999
- r.ema50.setData(chartData(studies.ema50));
19000
- r.upper.setData(chartData(studies.upper));
19001
- r.lower.setData(chartData(studies.lower));
19002
- r.volume.applyOptions({ visible: showVolume });
19003
- r.sma.applyOptions({ visible: indicators.has("sma20") });
19004
- r.sma60.applyOptions({ visible: indicators.has("sma60") });
19005
- r.ema20.applyOptions({ visible: indicators.has("ema20") });
19006
- r.ema50.applyOptions({ visible: indicators.has("ema50") });
19007
- r.upper.applyOptions({ visible: indicators.has("bollinger20") });
19008
- r.lower.applyOptions({ visible: indicators.has("bollinger20") });
19175
+ const studySig = JSON.stringify(settings), colorSig = `${chartType}|${th.up}|${th.down}`;
19176
+ feed(r.candles, chartType === "line" || chartType === "area" ? bars.map((bar) => ({ time: bar.time, value: bar.close })) : bars, chartType);
19177
+ if (showVolume) feed(r.volume, vols.map((v) => ({ ...v, color: v.color === FALLBACK_UP ? th.up : th.down })), colorSig);
19178
+ if (r.volume.options().visible !== showVolume) r.volume.applyOptions({ visible: showVolume });
19179
+ for (const [series, key3, indicator] of [
19180
+ [r.sma, "sma20", "sma20"],
19181
+ [r.sma60, "sma60", "sma60"],
19182
+ [r.ema20, "ema20", "ema20"],
19183
+ [r.ema50, "ema50", "ema50"],
19184
+ [r.basis, "basis", "bollinger20"],
19185
+ [r.upper, "upper", "bollinger20"],
19186
+ [r.lower, "lower", "bollinger20"]
19187
+ ]) {
19188
+ const visible = indicators.has(indicator);
19189
+ if (visible) feed(series, chartData(studies[key3]), studySig);
19190
+ if (series.options().visible !== visible) series.applyOptions({ visible });
19191
+ }
19009
19192
  if (indicators.has("vwap")) {
19010
19193
  r.vwap ??= r.chart.addSeries(lineSeries, { color: "#e89a56", lineWidth: 2, lastValueVisible: false, priceLineVisible: false });
19011
- r.vwap.setData(chartData(studies.vwap));
19194
+ feed(r.vwap, chartData(studies.vwap), studySig);
19012
19195
  } else if (r.vwap) {
19013
19196
  r.chart.removeSeries(r.vwap);
19014
19197
  r.vwap = void 0;
@@ -19019,7 +19202,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19019
19202
  pane.setStretchFactor(0.28);
19020
19203
  r.atr = pane.addSeries(lineSeries, { color: "#56bca7", lineWidth: 2, priceLineVisible: false });
19021
19204
  }
19022
- r.atr.setData(chartData(studies.atr14));
19205
+ feed(r.atr, chartData(studies.atr14), studySig);
19023
19206
  } else if (r.atr) {
19024
19207
  r.chart.removeSeries(r.atr);
19025
19208
  r.atr = void 0;
@@ -19032,7 +19215,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19032
19215
  r.rsiBounds = [settings.rsi.lower, settings.rsi.upper].map((price) => r.rsi.createPriceLine({ price, color: "#8d8d8d", lineWidth: 1, lineStyle: LineStyle.Dashed, axisLabelVisible: false }));
19033
19216
  }
19034
19217
  r.rsiBounds?.forEach((line, index) => line.applyOptions({ price: index ? settings.rsi.upper : settings.rsi.lower }));
19035
- r.rsi.setData(chartData(studies.rsi14));
19218
+ feed(r.rsi, chartData(studies.rsi14), studySig);
19036
19219
  } else if (r.rsi) {
19037
19220
  r.chart.removeSeries(r.rsi);
19038
19221
  r.rsi = void 0;
@@ -19045,9 +19228,9 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19045
19228
  r.macd = pane.addSeries(lineSeries, { color: "#55a6ce", lineWidth: 2, priceLineVisible: false, lastValueVisible: false });
19046
19229
  r.signal = pane.addSeries(lineSeries, { color: "#e2a943", lineWidth: 2, priceLineVisible: false, lastValueVisible: false });
19047
19230
  }
19048
- r.macd.setData(chartData(studies.macd));
19049
- r.signal.setData(chartData(studies.signal));
19050
- r.histogram.setData(studies.histogram.map((point) => ({ time: point.time, value: point.value, color: point.value >= 0 ? th.up : th.down })));
19231
+ feed(r.macd, chartData(studies.macd), studySig);
19232
+ feed(r.signal, chartData(studies.signal), studySig);
19233
+ feed(r.histogram, studies.histogram.map((point) => ({ time: point.time, value: point.value, color: point.value >= 0 ? th.up : th.down })), studySig + colorSig);
19051
19234
  } else if (r.macd) {
19052
19235
  r.chart.removeSeries(r.macd);
19053
19236
  r.chart.removeSeries(r.signal);
@@ -19071,7 +19254,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19071
19254
  if (group2.id === "stochastic") r.stochBounds = [settings.stochastic.lower, settings.stochastic.upper].map((price) => r.extra.stochK.createPriceLine({ price, color: "#8d8d8d", lineWidth: 1, lineStyle: LineStyle.Dashed, axisLabelVisible: false }));
19072
19255
  if (group2.id === "adx") r.extra.adx.createPriceLine({ price: 25, color: "#8d8d8d", lineWidth: 1, lineStyle: LineStyle.Dashed, axisLabelVisible: false });
19073
19256
  }
19074
- for (const key3 of group2.keys) r.extra[key3].setData(chartData(studies[key3]));
19257
+ for (const key3 of group2.keys) feed(r.extra[key3], chartData(studies[key3]), studySig);
19075
19258
  if (group2.id === "stochastic") r.stochBounds?.forEach((line, index) => line.applyOptions({ price: index ? settings.stochastic.upper : settings.stochastic.lower }));
19076
19259
  } else for (const key3 of group2.keys) {
19077
19260
  if (r.extra[key3]) {
@@ -19112,11 +19295,11 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19112
19295
  maRefs.current.set(item.line.id, entry);
19113
19296
  }
19114
19297
  entry.main.applyOptions({ color: item.color });
19115
- entry.main.setData(toPoints(item.values));
19298
+ feed(entry.main, toPoints(item.values), JSON.stringify(item.line));
19116
19299
  if (item.smoothed) {
19117
19300
  entry.smooth ??= r.chart.addSeries(lineSeries, { lineWidth: 1, lineStyle: LineStyle.Dashed, lastValueVisible: false, priceLineVisible: false, crosshairMarkerVisible: false });
19118
19301
  entry.smooth.applyOptions({ color: item.color });
19119
- entry.smooth.setData(toPoints(item.smoothed));
19302
+ feed(entry.smooth, toPoints(item.smoothed), JSON.stringify(item.line));
19120
19303
  } else if (entry.smooth) {
19121
19304
  r.chart.removeSeries(entry.smooth);
19122
19305
  entry.smooth = void 0;
@@ -19125,7 +19308,8 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19125
19308
  if (!fitted.current) {
19126
19309
  restoring.current = true;
19127
19310
  r.chart.timeScale().fitContent();
19128
- const saved = initialRange.current;
19311
+ const linked = linkRef.current?.range && linkRef.current.range.source !== linkRef.current.id ? linkRef.current.range : null;
19312
+ const saved = linked ?? initialRange.current;
19129
19313
  if (saved && saved.from < saved.to) {
19130
19314
  try {
19131
19315
  r.chart.timeScale().setVisibleRange({ from: saved.from, to: saved.to });
@@ -19137,6 +19321,11 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19137
19321
  restoring.current = false;
19138
19322
  }, 100);
19139
19323
  }
19324
+ const overlayFrame = requestAnimationFrame(() => {
19325
+ const box2 = boxRef.current;
19326
+ if (box2 && readProjection(r, box2).some((value, index) => value !== projectionStamp.current[index])) React10.startTransition(() => setViewportVersion((value) => value + 1));
19327
+ });
19328
+ return () => cancelAnimationFrame(overlayFrame);
19140
19329
  }, [candles, studies, indicators, showVolume, maComputed, chartType, settings]);
19141
19330
  React10.useEffect(() => {
19142
19331
  const r = refs.current;
@@ -19296,22 +19485,28 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19296
19485
  const view = React10.useMemo(() => {
19297
19486
  const chartApi = refs.current, box = boxRef.current;
19298
19487
  if (!chartApi || !box) return null;
19488
+ const stamp = readProjection(chartApi, box);
19489
+ projectionStamp.current = stamp;
19490
+ const [x0, x1, y0, y1] = stamp;
19491
+ const base = candlesRef.current[0]?.c ?? 1;
19492
+ const ys = /* @__PURE__ */ new Map();
19299
19493
  return {
19300
19494
  project: (time, price) => {
19301
- const x = chartApi.chart.timeScale().logicalToCoordinate(timeline.toLogical(time));
19302
- const y = chartApi.candles.priceToCoordinate(price);
19495
+ const x = x0 == null || x1 == null ? null : x0 + (x1 - x0) * timeline.toLogical(time);
19496
+ if (!ys.has(price)) ys.set(price, priceScale === "log" ? chartApi.candles.priceToCoordinate(price) : y0 == null || y1 == null ? null : y0 + (y1 - y0) * (price - base) / (base * 0.01));
19497
+ const y = ys.get(price);
19303
19498
  return x == null || y == null || !Number.isFinite(x) || !Number.isFinite(y) ? null : { x, y };
19304
19499
  },
19305
19500
  width: Math.max(0, box.clientWidth - chartApi.chart.priceScale("right").width()),
19306
19501
  height: chartApi.chart.panes()[0]?.getHeight() ?? box.clientHeight,
19307
19502
  bars: (from, to) => timeline.toLogical(to) - timeline.toLogical(from),
19308
- closes: (from, to) => candles.filter((candle) => candle.t >= from && candle.t <= to).map((candle) => ({ time: candle.t, price: candle.c })),
19309
- candles: (from, to) => candles.filter((candle) => candle.t >= from && candle.t <= to).map((candle) => ({ time: candle.t, open: candle.o, high: candle.h, low: candle.l, close: candle.c, volume: candle.v })),
19310
- profile: (from, to, options) => cachedProfile(candles, from, to, options),
19311
- firstTime: candles[0]?.t,
19312
- barMs: candles.length > 1 ? candles[1].t - candles[0].t : void 0
19503
+ closes: (from, to) => candlesRef.current.filter((candle) => candle.t >= from && candle.t <= to).map((candle) => ({ time: candle.t, price: candle.c })),
19504
+ candles: (from, to) => candlesRef.current.filter((candle) => candle.t >= from && candle.t <= to).map((candle) => ({ time: candle.t, open: candle.o, high: candle.h, low: candle.l, close: candle.c, volume: candle.v })),
19505
+ profile: (from, to, options) => cachedProfile(candlesRef.current, from, to, options),
19506
+ firstTime: candlesRef.current[0]?.t,
19507
+ barMs: candlesRef.current.length > 1 ? candlesRef.current[1].t - candlesRef.current[0].t : void 0
19313
19508
  };
19314
- }, [viewportVersion, timeline, themeVersion, expanded, indicators]);
19509
+ }, [viewportVersion, timeline, themeVersion, expanded, indicators, priceScale]);
19315
19510
  React10.useEffect(() => {
19316
19511
  if (selectedId && !markById.has(selectedId)) setSelectedId(null);
19317
19512
  }, [markById, selectedId]);
@@ -19426,7 +19621,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19426
19621
  const cloneOf = React10.useCallback((mark) => {
19427
19622
  const line = timelineRef.current;
19428
19623
  const shifted = markPoints(mark).map((point) => ({ time: line.toTime(line.toLogical(point.time) + 5), price: point.price }));
19429
- const style = Object.fromEntries(Object.entries({ color: mark.color, lineWidth: mark.lineWidth, lineStyle: mark.lineStyle }).filter(([, value]) => value != null));
19624
+ const style = Object.fromEntries(Object.entries({ color: mark.color, lineWidth: mark.lineWidth, lineStyle: mark.lineStyle, levels: mark.levels, rows: mark.rows, valueArea: mark.valueArea }).filter(([, value]) => value != null));
19430
19625
  return { kind: mark.kind, geometry: geometryPatch(mark.kind, shifted), style, label: mark.label };
19431
19626
  }, []);
19432
19627
  const clipboard = React10.useRef(null);
@@ -19526,6 +19721,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19526
19721
  }
19527
19722
  latest2 = next;
19528
19723
  setGroupDraft(next);
19724
+ for (const [memberId, member] of originals) if (member.kind === "level") priceLines.current.get(memberId)?.applyOptions({ price: next.get(memberId)[0].price });
19529
19725
  };
19530
19726
  const move2 = (ev) => {
19531
19727
  if (!moved2 && Math.hypot(ev.clientX - origin2.x, ev.clientY - origin2.y) < 3) return;
@@ -19543,6 +19739,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19543
19739
  };
19544
19740
  const cancel2 = () => {
19545
19741
  detach2();
19742
+ for (const [memberId, member] of originals) if (member.kind === "level") priceLines.current.get(memberId)?.applyOptions({ price: Number(member.price) });
19546
19743
  setGroupDraft(null);
19547
19744
  };
19548
19745
  const onKey2 = (ev) => {
@@ -19582,7 +19779,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19582
19779
  const compute = (ev) => {
19583
19780
  let next;
19584
19781
  if (anchor != null) {
19585
- const point = snap(ev, original[anchor === 0 ? 1 : 0]);
19782
+ const point = snap(ev, original[anchor === 0 ? 1 : anchor - 1]);
19586
19783
  if (!point) return;
19587
19784
  next = original.map((item) => ({ ...item }));
19588
19785
  if (mark.kind === "level") next[0] = { ...next[0], price: point.price };
@@ -19671,6 +19868,8 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19671
19868
  const live = draft?.id === mark.id ? draft.points : groupDraft?.get(mark.id) ?? points2;
19672
19869
  const shown = mark.kind === "level" ? [{ time: lastTime, price: live[0].price }] : live;
19673
19870
  const selected = selectedId === mark.id || group.includes(mark.id);
19871
+ const clampedDecoration = !(mark.source === "user" || mark.kind === "level") || Boolean(mark.alert) || Boolean(mark.shared);
19872
+ if (!selected && !clampedDecoration && FINITE_KINDS.has(mark.kind) && offscreen(shown, view)) return null;
19674
19873
  return /* @__PURE__ */ React10.createElement(
19675
19874
  DrawingShape,
19676
19875
  {
@@ -19680,6 +19879,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19680
19879
  points: shown,
19681
19880
  view,
19682
19881
  text: mark.label,
19882
+ dataVersion: mark.kind === "vprofile" || mark.kind === "regression" ? candles : void 0,
19683
19883
  className: `dhl-drawing ${roleClass(mark)}${mark.kind === "level" ? " is-level" : ""}${selected ? " selected" : ""}${mark.locked || drawingsLocked ? " locked" : ""}`,
19684
19884
  label: mark.source === "user" || mark.kind === "level" ? void 0 : mark.label,
19685
19885
  anchors: selected,
@@ -19690,7 +19890,8 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19690
19890
  alert: mark.alert,
19691
19891
  shared: mark.shared,
19692
19892
  rows: mark.rows,
19693
- valueArea: mark.valueArea
19893
+ valueArea: mark.valueArea,
19894
+ levels: mark.levels
19694
19895
  }
19695
19896
  );
19696
19897
  }), /* @__PURE__ */ React10.createElement("g", { className: "dhl-fills" }, fills.map((event) => {
@@ -19766,7 +19967,7 @@ function CandleChart({ candles, marks, indicators, expanded, resetToken, drawMod
19766
19967
  var React11 = __toESM(require("react"), 1);
19767
19968
  var HISTORY_LIMIT = 100;
19768
19969
  var GEOMETRY = ["price", "fromTime", "fromPrice", "toTime", "toPrice", "points"];
19769
- var STYLE = ["color", "lineWidth", "lineStyle", "locked", "hidden", "shared", "alert", "rows", "valueArea"];
19970
+ var STYLE = ["color", "lineWidth", "lineStyle", "locked", "hidden", "shared", "alert", "rows", "valueArea", "levels"];
19770
19971
  var FLAGS = /* @__PURE__ */ new Set(["locked", "hidden", "shared", "alert"]);
19771
19972
  function recreateRequest(mark) {
19772
19973
  const out = { kind: mark.kind, label: mark.label, role: mark.role, coin: mark.coin, interval: mark.interval };
@@ -19878,6 +20079,28 @@ function useDrawingHistory(scope) {
19878
20079
  }
19879
20080
  return apiDelete(ctx, op.mark.id);
19880
20081
  };
20082
+ const step = async (ctx, forward) => {
20083
+ const from = forward ? redoStack : undoStack, to = forward ? undoStack : redoStack;
20084
+ const op = from.current.pop();
20085
+ if (!op) return;
20086
+ bump();
20087
+ let done = null, left = null;
20088
+ if (op.type === "batch") {
20089
+ const ok = [], failed = [];
20090
+ for (const inner of forward ? op.ops : [...op.ops].reverse()) (await apply2(ctx, inner, forward) ? ok : failed).push(inner);
20091
+ if (!forward) {
20092
+ ok.reverse();
20093
+ failed.reverse();
20094
+ }
20095
+ if (ok.length) done = { type: "batch", ops: ok };
20096
+ if (failed.length) left = { type: "batch", ops: failed };
20097
+ } else if (await apply2(ctx, op, forward)) done = op;
20098
+ else left = op;
20099
+ if (ctx.scope !== scopeRef.current) return;
20100
+ if (done) to.current.push(done);
20101
+ if (left) from.current.push(left);
20102
+ bump();
20103
+ };
19881
20104
  return {
19882
20105
  create: (request) => serial(async (ctx) => {
19883
20106
  const saved = await apiCreate(ctx, request);
@@ -19919,29 +20142,8 @@ function useDrawingHistory(scope) {
19919
20142
  }
19920
20143
  if (done.length) record({ type: "batch", ops: done }, ctx);
19921
20144
  }, void 0),
19922
- // A failed undo/redo goes back where it was, so the stacks keep describing the host.
19923
- undo: () => serial(async (ctx) => {
19924
- const op = undoStack.current.pop();
19925
- if (!op) return;
19926
- redoStack.current.push(op);
19927
- bump();
19928
- if (!await apply2(ctx, op, false)) {
19929
- redoStack.current.pop();
19930
- undoStack.current.push(op);
19931
- bump();
19932
- }
19933
- }, void 0),
19934
- redo: () => serial(async (ctx) => {
19935
- const op = redoStack.current.pop();
19936
- if (!op) return;
19937
- undoStack.current.push(op);
19938
- bump();
19939
- if (!await apply2(ctx, op, true)) {
19940
- undoStack.current.pop();
19941
- redoStack.current.push(op);
19942
- bump();
19943
- }
19944
- }, void 0),
20145
+ undo: () => serial((ctx) => step(ctx, false), void 0),
20146
+ redo: () => serial((ctx) => step(ctx, true), void 0),
19945
20147
  canUndo: undoStack.current.length > 0,
19946
20148
  canRedo: redoStack.current.length > 0
19947
20149
  };
@@ -19950,7 +20152,7 @@ function useDrawingHistory(scope) {
19950
20152
  // src/client/components/drawing-list.tsx
19951
20153
  var React12 = __toESM(require("react"), 1);
19952
20154
  var OPEN_KEY = "dsh-hyperliquid.draw.listOpen";
19953
- function DrawingList({ marks, locked, onSelect, onEdit, onRemove }) {
20155
+ var DrawingList = React12.memo(function DrawingList2({ marks, locked, onSelect, onEdit, onRemove, language }) {
19954
20156
  const [open, setOpen] = React12.useState(() => {
19955
20157
  try {
19956
20158
  return localStorage.getItem(OPEN_KEY) !== "0";
@@ -19966,11 +20168,11 @@ function DrawingList({ marks, locked, onSelect, onEdit, onRemove }) {
19966
20168
  return !value;
19967
20169
  });
19968
20170
  const hidden = marks.filter((mark) => mark.hidden).length;
19969
- return /* @__PURE__ */ React12.createElement("section", { className: "dhl-drawing-list", "aria-label": tr("\u753B\u7EBF\u5217\u8868", "Drawings") }, /* @__PURE__ */ React12.createElement("button", { type: "button", className: "dhl-drawing-list-head", "aria-expanded": open, onClick: toggle }, /* @__PURE__ */ React12.createElement("span", null, tr("\u753B\u7EBF", "Drawings"), " ", marks.length, hidden ? tr(` \xB7 \u9690\u85CF ${hidden}`, ` \xB7 ${hidden} hidden`) : ""), /* @__PURE__ */ React12.createElement(UiIcon, { name: open ? "chevronDown" : "chevronRight", size: 14 })), open ? /* @__PURE__ */ React12.createElement("ul", null, marks.map((mark) => {
20171
+ return /* @__PURE__ */ React12.createElement("section", { className: "dhl-drawing-list", lang: language, "aria-label": tr("\u753B\u7EBF\u5217\u8868", "Drawings") }, /* @__PURE__ */ React12.createElement("button", { type: "button", className: "dhl-drawing-list-head", "aria-expanded": open, onClick: toggle }, /* @__PURE__ */ React12.createElement("span", null, tr("\u753B\u7EBF", "Drawings"), " ", marks.length, hidden ? tr(` \xB7 \u9690\u85CF ${hidden}`, ` \xB7 ${hidden} hidden`) : ""), /* @__PURE__ */ React12.createElement(UiIcon, { name: open ? "chevronDown" : "chevronRight", size: 14 })), open ? /* @__PURE__ */ React12.createElement("ul", null, marks.map((mark) => {
19970
20172
  const tool = toolOf(mark.kind);
19971
20173
  return /* @__PURE__ */ React12.createElement("li", { key: mark.id, className: mark.hidden ? "is-hidden" : "" }, /* @__PURE__ */ React12.createElement("button", { type: "button", className: "dhl-drawing-list-name", title: tr("\u5728\u56FE\u4E0A\u5B9A\u4F4D\u5E76\u9009\u4E2D", "Find and select on the chart"), onClick: () => onSelect(mark.id), disabled: mark.hidden }, /* @__PURE__ */ React12.createElement("span", { style: { color: mark.color } }, /* @__PURE__ */ React12.createElement(ToolIcon, { kind: mark.kind, size: 14 })), /* @__PURE__ */ React12.createElement("b", null, mark.label), /* @__PURE__ */ React12.createElement("small", null, mark.alert ? /* @__PURE__ */ React12.createElement("span", { className: "dhl-list-flag", title: tr("\u4EF7\u683C\u63D0\u9192\u5DF2\u5F00\u542F", "Price alert armed") }, /* @__PURE__ */ React12.createElement(UiIcon, { name: "bell", size: 12 })) : null, mark.shared ? /* @__PURE__ */ React12.createElement("span", { className: "dhl-list-flag", title: tr("\u5DF2\u540C\u6B65\u5230\u6240\u6709\u4F1A\u8BDD", "Synced to all sessions") }, /* @__PURE__ */ React12.createElement(UiIcon, { name: "sync", size: 12 })) : null, tr(tool.zh, tool.en), " \xB7 ", mark.interval, mark.source === "agent" ? " \xB7 agent" : "")), /* @__PURE__ */ React12.createElement("button", { type: "button", "aria-pressed": !mark.hidden, title: mark.hidden ? tr("\u663E\u793A", "Show") : tr("\u9690\u85CF", "Hide"), "aria-label": `${mark.hidden ? tr("\u663E\u793A", "Show") : tr("\u9690\u85CF", "Hide")} ${mark.label}`, onClick: () => onEdit(mark.id, { hidden: !mark.hidden }) }, /* @__PURE__ */ React12.createElement(UiIcon, { name: mark.hidden ? "eyeOff" : "eye", size: 14 })), /* @__PURE__ */ React12.createElement("button", { type: "button", className: mark.locked ? "on" : "", "aria-pressed": Boolean(mark.locked), disabled: locked, title: mark.locked ? tr("\u89E3\u9501", "Unlock") : tr("\u9501\u5B9A", "Lock"), "aria-label": `${mark.locked ? tr("\u89E3\u9501", "Unlock") : tr("\u9501\u5B9A", "Lock")} ${mark.label}`, onClick: () => onEdit(mark.id, { locked: !mark.locked }) }, /* @__PURE__ */ React12.createElement(UiIcon, { name: mark.locked ? "lock" : "unlock", size: 14 })), /* @__PURE__ */ React12.createElement("button", { type: "button", disabled: locked || mark.locked, title: tr("\u5220\u9664", "Delete"), "aria-label": `${tr("\u5220\u9664\u753B\u7EBF", "Remove line")} ${mark.label}`, onClick: () => onRemove(mark.id) }, /* @__PURE__ */ React12.createElement(UiIcon, { name: "trash", size: 14 })));
19972
20174
  })) : null);
19973
- }
20175
+ });
19974
20176
 
19975
20177
  // src/client/components/sticker-picker.tsx
19976
20178
  var React13 = __toESM(require("react"), 1);
@@ -20426,7 +20628,7 @@ function MarketPicker({ activeCoin, onClose }) {
20426
20628
  const marketRows = React21.useMemo(() => {
20427
20629
  const livePerps = new Map(s.markets.map((market) => [market.name, market]));
20428
20630
  const catalog = s.marketCatalog.length ? s.marketCatalog.map((row) => {
20429
- const latest = row.tradable ? livePerps.get(row.name) : null;
20631
+ const latest = row.kind === "perp" ? livePerps.get(row.name) : null;
20430
20632
  return latest ? { ...row, ctx: latest.ctx } : row;
20431
20633
  }) : s.markets.map((row) => ({
20432
20634
  name: row.name,
@@ -20540,7 +20742,7 @@ function MarketPicker({ activeCoin, onClose }) {
20540
20742
  }
20541
20743
  };
20542
20744
  const header = (label, key3) => /* @__PURE__ */ React21.createElement("button", { type: "button", onClick: () => sortBy(key3), "aria-label": tr(`\u6309${label}\u6392\u5E8F`, `Sort by ${label}`) }, label, sort === key3 ? /* @__PURE__ */ React21.createElement(UiIcon, { name: descending ? "arrowDown" : "arrowUp", size: 12 }) : null);
20543
- return /* @__PURE__ */ React21.createElement(import_dsh_client_ui_primitives3.Modal, { open: true, onClose, title: t("market.choose", "\u9009\u62E9\u5E02\u573A"), headless: true, className: "dhl-picker-modal", backdropBlur: true, onKeyDownCapture: onKeyDown }, /* @__PURE__ */ React21.createElement("section", { className: "dhl-picker" }, /* @__PURE__ */ React21.createElement("div", { className: "dhl-picker-top" }, /* @__PURE__ */ React21.createElement("div", { className: "dhl-picker-search" }, /* @__PURE__ */ React21.createElement(SearchIcon, null), /* @__PURE__ */ React21.createElement("input", { ref: searchRef, "data-modal-autofocus": true, value: query, onChange: (event) => setQuery(event.target.value), placeholder: t("market.search", "\u641C\u7D22\u5E02\u573A"), "aria-label": t("market.search", "\u641C\u7D22\u5E02\u573A") }), /* @__PURE__ */ React21.createElement("kbd", null, "/")), /* @__PURE__ */ React21.createElement("div", { className: "dhl-picker-mode", role: "group", "aria-label": tr("\u5E02\u573A\u8303\u56F4", "Market scope") }, /* @__PURE__ */ React21.createElement("button", { type: "button", className: strict ? "on" : "", "aria-pressed": strict, onClick: () => setStrict(true) }, t("market.tradable", "\u53EF\u4EA4\u6613")), /* @__PURE__ */ React21.createElement("button", { type: "button", className: !strict ? "on" : "", "aria-pressed": !strict, onClick: () => setStrict(false) }, t("market.all", "\u5168\u90E8"))), /* @__PURE__ */ React21.createElement("button", { type: "button", className: "dhl-picker-close", onClick: onClose, "aria-label": t("modal.close", "\u5173\u95ED") }, /* @__PURE__ */ React21.createElement(UiIcon, { name: "close", size: 18 }))), !strict ? /* @__PURE__ */ React21.createElement("p", { className: "dhl-picker-readonly-note" }, tr("\u201C\u53EA\u8BFB\u201D\u8868\u793A\u672C\u63D2\u4EF6\u4EC5\u63D0\u4F9B\u884C\u60C5\uFF0C\u5C1A\u672A\u63A5\u5165\u8BE5\u5E02\u573A\u7684\u4E0B\u5355\u4E0E\u98CE\u63A7\uFF1BHIP-3 \u548C\u73B0\u8D27\u5728 Hyperliquid \u672C\u8EAB\u53EF\u4EA4\u6613\u3002", "\u201CView only\u201D means this plugin has market data but no order and risk support for that market. Hyperliquid itself supports HIP-3 and spot trading.")) : null, /* @__PURE__ */ React21.createElement("nav", { className: "dhl-picker-tabs", "aria-label": tr("\u5E02\u573A\u5206\u7C7B", "Market categories") }, CATEGORIES.map(([key3, label]) => /* @__PURE__ */ React21.createElement("button", { type: "button", key: key3, className: category === key3 ? "on" : "", "aria-current": category === key3 ? "page" : void 0, onClick: () => {
20745
+ return /* @__PURE__ */ React21.createElement(import_dsh_client_ui_primitives3.Modal, { open: true, onClose, title: t("market.choose", "\u9009\u62E9\u5E02\u573A"), headless: true, className: "dhl-picker-modal", backdropBlur: true, onKeyDownCapture: onKeyDown }, /* @__PURE__ */ React21.createElement("section", { className: "dhl-picker" }, /* @__PURE__ */ React21.createElement("div", { className: "dhl-picker-top" }, /* @__PURE__ */ React21.createElement("div", { className: "dhl-picker-search" }, /* @__PURE__ */ React21.createElement(SearchIcon, null), /* @__PURE__ */ React21.createElement("input", { ref: searchRef, "data-modal-autofocus": true, value: query, onChange: (event) => setQuery(event.target.value), placeholder: t("market.search", "\u641C\u7D22\u5E02\u573A"), "aria-label": t("market.search", "\u641C\u7D22\u5E02\u573A") }), /* @__PURE__ */ React21.createElement("kbd", null, "/")), /* @__PURE__ */ React21.createElement("div", { className: "dhl-picker-mode", role: "group", "aria-label": tr("\u5E02\u573A\u8303\u56F4", "Market scope") }, /* @__PURE__ */ React21.createElement("button", { type: "button", className: strict ? "on" : "", "aria-pressed": strict, onClick: () => setStrict(true) }, t("market.tradable", "\u53EF\u4EA4\u6613")), /* @__PURE__ */ React21.createElement("button", { type: "button", className: !strict ? "on" : "", "aria-pressed": !strict, onClick: () => setStrict(false) }, t("market.all", "\u5168\u90E8"))), /* @__PURE__ */ React21.createElement("button", { type: "button", className: "dhl-picker-close", onClick: onClose, "aria-label": t("modal.close", "\u5173\u95ED") }, /* @__PURE__ */ React21.createElement(UiIcon, { name: "close", size: 18 }))), !strict ? /* @__PURE__ */ React21.createElement("p", { className: "dhl-picker-readonly-note" }, tr("\u201C\u53EA\u8BFB\u201D\u8868\u793A\u672C\u63D2\u4EF6\u4EC5\u63D0\u4F9B\u884C\u60C5\uFF0C\u5C1A\u672A\u63A5\u5165\u8BE5\u5E02\u573A\u7684\u4E0B\u5355\u4E0E\u98CE\u63A7\uFF1B\u73B0\u8D27\u4E0E Outcome \u5728 Hyperliquid \u672C\u8EAB\u53EF\u4EA4\u6613\uFF1B\u4EE5 USDC \u4E3A\u4FDD\u8BC1\u91D1\u7684 HIP-3 \u6C38\u7EED\u5DF2\u53EF\u5728\u8FD9\u91CC\u4EA4\u6613\u3002", "\u201CView only\u201D means this plugin has market data but no order and risk support for that market. Hyperliquid itself supports spot and outcome trading; USDC-collateral HIP-3 perps are tradable here.")) : null, /* @__PURE__ */ React21.createElement("nav", { className: "dhl-picker-tabs", "aria-label": tr("\u5E02\u573A\u5206\u7C7B", "Market categories") }, CATEGORIES.map(([key3, label]) => /* @__PURE__ */ React21.createElement("button", { type: "button", key: key3, className: category === key3 ? "on" : "", "aria-current": category === key3 ? "page" : void 0, onClick: () => {
20544
20746
  setCategory(key3);
20545
20747
  if (["spot", "hip3", "outcome", "crypto", "tradfi", "preipo"].includes(key3)) setStrict(false);
20546
20748
  } }, t(`market.category.${key3}`, label)))), /* @__PURE__ */ React21.createElement("div", { className: "dhl-picker-tablehead" }, /* @__PURE__ */ React21.createElement(UiIcon, { name: "star", size: 15 }), header(t("market.name", "\u5E02\u573A"), "name"), header(t("market.price", "\u6700\u65B0\u4EF7"), "price"), header(t("market.change", "24h \u6DA8\u8DCC"), "change"), header(t("market.funding", "\u8D44\u91D1\u8D39"), "funding"), header(t("market.volume", "24h \u6210\u4EA4\u989D"), "volume"), header(t("market.openInterest", "\u672A\u5E73\u4ED3\u91CF"), "openInterest")), /* @__PURE__ */ React21.createElement("div", { className: "dhl-picker-list", role: "listbox", "aria-label": tr("\u5E02\u573A\u5217\u8868", "Market list"), ref: listRef, onScroll: (event) => {
@@ -20615,8 +20817,8 @@ function atr14(rows) {
20615
20817
  for (const item of ranges.slice(14)) value = (value * 13 + item) / 14;
20616
20818
  return value;
20617
20819
  }
20618
- function evaluateStrategyStudies(candles, coin, interval, now = Date.now(), english = false) {
20619
- const say = (zh, en) => english ? en : zh;
20820
+ function evaluateStrategyStudies(candles, coin, interval, now = Date.now(), english2 = false) {
20821
+ const say = (zh, en) => english2 ? en : zh;
20620
20822
  const valid = candles.filter((row) => Number.isFinite(row.t) && Number.isFinite(row.c) && row.c > 0 && Number.isFinite(row.h) && Number.isFinite(row.l) && row.h >= row.l && Number.isFinite(row.v));
20621
20823
  const rows = [...new Map(valid.map((row) => [row.t, row])).values()].sort((a, b) => a.t - b.t).filter((row) => closedAt(row, interval) <= now);
20622
20824
  const latest = rows.at(-1);
@@ -20712,8 +20914,8 @@ var STATE_LABELS = {
20712
20914
  };
20713
20915
  function StrategyMenu({ candles, coin, interval }) {
20714
20916
  const menu = React22.useRef(null);
20715
- const english = tr("zh", "en") === "en";
20716
- const studies = React22.useMemo(() => evaluateStrategyStudies(candles, coin, interval, Date.now(), english), [candles, coin, interval, english]);
20917
+ const english2 = tr("zh", "en") === "en";
20918
+ const studies = React22.useMemo(() => evaluateStrategyStudies(candles, coin, interval, Date.now(), english2), [candles, coin, interval, english2]);
20717
20919
  return /* @__PURE__ */ React22.createElement("details", { className: "dhl-strategy-menu", ref: menu }, /* @__PURE__ */ React22.createElement("summary", null, tr("\u7B56\u7565\u6846\u67B6", "Strategies"), /* @__PURE__ */ React22.createElement(UiIcon, { name: "chevronDown", size: 12 })), /* @__PURE__ */ React22.createElement("div", { className: "dhl-strategy-popover", role: "group", "aria-label": tr("\u4EA4\u6613\u7B56\u7565\u7814\u7A76", "Trading strategy studies") }, /* @__PURE__ */ React22.createElement("header", null, /* @__PURE__ */ React22.createElement("strong", null, tr("\u7B56\u7565\u7814\u7A76 \xB7 \u5DF2\u6536\u76D8 K \u7EBF", "Strategy studies \xB7 closed candles")), /* @__PURE__ */ React22.createElement("small", null, coin, " \xB7 ", interval)), /* @__PURE__ */ React22.createElement("p", { className: "dhl-strategy-caveat" }, tr(
20718
20920
  "\u8FD9\u4E9B\u6761\u4EF6\u7528\u4E8E\u7EC4\u7EC7\u5206\u6790\uFF0C\u672A\u5728\u672C\u5E02\u573A\u548C\u5468\u671F\u9A8C\u8BC1\u6536\u76CA\uFF1B\u9700\u8981\u6838\u5BF9\u66F4\u9AD8\u5468\u671F\u3001\u76D8\u53E3\u3001\u8D44\u91D1\u8D39\u548C\u4EA4\u6613\u6210\u672C\u3002",
20719
20921
  "These conditions structure analysis; returns are unverified for this market and interval. Check higher timeframe, book, funding and costs."
@@ -20803,7 +21005,7 @@ function IndicatorEditor({ settings, indicators, maLines, onApply }) {
20803
21005
  const [name, setName] = React23.useState("");
20804
21006
  const [error, setError] = React23.useState("");
20805
21007
  const [busy, setBusy] = React23.useState(false);
20806
- const populate = (value) => setDraft(Object.fromEntries(Object.entries(normalizeIndicatorSettings(value)).map(([section, fields]) => [section, Object.fromEntries(Object.entries(fields).map(([key3, n2]) => [key3, String(n2)]))])));
21008
+ const populate = (value) => setDraft(Object.fromEntries(Object.entries(normalizeIndicatorSettings(value)).map(([section, fields2]) => [section, Object.fromEntries(Object.entries(fields2).map(([key3, n2]) => [key3, String(n2)]))])));
20807
21009
  React23.useEffect(() => {
20808
21010
  if (!open) return;
20809
21011
  populate(settings);
@@ -20822,7 +21024,7 @@ function IndicatorEditor({ settings, indicators, maLines, onApply }) {
20822
21024
  cancelled = true;
20823
21025
  };
20824
21026
  }, [open]);
20825
- const values = () => normalizeIndicatorSettings(Object.fromEntries(Object.entries(draft).map(([section, fields]) => [section, Object.fromEntries(Object.entries(fields).map(([key3, n2]) => [key3, n2.trim() ? Number(n2) : NaN]))])));
21027
+ const values = () => normalizeIndicatorSettings(Object.fromEntries(Object.entries(draft).map(([section, fields2]) => [section, Object.fromEntries(Object.entries(fields2).map(([key3, n2]) => [key3, n2.trim() ? Number(n2) : NaN]))])));
20826
21028
  const apply2 = () => {
20827
21029
  try {
20828
21030
  onApply({ indicatorSettings: values(), indicators: active, maLines: lines });
@@ -20871,7 +21073,7 @@ function IndicatorEditor({ settings, indicators, maLines, onApply }) {
20871
21073
  setTemplates(result.templates);
20872
21074
  setSelected("");
20873
21075
  }).catch((cause) => setError(String(cause.message))).finally(() => setBusy(false));
20874
- } }, tr("\u5220\u9664\u6A21\u677F", "Delete")))), /* @__PURE__ */ React23.createElement("div", { className: "dhl-study-parameter-grid" }, SECTIONS.map(([section, title, fields]) => /* @__PURE__ */ React23.createElement("fieldset", { key: section }, /* @__PURE__ */ React23.createElement("legend", null, title), fields.map(([key3, zh, en]) => /* @__PURE__ */ React23.createElement("label", { key: key3 }, tr(zh, en), /* @__PURE__ */ React23.createElement(import_dsh_client_ui_primitives4.Input, { type: "number", step: key3 === "multiplier" ? "any" : 1, "aria-label": `${title} ${tr(zh, en)}`, value: draft[section]?.[key3] ?? "", onChange: (event) => setDraft((current2) => ({ ...current2, [section]: { ...current2[section], [key3]: event.target.value } })) })))))), error ? /* @__PURE__ */ React23.createElement("p", { className: "dhl-inline-error", role: "alert" }, error) : null)
21076
+ } }, tr("\u5220\u9664\u6A21\u677F", "Delete")))), /* @__PURE__ */ React23.createElement("div", { className: "dhl-study-parameter-grid" }, SECTIONS.map(([section, title, fields2]) => /* @__PURE__ */ React23.createElement("fieldset", { key: section }, /* @__PURE__ */ React23.createElement("legend", null, title), fields2.map(([key3, zh, en]) => /* @__PURE__ */ React23.createElement("label", { key: key3 }, tr(zh, en), /* @__PURE__ */ React23.createElement(import_dsh_client_ui_primitives4.Input, { type: "number", step: key3 === "multiplier" ? "any" : 1, "aria-label": `${title} ${tr(zh, en)}`, value: draft[section]?.[key3] ?? "", onChange: (event) => setDraft((current2) => ({ ...current2, [section]: { ...current2[section], [key3]: event.target.value } })) })))))), error ? /* @__PURE__ */ React23.createElement("p", { className: "dhl-inline-error", role: "alert" }, error) : null)
20875
21077
  ));
20876
21078
  }
20877
21079
 
@@ -20885,7 +21087,7 @@ function ResearchPane({ coin, interval, onInterval, indicators, marks, link, cha
20885
21087
  const [state, setState] = React24.useState({ scope: "", candles: [], error: "", older: "idle", at: 0 });
20886
21088
  const currentScope = React24.useRef(scope);
20887
21089
  currentScope.current = scope;
20888
- const feed = state.scope === scope ? state : { scope, candles: [], error: "", older: "idle", at: 0 };
21090
+ const feed2 = state.scope === scope ? state : { scope, candles: [], error: "", older: "idle", at: 0 };
20889
21091
  React24.useEffect(() => {
20890
21092
  let alive = true, busy = false;
20891
21093
  setState({ scope, candles: [], error: "", older: "idle", at: 0 });
@@ -20910,11 +21112,11 @@ function ResearchPane({ coin, interval, onInterval, indicators, marks, link, cha
20910
21112
  }, [scope]);
20911
21113
  const olderBusy = React24.useRef(false);
20912
21114
  const needOlder = async () => {
20913
- if (olderBusy.current || feed.older !== "idle" || !feed.candles.length) return;
21115
+ if (olderBusy.current || feed2.older !== "idle" || !feed2.candles.length) return;
20914
21116
  olderBusy.current = true;
20915
21117
  setState((old) => ({ ...old, older: "loading" }));
20916
21118
  try {
20917
- const first = feed.candles[0].t;
21119
+ const first = feed2.candles[0].t;
20918
21120
  const result = await hyperCall("candles", { coin, interval, endMs: first - 1, startMs: first - durations[RESEARCH_INTERVALS.indexOf(interval)] * 500 });
20919
21121
  if (currentScope.current === scope) setState((old) => {
20920
21122
  const rows = merge2(result.candles, old.candles);
@@ -20927,12 +21129,12 @@ function ResearchPane({ coin, interval, onInterval, indicators, marks, link, cha
20927
21129
  }
20928
21130
  };
20929
21131
  const layers = { orders: true, fills: true, reasons: false, ...s.workspace?.chartLayers };
20930
- const events = React24.useMemo(() => chartTradeEvents(feed.candles, coin, layers.orders ? s.tradeHistory?.orders ?? [] : [], layers.fills ? s.tradeHistory?.fills ?? [] : []), [feed.candles, s.tradeHistory, layers.orders, layers.fills, coin]);
20931
- return /* @__PURE__ */ React24.createElement("section", { className: "dhl-research-pane", "aria-label": `${coin} ${interval} ${tr("\u7814\u7A76\u56FE\u8868", "research chart")}` }, /* @__PURE__ */ React24.createElement("header", null, /* @__PURE__ */ React24.createElement("b", null, coin), /* @__PURE__ */ React24.createElement(Select, { "aria-label": tr("\u7814\u7A76\u5468\u671F", "Research interval"), value: interval, onChange: (e) => onInterval(e.target.value) }, RESEARCH_INTERVALS.map((iv) => /* @__PURE__ */ React24.createElement("option", { key: iv, value: iv }, iv))), /* @__PURE__ */ React24.createElement("small", null, feed.at ? `${tr("\u66F4\u65B0", "Updated")} ${new Date(feed.at).toLocaleTimeString()}` : tr("\u52A0\u8F7D\u4E2D\u2026", "Loading\u2026"))), /* @__PURE__ */ React24.createElement(
21132
+ const events = React24.useMemo(() => chartTradeEvents(feed2.candles, coin, layers.orders ? s.tradeHistory?.orders ?? [] : [], layers.fills ? s.tradeHistory?.fills ?? [] : []), [feed2.candles, s.tradeHistory, layers.orders, layers.fills, coin]);
21133
+ return /* @__PURE__ */ React24.createElement("section", { className: "dhl-research-pane", "aria-label": `${coin} ${interval} ${tr("\u7814\u7A76\u56FE\u8868", "research chart")}` }, /* @__PURE__ */ React24.createElement("header", null, /* @__PURE__ */ React24.createElement("b", null, coin), /* @__PURE__ */ React24.createElement(Select, { "aria-label": tr("\u7814\u7A76\u5468\u671F", "Research interval"), value: interval, onChange: (e) => onInterval(e.target.value) }, RESEARCH_INTERVALS.map((iv) => /* @__PURE__ */ React24.createElement("option", { key: iv, value: iv }, iv))), /* @__PURE__ */ React24.createElement("small", null, feed2.at ? `${tr("\u66F4\u65B0", "Updated")} ${new Date(feed2.at).toLocaleTimeString()}` : tr("\u52A0\u8F7D\u4E2D\u2026", "Loading\u2026"))), /* @__PURE__ */ React24.createElement(
20932
21134
  CandleChart,
20933
21135
  {
20934
21136
  key: scope,
20935
- candles: feed.candles,
21137
+ candles: feed2.candles,
20936
21138
  marks,
20937
21139
  indicators,
20938
21140
  expanded: false,
@@ -20953,7 +21155,7 @@ function ResearchPane({ coin, interval, onInterval, indicators, marks, link, cha
20953
21155
  },
20954
21156
  onSelect: (selection) => insertChartReference(selection, coin, interval),
20955
21157
  onNeedOlder: () => void needOlder(),
20956
- olderState: feed.older,
21158
+ olderState: feed2.older,
20957
21159
  tradeEvents: events,
20958
21160
  showReasons: layers.fills && layers.reasons,
20959
21161
  onAnalyzeFill,
@@ -20968,7 +21170,7 @@ function ResearchPane({ coin, interval, onInterval, indicators, marks, link, cha
20968
21170
  selectRequest: null,
20969
21171
  focus: null
20970
21172
  }
20971
- ), feed.error ? /* @__PURE__ */ React24.createElement("button", { type: "button", className: "dhl-inline-error", onClick: () => setState((old) => ({ ...old, older: "idle" })) }, feed.error, " \xB7 ", tr("\u81EA\u52A8\u91CD\u8BD5\u4E2D\uFF1B\u70B9\u51FB\u91CD\u8BD5\u5386\u53F2", "Retrying; click to retry history")) : null);
21173
+ ), feed2.error ? /* @__PURE__ */ React24.createElement("button", { type: "button", className: "dhl-inline-error", onClick: () => setState((old) => ({ ...old, older: "idle" })) }, feed2.error, " \xB7 ", tr("\u81EA\u52A8\u91CD\u8BD5\u4E2D\uFF1B\u70B9\u51FB\u91CD\u8BD5\u5386\u53F2", "Retrying; click to retry history")) : null);
20972
21174
  }
20973
21175
 
20974
21176
  // lib/workspace-settings.js
@@ -21180,12 +21382,22 @@ function DeskColumn({ onCollapse }) {
21180
21382
  }, []);
21181
21383
  const editMark = React26.useCallback((id, patch) => historyRef.current.edit(id, patch), []);
21182
21384
  const erase = React26.useCallback((id) => historyRef.current.remove(id), []);
21385
+ const selectMark = React26.useCallback((id) => setSelectRequest({ id, at: Date.now() }), []);
21183
21386
  const cloneMark = React26.useCallback((copy) => {
21184
21387
  void historyRef.current.create({ kind: copy.kind, label: copy.label, ...copy.geometry, ...copy.style });
21185
21388
  }, []);
21389
+ const onSecondaryChart = React26.useRef(false);
21390
+ React26.useEffect(() => {
21391
+ const track = (event) => {
21392
+ const target = event.target;
21393
+ onSecondaryChart.current = Boolean(target?.closest?.(".dhl-research-pane") && !target.closest(".dhl-primary-pane"));
21394
+ };
21395
+ window.addEventListener("pointerdown", track, true);
21396
+ return () => window.removeEventListener("pointerdown", track, true);
21397
+ }, []);
21186
21398
  React26.useEffect(() => {
21187
21399
  const onKey = (event) => {
21188
- if (!(event.metaKey || event.ctrlKey) || event.altKey) return;
21400
+ if (!(event.metaKey || event.ctrlKey) || event.altKey || onSecondaryChart.current) return;
21189
21401
  const element = event.target;
21190
21402
  if (element?.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(element?.tagName ?? "")) return;
21191
21403
  const key3 = event.key.toLowerCase();
@@ -21225,7 +21437,7 @@ function DeskColumn({ onCollapse }) {
21225
21437
  React26.useEffect(() => {
21226
21438
  const keys = { KeyT: "trend", KeyH: "level", KeyJ: "hray", KeyV: "vline", KeyC: "crossline", KeyF: "fib" };
21227
21439
  const onKey = (event) => {
21228
- if (!event.altKey || event.ctrlKey || event.metaKey || chartTab !== "chart") return;
21440
+ if (!event.altKey || event.ctrlKey || event.metaKey || chartTab !== "chart" || onSecondaryChart.current) return;
21229
21441
  const element = event.target;
21230
21442
  if (element?.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(element?.tagName ?? "")) return;
21231
21443
  const kind = event.shiftKey ? event.code === "KeyR" ? "rect" : void 0 : keys[event.code];
@@ -21421,9 +21633,10 @@ function DeskColumn({ onCollapse }) {
21421
21633
  {
21422
21634
  marks,
21423
21635
  locked: drawingsLocked,
21424
- onSelect: (id) => setSelectRequest({ id, at: Date.now() }),
21425
- onEdit: (id, patch) => void editMark(id, patch),
21426
- onRemove: (id) => void erase(id)
21636
+ language: tr("zh", "en"),
21637
+ onSelect: selectMark,
21638
+ onEdit: editMark,
21639
+ onRemove: erase
21427
21640
  }
21428
21641
  ) : null, /* @__PURE__ */ React26.createElement(
21429
21642
  ChartNavigation,
@@ -21638,8 +21851,8 @@ var React28 = __toESM(require("react"), 1);
21638
21851
  function TradeTicket({ onSubmitted }) {
21639
21852
  const s = useStore();
21640
21853
  const coin = s.activeCoin;
21641
- const market = s.markets.find((row) => row.name === coin);
21642
21854
  const catalogMarket = s.marketCatalog.find((row) => row.name === coin);
21855
+ const market = s.markets.find((row) => row.name === coin) ?? (catalogMarket?.tradable ? catalogMarket : void 0);
21643
21856
  const liveMid = s.snapshot?.coin === coin && Date.now() - Number(s.snapshot?.observedAt?.mids) < 1e4 ? Number(s.snapshot?.mids?.mids?.[coin] ?? s.snapshot?.mids?.[coin]) : 0;
21644
21857
  const mark = liveMid || Number(s.selectedMarket?.name === coin ? s.selectedMarket.ctx?.markPx : 0) || Number(market?.ctx?.markPx);
21645
21858
  const position = s.account?.positions?.find((p) => p.coin === coin);
@@ -21874,20 +22087,34 @@ function ChatColumn({ width, fill = false, onCollapse, SessionProvider, renderFa
21874
22087
  requestAnimationFrame(() => input.focus?.());
21875
22088
  }
21876
22089
  }, [activeId, readyRef, showTicket, s.chartReferences]);
21877
- const askMarket = React29.useCallback(() => {
21878
- queueToAgent(tr(
21879
- `\u8BF7\u8C03\u7528 hyper_context \u8BFB\u53D6\u5F53\u524D\u4EA4\u6613\u53F0\u7684 ${s.activeCoin} \u5B9E\u65F6\u884C\u60C5\u3001\u76D8\u53E3\u548C\u8D26\u6237\u4FE1\u606F\uFF0C\u6982\u62EC\u5E02\u573A\u72B6\u6001\u3001\u5173\u952E\u98CE\u9669\u4E0E\u53EF\u80FD\u7684\u4EA4\u6613\u6761\u4EF6\u3002\u4EC5\u5206\u6790\uFF0C\u4E0D\u8981\u4E0B\u5355\u3002`,
21880
- `Use hyper_context to read live ${s.activeCoin} market data, order book and account state. Summarize conditions, key risks and possible trade criteria. Analysis only; do not place an order.`
21881
- ));
21882
- }, [s.activeCoin]);
21883
- const askRisk = React29.useCallback(() => {
21884
- queueToAgent(tr(
21885
- `\u8BF7\u8C03\u7528 hyper_context \u68C0\u67E5\u5F53\u524D\u4EA4\u6613\u4F1A\u8BDD\u7684\u8D26\u6237\u3001${s.activeCoin} \u4ED3\u4F4D\u3001\u6760\u6746\u3001\u6302\u5355\u548C\u98CE\u9669\u9650\u989D\uFF0C\u6307\u51FA\u9700\u8981\u6CE8\u610F\u7684\u98CE\u9669\u4E0E\u53EF\u91C7\u53D6\u7684\u7BA1\u7406\u52A8\u4F5C\u3002\u4EC5\u5206\u6790\uFF0C\u4E0D\u8981\u4E0B\u5355\u6216\u64A4\u5355\u3002`,
21886
- `Use hyper_context to review the account, ${s.activeCoin} position, leverage, open orders and risk limits. Identify risks and possible management actions. Analysis only; do not place or cancel orders.`
21887
- ));
21888
- }, [s.activeCoin]);
22090
+ const QUICK = [
22091
+ [
22092
+ "\u4EA4\u6613\u4F53\u68C0",
22093
+ "Trading check-up",
22094
+ "\u8BF7\u7528 hyper_account action=analyze \u7ED9\u6211\u505A\u4E00\u6B21\u4EA4\u6613\u4F53\u68C0\uFF08\u8FD1 90 \u5929\uFF09\uFF1A\u5148\u8BB2\u6700\u503C\u94B1\u7684 1\u20132 \u4E2A\u53D1\u73B0\u548C\u5EFA\u8BAE\u89C4\u5219\uFF0C\u518D\u95EE\u6211\u662F\u5426\u91C7\u7528\u3002\u4E0D\u8981\u4E0B\u5355\u3002",
22095
+ "Run a trading check-up with hyper_account action=analyze (last 90 days): lead with the 1\u20132 most valuable findings and suggested rules, then ask whether I want to adopt them. Do not trade."
22096
+ ],
22097
+ [
22098
+ "\u4ECA\u5929\u6709\u4EC0\u4E48\u673A\u4F1A",
22099
+ "Today's opportunities",
22100
+ "\u8BF7\u7528 hyper_market action=scan \u770B\u770B\u4ECA\u5929\u7684\u673A\u4F1A\uFF0C\u7ED3\u5408\u6211\u7684\u6863\u6848\u6311 1 \u4E2A\u6700\u503C\u5F97\u5173\u6CE8\u7684\uFF0C\u7528 hyper_market candles/indicators \u6838\u5B9E\uFF0C\u518D\u7528 hyper_chart \u753B\u51FA\u5173\u952E\u4F4D\u548C\u5934\u5BF8\u6846\uFF0C\u6309\u4EA4\u6613\u5361\u683C\u5F0F\u7ED9\u51FA\u8BA1\u5212\u3002\u4E0D\u8981\u4E0B\u5355\u3002",
22101
+ "Use hyper_market action=scan for today's opportunities, pick the one that best fits my profile, verify it with hyper_market candles/indicators, draw the levels and a position box with hyper_chart, and give the plan as a trade card. Do not trade."
22102
+ ],
22103
+ [
22104
+ "\u5E2E\u6211\u76EF\u7740\u4ED3\u4F4D",
22105
+ "Watch my positions",
22106
+ "\u8BF7\u7528 hyper_context \u68C0\u67E5\u6211\u7684\u4ED3\u4F4D\u548C\u6302\u5355\uFF1A\u6BCF\u4E2A\u4ED3\u4F4D\u662F\u5426\u6709\u8DB3\u91CF\u6B62\u635F\u3001\u79BB\u6B62\u635F\u548C\u76EE\u6807\u591A\u8FDC\u3001\u8D44\u91D1\u8D39\u6210\u672C\uFF1B\u5728\u5173\u952E\u4EF7\u4F4D\u7528 hyper_chart \u8BBE\u4EF7\u683C\u63D0\u9192\uFF08alert=true\uFF09\uFF1B\u9700\u8981\u5B9A\u671F\u68C0\u67E5\u7684\u8BDD\u5148\u95EE\u6211\u4E00\u6B21\uFF0C\u518D\u7528 schedule_create \u5B89\u6392\u3002\u4E0D\u8981\u4E0B\u5355\u6216\u64A4\u5355\u3002",
22107
+ "Use hyper_context to check my positions and orders: is each one covered by a stop, how far are stop and target, what does funding cost; set price alerts (alert=true) at key levels with hyper_chart; if periodic checks would help, ask me once, then use schedule_create. Do not place or cancel orders."
22108
+ ],
22109
+ [
22110
+ "\u628A\u56FE\u4E0A\u753B\u7EBF\u53D8\u6210\u7B56\u7565",
22111
+ "Turn my drawings into a strategy",
22112
+ `\u8BF7\u7528 hyper_chart action=list \u8BFB\u53D6 ${s.activeCoin} \u56FE\u4E0A\u7684\u753B\u7EBF\uFF0C\u7ED3\u5408 hyper_market candles \u7406\u89E3\u6211\u7684\u601D\u8DEF\uFF0C\u6574\u7406\u6210\u4E00\u6761\u53EF\u6838\u5BF9\u7684\u7B56\u7565\uFF08\u4F9D\u636E\u3001\u5165\u573A\u3001\u79BB\u573A\u3001\u4F5C\u5E9F\u3001\u4ED3\u4F4D\uFF09\uFF0C\u7528 hyper_journal action=strategy \u5B58\u4E3A idea\uFF0C\u5E76\u8BF4\u660E\u8FD8\u9700\u8981\u9A8C\u8BC1\u4EC0\u4E48\u3002\u4E0D\u8981\u4E0B\u5355\u3002`,
22113
+ `Read my drawings on ${s.activeCoin} with hyper_chart action=list, use hyper_market candles to understand the idea, write it up as a checkable strategy (thesis, entry, exit, invalidation, sizing), save it with hyper_journal action=strategy as an idea, and say what still needs verifying. Do not trade.`
22114
+ ]
22115
+ ];
21889
22116
  const content = !activeId ? /* @__PURE__ */ React29.createElement("div", { className: "dhl-empty" }, t("chat.noSession", "\u9009\u62E9\u5DE6\u4FA7\u4EA4\u6613\u4F1A\u8BDD\uFF0C\u5728\u8FD9\u91CC\u76F4\u63A5\u4E0E\u4EA4\u6613 agent \u5BF9\u8BDD\u3002")) : bindError ? /* @__PURE__ */ React29.createElement("div", { className: "dhl-inline-error", role: "alert" }, t("chat.bindError", "\u4F1A\u8BDD\u7ED1\u5B9A\u5931\u8D25\uFF1A") + bindError) : legacy ? /* @__PURE__ */ React29.createElement("div", { className: "dhl-chat-unavailable", role: "alert" }, t("chat.upgrade", "\u5F53\u524D DSH \u7248\u672C\u4E0D\u652F\u6301\u5D4C\u5165\u539F\u751F\u4F1A\u8BDD\uFF1B\u8BF7\u4F7F\u7528 DSH 0.2 \u6216\u66F4\u65B0\u7248\u672C\u3002")) : !readyRef ? /* @__PURE__ */ React29.createElement("div", { className: "dhl-empty" }, t("chat.linking", "\u6B63\u5728\u63A5\u5165\u4F1A\u8BDD\u2026")) : !SessionProvider || !renderFactorySlot ? /* @__PURE__ */ React29.createElement("div", { className: "dhl-inline-error", role: "alert" }, t("chat.noFactory", "DSH \u539F\u751F\u4F1A\u8BDD\u7EC4\u4EF6\u4E0D\u53EF\u7528\u3002")) : /* @__PURE__ */ React29.createElement("div", { className: "dhl-chatmount", "data-trading-session": activeId }, /* @__PURE__ */ React29.createElement(SessionProvider, { session: readyRef }, renderFactorySlot("conversation.content", { variant: "embedded", phase: "active", hero: false }, { slots: { views: FixedChatView } })));
21890
- return /* @__PURE__ */ React29.createElement("div", { id: "dhl-panel-chat", className: "dhl-col-chat", style: fill ? { flex: "1 1 0", width: "auto" } : width ? { width } : void 0 }, /* @__PURE__ */ React29.createElement("div", { className: "dhl-chat-header" }, /* @__PURE__ */ React29.createElement("div", { className: "dhl-chat-heading" }, /* @__PURE__ */ React29.createElement("strong", null, t("chat.title", "\u4EA4\u6613\u4F1A\u8BDD")), activeId ? /* @__PURE__ */ React29.createElement("span", { title }, title) : null), /* @__PURE__ */ React29.createElement("button", { type: "button", className: "dhl-chat-ticket-toggle", "aria-expanded": showTicket, onClick: () => setShowTicket((value) => !value) }, showTicket ? t("chat.back", "\u8FD4\u56DE\u4F1A\u8BDD") : t("chat.ticket", "\u4EA4\u6613\u7968\u636E")), /* @__PURE__ */ React29.createElement("button", { className: "dhl-panel-iconbtn", type: "button", onClick: onCollapse, "aria-label": tr("\u6536\u8D77\u5BF9\u8BDD\u680F", "Collapse chat"), title: tr("\u6536\u8D77\u5BF9\u8BDD\u680F", "Collapse chat") }, /* @__PURE__ */ React29.createElement(FoldIcon, { direction: "right" }))), activeId && !showTicket && !legacy ? /* @__PURE__ */ React29.createElement("div", { className: "dhl-chat-prompts", role: "group", "aria-label": t("chat.prompts", "\u5FEB\u6377\u63D0\u95EE") }, /* @__PURE__ */ React29.createElement("button", { type: "button", onClick: askMarket }, t("chat.analyze", "\u5206\u6790\u884C\u60C5")), /* @__PURE__ */ React29.createElement("button", { type: "button", onClick: askRisk }, t("chat.risk", "\u68C0\u67E5\u98CE\u9669"))) : null, showTicket ? /* @__PURE__ */ React29.createElement(TradeTicket, { key: activeId ?? "anonymous", onSubmitted: () => setShowTicket(false) }) : content);
22117
+ return /* @__PURE__ */ React29.createElement("div", { id: "dhl-panel-chat", className: "dhl-col-chat", style: fill ? { flex: "1 1 0", width: "auto" } : width ? { width } : void 0 }, /* @__PURE__ */ React29.createElement("div", { className: "dhl-chat-header" }, /* @__PURE__ */ React29.createElement("div", { className: "dhl-chat-heading" }, /* @__PURE__ */ React29.createElement("strong", null, t("chat.title", "\u4EA4\u6613\u4F1A\u8BDD")), activeId ? /* @__PURE__ */ React29.createElement("span", { title }, title) : null), /* @__PURE__ */ React29.createElement("button", { type: "button", className: "dhl-chat-ticket-toggle", "aria-expanded": showTicket, onClick: () => setShowTicket((value) => !value) }, showTicket ? t("chat.back", "\u8FD4\u56DE\u4F1A\u8BDD") : t("chat.ticket", "\u4EA4\u6613\u7968\u636E")), /* @__PURE__ */ React29.createElement("button", { className: "dhl-panel-iconbtn", type: "button", onClick: onCollapse, "aria-label": tr("\u6536\u8D77\u5BF9\u8BDD\u680F", "Collapse chat"), title: tr("\u6536\u8D77\u5BF9\u8BDD\u680F", "Collapse chat") }, /* @__PURE__ */ React29.createElement(FoldIcon, { direction: "right" }))), activeId && !showTicket && !legacy ? /* @__PURE__ */ React29.createElement("div", { className: "dhl-chat-prompts", role: "group", "aria-label": t("chat.prompts", "\u5FEB\u6377\u63D0\u95EE") }, QUICK.map(([zh, en, askZh, askEn]) => /* @__PURE__ */ React29.createElement("button", { key: zh, type: "button", onClick: () => queueToAgent(tr(askZh, askEn)) }, tr(zh, en)))) : null, showTicket ? /* @__PURE__ */ React29.createElement(TradeTicket, { key: activeId ?? "anonymous", onSubmitted: () => setShowTicket(false) }) : content);
21891
22118
  }
21892
22119
 
21893
22120
  // src/client/components/wallet-details.tsx
@@ -22015,22 +22242,60 @@ function TradingLimits({ open, onClose }) {
22015
22242
  setBusy(false);
22016
22243
  }
22017
22244
  };
22018
- const field = (key3, label, props, wide = false) => /* @__PURE__ */ React31.createElement("label", { className: wide ? "dhl-risk-wide" : void 0 }, /* @__PURE__ */ React31.createElement("span", null, label), /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Input, { className: "dhl-risk-input", value: draft[key3], onChange: (event) => change(key3, event.target.value), required: true, ...props }));
22019
- const footer = limits ? /* @__PURE__ */ React31.createElement(React31.Fragment, null, st?.riskSettingsSource === "desk" ? /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Button, { variant: "outline", disabled: busy, onClick: () => void reset() }, tr("\u6062\u590D\u63D2\u4EF6\u914D\u7F6E", "Restore plugin config")) : null, /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Button, { variant: "primary", type: "submit", form: "dhl-risk-form", disabled: busy }, busy ? tr("\u4FDD\u5B58\u4E2D\u2026", "Saving\u2026") : tr("\u4FDD\u5B58", "Save"))) : null;
22020
- return /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Modal, { open, onClose, title: tr("\u4EA4\u6613\u9650\u989D\u4E0E\u7B7E\u540D\u89C4\u5219", "Trading limits & signing rules"), closeLabel: tr("\u5173\u95ED", "Close"), className: "dhl-limits-modal", footer }, /* @__PURE__ */ React31.createElement("section", { className: "dhl-limits-section" }, /* @__PURE__ */ React31.createElement("h3", null, tr("\u4EA4\u6613\u9650\u989D", "Trading limits"), /* @__PURE__ */ React31.createElement("span", { className: "dhl-limits-ledger" }, paper ? tr("\u6A21\u62DF\u76D8\u8D26\u672C", "Paper ledger") : tr("\u5B9E\u76D8\u8D26\u672C", "Live ledger"))), limits ? /* @__PURE__ */ React31.createElement("form", { id: "dhl-risk-form", className: "dhl-risk-form", onSubmit: (event) => void save(event) }, field("allowedCoins", tr("\u5141\u8BB8\u5F00\u4ED3\u5E01\u5BF9", "Coins allowed to open"), { placeholder: "BTC, ETH", maxLength: 1e3 }, true), field("maxOrderUsd", tr("\u5355\u7B14\u540D\u4E49\u4EF7\u503C\u4E0A\u9650 \xB7 USD", "Max per order \xB7 USD"), { type: "number", min: "0.01", step: "any" }), field("maxDailyUsd", tr("\u6BCF\u65E5\u5F00\u4ED3\u989D\u5EA6 \xB7 USD", "Daily opening budget \xB7 USD"), { type: "number", min: "0.01", step: "any" }), field("maxPositionUsd", tr("\u5355\u5E01\u4ED3\u4F4D\u4E0A\u9650 \xB7 USD", "Max position per coin \xB7 USD"), { type: "number", min: "0.01", step: "any" }), field("maxLeverage", tr("\u6700\u5927\u6760\u6746 \xB7 \u500D", "Max leverage \xB7 x"), { type: "number", min: "1", max: "50", step: "1" }), /* @__PURE__ */ React31.createElement("p", { className: "dhl-risk-budget" }, tr("\u4ECA\u65E5\u5DF2\u7528\u989D\u5EA6\uFF08UTC\uFF09", "Used today (UTC)"), "\uFF1A", fmt(limits.usedTodayUsd ?? 0), " / ", fmt(limits.maxDailyUsd), " USD"), error ? /* @__PURE__ */ React31.createElement("p", { className: "dhl-risk-error", role: "alert" }, error) : null) : /* @__PURE__ */ React31.createElement("div", { className: "dhl-empty" }, tr("\u6B63\u5728\u8BFB\u53D6\u2026", "Loading\u2026")), /* @__PURE__ */ React31.createElement("p", { className: "dhl-limits-note" }, tr(
22021
- "\u4FDD\u5B58\u540E\u7ACB\u5373\u4F5C\u7528\u4E8E\u6A21\u62DF\u76D8\u548C\u5B9E\u76D8\uFF0C\u91CD\u542F\u540E\u7EE7\u7EED\u751F\u6548\u3002\u4E24\u79CD\u6A21\u5F0F\u7684\u6BCF\u65E5\u989D\u5EA6\u5206\u5F00\u8BB0\u8D26\uFF1B\u51CF\u4ED3\u548C\u5E73\u4ED3\u4E0D\u53D7\u5F00\u4ED3\u9650\u989D\u963B\u6B62\u3002",
22022
- "Changes apply immediately to paper and live trading and survive restart. Each mode has its own daily budget; reducing and closing remain available."
22023
- ))), /* @__PURE__ */ React31.createElement("section", { className: "dhl-limits-section" }, /* @__PURE__ */ React31.createElement("h3", null, tr("\u6A21\u62DF\u6210\u4EA4\u6210\u672C", "Paper execution costs")), /* @__PURE__ */ React31.createElement("div", { className: "dhl-risk-form" }, ["feeBps", "slippageBps"].map((key3) => /* @__PURE__ */ React31.createElement("label", { key: key3 }, /* @__PURE__ */ React31.createElement("span", null, key3 === "feeBps" ? tr("\u624B\u7EED\u8D39 \xB7 bps", "Fee \xB7 bps") : tr("\u4E0D\u5229\u6ED1\u70B9 \xB7 bps", "Adverse slippage \xB7 bps")), /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Input, { type: "number", "aria-label": key3 === "feeBps" ? tr("\u6A21\u62DF\u624B\u7EED\u8D39 bps", "Paper fee bps") : tr("\u6A21\u62DF\u6ED1\u70B9 bps", "Paper slippage bps"), min: "0", max: "100", step: "any", value: costs[key3], onChange: (e) => setCosts((old) => ({ ...old, [key3]: e.target.value })) })))), /* @__PURE__ */ React31.createElement("p", { className: "dhl-limits-note" }, tr("1 bps = 0.01%\u3002\u8FD9\u662F\u6A21\u62DF\u5047\u8BBE\uFF0C\u4E0D\u662F\u5B9E\u76D8\u8D39\u7387\u3002\u4EC5\u4F5C\u7528\u4E8E\u4E4B\u540E\u7684\u6A21\u62DF\u6210\u4EA4\uFF0C\u65E7\u8BB0\u5F55\u4FDD\u7559\u539F\u503C\uFF1B\u4E0D\u6A21\u62DF\u76D8\u53E3\u6DF1\u5EA6\u3001\u8D44\u91D1\u8D39\u548C\u5F3A\u5E73\u3002", "1 bps = 0.01%. Simulation assumptions, not exchange fee tiers. Future paper fills only; historical assumptions are preserved. No depth, funding or liquidation simulation.")), /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Button, { variant: "outline", disabled: busy, onClick: () => {
22245
+ const [tab, setTab] = React31.useState("limits");
22246
+ const [wallet, setWallet] = React31.useState({ signer: "local", web3Account: "main", tradingAddress: "" });
22247
+ const [web3Accounts, setWeb3Accounts] = React31.useState({ available: false, accounts: [] });
22248
+ React31.useEffect(() => {
22249
+ if (!open) return;
22250
+ setWallet({ signer: st?.signer ?? "local", web3Account: st?.web3Account ?? "main", tradingAddress: st?.tradingAddress ?? "" });
22251
+ void hyperCall("walletAccounts").then(setWeb3Accounts).catch(() => setWeb3Accounts({ available: false, accounts: [] }));
22252
+ }, [open]);
22253
+ const saveWallet = () => {
22254
+ setBusy(true);
22255
+ setError("");
22256
+ void hyperCall("updateWalletSettings", wallet).then(() => resetWalletView()).then(() => notify(tr("\u94B1\u5305\u8BBE\u7F6E\u5DF2\u751F\u6548", "Wallet settings applied"))).catch((cause) => setError(String(cause.message))).finally(() => setBusy(false));
22257
+ };
22258
+ React31.useEffect(() => {
22259
+ if (open) setTab("limits");
22260
+ }, [open]);
22261
+ const saveCosts = () => {
22024
22262
  setBusy(true);
22025
22263
  setError("");
22026
22264
  void hyperCall("updatePaperCosts", { costs: { feeBps: costs.feeBps.trim() ? Number(costs.feeBps) : NaN, slippageBps: costs.slippageBps.trim() ? Number(costs.slippageBps) : NaN } }).then(() => loadStatus()).then(() => notify(tr("\u6A21\u62DF\u6210\u672C\u5DF2\u4FDD\u5B58", "Paper costs saved"))).catch((cause) => setError(String(cause.message))).finally(() => setBusy(false));
22027
- } }, tr("\u4FDD\u5B58\u6A21\u62DF\u6210\u672C", "Save paper costs"))), /* @__PURE__ */ React31.createElement("section", { className: "dhl-limits-section" }, /* @__PURE__ */ React31.createElement("h3", null, tr("\u94B1\u5305\u7B7E\u540D", "Wallet signing")), /* @__PURE__ */ React31.createElement("p", { className: "dhl-limits-note" }, paper ? tr("\u6A21\u62DF\u76D8\u5728\u672C\u5730\u64AE\u5408\uFF0C\u4E0D\u7B7E\u540D\uFF0C\u4E5F\u4E0D\u7ECF\u8FC7\u94B1\u5305\u89C4\u5219\u3002", "Paper orders fill locally and do not use wallet signing.") : st?.signer === "web3" ? tr(
22028
- "\u5B9E\u76D8\u8BA2\u5355\u901A\u8FC7\u9650\u989D\u68C0\u67E5\u540E\uFF0C\u7531 dsh-web3 \u94B1\u5305\u6309\u5176\u7B7E\u540D\u7B56\u7565\u5904\u7406\u3002\u7B7E\u540D\u7B56\u7565\u5728 DSH \u8BBE\u7F6E \u2192 Web3 \u94B1\u5305\u4E2D\u7BA1\u7406\u3002",
22029
- "After the limits check, live orders use the dsh-web3 wallet signing policy. Manage it in DSH Settings \u2192 Web3 wallet."
22030
- ) : tr(
22031
- "\u5B9E\u76D8\u4F7F\u7528\u63D2\u4EF6\u914D\u7F6E\u4E2D\u7684\u79C1\u94A5\u7B7E\u540D\u3002\u79C1\u94A5\u4E0E\u7B7E\u540D\u65B9\u5F0F\u4ECD\u5728 DSH \u63D2\u4EF6\u914D\u7F6E\u4E2D\u7BA1\u7406\u3002",
22032
- "Live orders use the private key from plugin configuration. Manage the key and signing method in DSH plugin settings."
22033
- )), !paper && !st?.autonomousLive ? /* @__PURE__ */ React31.createElement("p", { className: "dhl-limits-note" }, tr("agent \u6BCF\u7B14\u5B9E\u76D8\u4E0B\u5355\u6216\u6539\u6760\u6746\u524D\u4ECD\u9700 DSH \u5BA1\u6279\u3002", "Each agent live order or leverage change still requires DSH approval.")) : null));
22265
+ };
22266
+ const field = (key3, label, props, wide = false) => /* @__PURE__ */ React31.createElement("label", { className: wide ? "dhl-risk-wide" : void 0 }, /* @__PURE__ */ React31.createElement("span", null, label), /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Input, { className: "dhl-risk-input", value: draft[key3], onChange: (event) => change(key3, event.target.value), required: true, ...props }));
22267
+ const footer = tab === "limits" ? limits ? /* @__PURE__ */ React31.createElement(React31.Fragment, null, st?.riskSettingsSource === "desk" ? /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Button, { variant: "outline", disabled: busy, onClick: () => void reset() }, tr("\u6062\u590D\u63D2\u4EF6\u914D\u7F6E", "Restore plugin config")) : null, /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Button, { variant: "primary", type: "submit", form: "dhl-risk-form", disabled: busy }, busy ? tr("\u4FDD\u5B58\u4E2D\u2026", "Saving\u2026") : tr("\u4FDD\u5B58\u9650\u989D", "Save limits"))) : null : tab === "costs" ? /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Button, { variant: "primary", disabled: busy, onClick: saveCosts }, busy ? tr("\u4FDD\u5B58\u4E2D\u2026", "Saving\u2026") : tr("\u4FDD\u5B58\u6A21\u62DF\u6210\u672C", "Save paper costs")) : /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Button, { variant: "primary", disabled: busy, onClick: saveWallet }, busy ? tr("\u8FDE\u63A5\u4E2D\u2026", "Connecting\u2026") : tr("\u4FDD\u5B58\u5E76\u8FDE\u63A5", "Save & connect"));
22268
+ const tabs = [["limits", "\u4EA4\u6613\u9650\u989D", "Limits"], ["costs", "\u6A21\u62DF\u6210\u672C", "Paper costs"], ["signing", "\u7B7E\u540D\u4E0E\u6258\u7BA1", "Signing & custody"]];
22269
+ return /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Modal, { open, onClose, title: tr("\u4EA4\u6613\u9650\u989D\u4E0E\u7B7E\u540D\u89C4\u5219", "Trading limits & signing rules"), closeLabel: tr("\u5173\u95ED", "Close"), className: "dhl-limits-modal", contentClassName: "dhl-limits-content", footer }, /* @__PURE__ */ React31.createElement("div", { className: "dhl-limits-tabs", role: "tablist", "aria-label": tr("\u8BBE\u7F6E\u5206\u7EC4", "Settings sections") }, tabs.map(([key3, zh, en]) => /* @__PURE__ */ React31.createElement(
22270
+ "button",
22271
+ {
22272
+ key: key3,
22273
+ type: "button",
22274
+ role: "tab",
22275
+ id: `dhl-limits-tab-${key3}`,
22276
+ "aria-controls": `dhl-limits-panel-${key3}`,
22277
+ "aria-selected": tab === key3,
22278
+ className: tab === key3 ? "on" : "",
22279
+ onClick: () => {
22280
+ setTab(key3);
22281
+ setError("");
22282
+ }
22283
+ },
22284
+ tr(zh, en)
22285
+ ))), /* @__PURE__ */ React31.createElement("div", { className: "dhl-limits-scroll" }, tab === "limits" ? /* @__PURE__ */ React31.createElement("section", { className: "dhl-limits-section", role: "tabpanel", id: "dhl-limits-panel-limits", "aria-labelledby": "dhl-limits-tab-limits" }, /* @__PURE__ */ React31.createElement("p", { className: "dhl-limits-ledger-row" }, /* @__PURE__ */ React31.createElement("span", { className: "dhl-limits-ledger" }, paper ? tr("\u6A21\u62DF\u76D8\u8D26\u672C", "Paper ledger") : tr("\u5B9E\u76D8\u8D26\u672C", "Live ledger")), limits ? /* @__PURE__ */ React31.createElement("span", null, tr("\u4ECA\u65E5\u5DF2\u7528\uFF08UTC\uFF09", "Used today (UTC)"), " ", fmt(limits.usedTodayUsd ?? 0), " / ", fmt(limits.maxDailyUsd), " USD") : null), limits ? /* @__PURE__ */ React31.createElement("form", { id: "dhl-risk-form", className: "dhl-risk-form", onSubmit: (event) => void save(event) }, field("allowedCoins", tr("\u5141\u8BB8\u5F00\u4ED3\u5E01\u5BF9\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u5982 BTC, xyz:XYZ100\uFF09", "Coins allowed to open (comma separated)"), { placeholder: "BTC, ETH, xyz:XYZ100", maxLength: 1e3 }, true), field("maxOrderUsd", tr("\u5355\u7B14\u4E0A\u9650 \xB7 USD", "Per order \xB7 USD"), { type: "number", min: "0.01", step: "any" }), field("maxDailyUsd", tr("\u6BCF\u65E5\u5F00\u4ED3\u989D\u5EA6 \xB7 USD", "Daily budget \xB7 USD"), { type: "number", min: "0.01", step: "any" }), field("maxPositionUsd", tr("\u5355\u5E01\u4ED3\u4F4D\u4E0A\u9650 \xB7 USD", "Per coin position \xB7 USD"), { type: "number", min: "0.01", step: "any" }), field("maxLeverage", tr("\u6700\u5927\u6760\u6746 \xB7 \u500D", "Max leverage \xB7 x"), { type: "number", min: "1", max: "50", step: "1" })) : /* @__PURE__ */ React31.createElement("div", { className: "dhl-empty" }, tr("\u6B63\u5728\u8BFB\u53D6\u2026", "Loading\u2026")), /* @__PURE__ */ React31.createElement("p", { className: "dhl-limits-note" }, tr(
22286
+ "\u7ACB\u5373\u4F5C\u7528\u4E8E\u6A21\u62DF\u76D8\u548C\u5B9E\u76D8\uFF0C\u91CD\u542F\u540E\u4FDD\u7559\uFF1B\u4E24\u79CD\u6A21\u5F0F\u7684\u6BCF\u65E5\u989D\u5EA6\u5206\u5F00\u8BB0\u8D26\uFF0C\u51CF\u4ED3\u548C\u5E73\u4ED3\u4E0D\u53D7\u9650\u3002",
22287
+ "Applies to paper and live at once and survives restart; each mode has its own daily budget; reducing and closing are never blocked."
22288
+ ))) : null, tab === "costs" ? /* @__PURE__ */ React31.createElement("section", { className: "dhl-limits-section", role: "tabpanel", id: "dhl-limits-panel-costs", "aria-labelledby": "dhl-limits-tab-costs" }, /* @__PURE__ */ React31.createElement("div", { className: "dhl-risk-form" }, ["feeBps", "slippageBps"].map((key3) => /* @__PURE__ */ React31.createElement("label", { key: key3 }, /* @__PURE__ */ React31.createElement("span", null, key3 === "feeBps" ? tr("\u624B\u7EED\u8D39 \xB7 bps", "Fee \xB7 bps") : tr("\u4E0D\u5229\u6ED1\u70B9 \xB7 bps", "Adverse slippage \xB7 bps")), /* @__PURE__ */ React31.createElement(import_dsh_client_ui_primitives9.Input, { type: "number", "aria-label": key3 === "feeBps" ? tr("\u6A21\u62DF\u624B\u7EED\u8D39 bps", "Paper fee bps") : tr("\u6A21\u62DF\u6ED1\u70B9 bps", "Paper slippage bps"), min: "0", max: "100", step: "any", value: costs[key3], onChange: (e) => setCosts((old) => ({ ...old, [key3]: e.target.value })) })))), /* @__PURE__ */ React31.createElement("p", { className: "dhl-limits-note" }, tr("1 bps = 0.01%\u3002\u4EC5\u5F71\u54CD\u4E4B\u540E\u7684\u6A21\u62DF\u6210\u4EA4\uFF1B\u4E0D\u6A21\u62DF\u76D8\u53E3\u6DF1\u5EA6\u3001\u8D44\u91D1\u8D39\u548C\u5F3A\u5E73\u3002", "1 bps = 0.01%. Future paper fills only; no depth, funding or liquidation simulation."))) : null, tab === "signing" ? /* @__PURE__ */ React31.createElement("section", { className: "dhl-limits-section", role: "tabpanel", id: "dhl-limits-panel-signing", "aria-labelledby": "dhl-limits-tab-signing" }, /* @__PURE__ */ React31.createElement("div", { className: "dhl-risk-form dhl-wallet-form" }, /* @__PURE__ */ React31.createElement("label", null, /* @__PURE__ */ React31.createElement("span", null, tr("\u7B7E\u540D\u65B9\u5F0F", "Signer")), /* @__PURE__ */ React31.createElement(Select, { "aria-label": tr("\u7B7E\u540D\u65B9\u5F0F", "Signer"), value: wallet.signer, onChange: (event) => setWallet((old) => ({ ...old, signer: event.target.value })) }, /* @__PURE__ */ React31.createElement("option", { value: "web3" }, tr("dsh-web3 \u94B1\u5305", "dsh-web3 wallet")), /* @__PURE__ */ React31.createElement("option", { value: "local" }, tr("\u63D2\u4EF6\u914D\u7F6E\u7684\u79C1\u94A5", "Plugin private key")))), wallet.signer === "web3" ? /* @__PURE__ */ React31.createElement("label", null, /* @__PURE__ */ React31.createElement("span", null, tr("\u94B1\u5305\u8D26\u6237", "Wallet account")), /* @__PURE__ */ React31.createElement(Select, { "aria-label": tr("\u94B1\u5305\u8D26\u6237", "Wallet account"), value: wallet.web3Account, onChange: (event) => setWallet((old) => ({ ...old, web3Account: event.target.value })) }, web3Accounts.accounts.some((row) => row.id === wallet.web3Account || row.label === wallet.web3Account) ? null : /* @__PURE__ */ React31.createElement("option", { value: wallet.web3Account }, wallet.web3Account), web3Accounts.accounts.map((row) => /* @__PURE__ */ React31.createElement("option", { key: row.id, value: row.id }, `${row.label} \xB7 ${row.address.slice(0, 6)}\u2026${row.address.slice(-4)}`)))) : null, /* @__PURE__ */ React31.createElement("label", { className: "dhl-risk-wide" }, /* @__PURE__ */ React31.createElement("span", null, tr("Hyperliquid \u4E3B\u8D26\u6237\u5730\u5740\uFF08\u4EA4\u6613\u4E0E\u67E5\u8BE2\u7528\uFF09", "Hyperliquid main account address")), /* @__PURE__ */ React31.createElement(
22289
+ import_dsh_client_ui_primitives9.Input,
22290
+ {
22291
+ "aria-label": tr("\u4E3B\u8D26\u6237\u5730\u5740", "Main account address"),
22292
+ placeholder: "0x\u2026",
22293
+ value: wallet.tradingAddress,
22294
+ maxLength: 42,
22295
+ spellCheck: false,
22296
+ onChange: (event) => setWallet((old) => ({ ...old, tradingAddress: event.target.value.trim() }))
22297
+ }
22298
+ ))), wallet.signer === "web3" && !web3Accounts.available ? /* @__PURE__ */ React31.createElement("p", { className: "dhl-limits-note" }, tr("\u6CA1\u6709\u68C0\u6D4B\u5230 dsh-web3 \u94B1\u5305\u63D2\u4EF6\u3002", "The dsh-web3 wallet plugin is not available.")) : null, /* @__PURE__ */ React31.createElement("p", { className: "dhl-limits-note" }, tr("\u4FDD\u5B58\u540E\u7ACB\u5373\u751F\u6548\uFF0C\u65E0\u9700\u91CD\u542F\uFF1B\u79C1\u94A5\u4ECD\u53EA\u5728\u63D2\u4EF6\u914D\u7F6E\u91CC\u7BA1\u7406\u3002\u65B0\u5EFA\u7684 dsh-web3 \u8D26\u6237\u82E5\u4E0D\u5728\u5217\u8868\u4E2D\uFF0C\u9700\u91CD\u542F DSH \u5237\u65B0\u94B1\u5305\u3002", "Applies immediately, no restart; the private key stays in plugin config. A dsh-web3 account created elsewhere may need a DSH restart to appear.")), /* @__PURE__ */ React31.createElement("dl", { className: "dhl-limits-facts" }, /* @__PURE__ */ React31.createElement("dt", null, tr("\u5F53\u524D", "Current")), /* @__PURE__ */ React31.createElement("dd", null, paper ? tr("\u6A21\u62DF\u76D8\u672C\u5730\u64AE\u5408\uFF0C\u4E0D\u7B7E\u540D\u3002", "Paper fills locally without signing.") : st?.signer === "web3" ? tr("dsh-web3 \u94B1\u5305\uFF0C\u6309\u5176\u7B7E\u540D\u7B56\u7565\u5904\u7406\uFF08DSH \u8BBE\u7F6E \u2192 Web3 \u94B1\u5305\uFF09\u3002", "dsh-web3 wallet, under its signing policy (DSH Settings \u2192 Web3 wallet).") : tr("\u63D2\u4EF6\u914D\u7F6E\u4E2D\u7684\u79C1\u94A5\uFF08DSH \u63D2\u4EF6\u914D\u7F6E\uFF09\u3002", "Private key from plugin configuration.")), /* @__PURE__ */ React31.createElement("dt", null, tr("\u6258\u7BA1\u6A21\u5F0F", "Custody")), /* @__PURE__ */ React31.createElement("dd", null, st?.custody === "managed" ? tr("\u5168\u6258\u7BA1\uFF1Aagent \u5728\u9650\u989D\u5185\u76F4\u63A5\u6267\u884C\uFF0C\u4E5F\u53EF\u8C03\u6574\u8FD9\u4E9B\u9650\u989D\u5E76\u5212\u8F6C HIP-3 \u4FDD\u8BC1\u91D1\uFF0C\u6BCF\u6B21\u90FD\u4F1A\u5728\u4F1A\u8BDD\u91CC\u8BF4\u660E\u3002", "Managed: the agent trades within limits, may adjust them and move HIP-3 margin, and says so each time.") : tr("\u534A\u6258\u7BA1\uFF1Aagent \u6BCF\u7B14\u5B9E\u76D8\u4E0B\u5355\u3001\u6539\u6760\u6746\u548C\u5212\u8F6C\u90FD\u9700\u8981\u4F60\u6279\u51C6\uFF0C\u9650\u989D\u53EA\u7531\u4F60\u4FEE\u6539\u3002", "Assisted: you approve each live order, leverage change and transfer; only you change limits."), /* @__PURE__ */ React31.createElement("span", { className: "dhl-limits-hint" }, tr("\u5728\u53F3\u4E0A\u89D2 \u8BBE\u7F6E \u2192 \u6258\u7BA1\u6A21\u5F0F \u5207\u6362\u3002", "Switch in Settings \u2192 Custody."))))) : null, error ? /* @__PURE__ */ React31.createElement("p", { className: "dhl-risk-error", role: "alert" }, error) : null));
22034
22299
  }
22035
22300
 
22036
22301
  // src/client/app.tsx
@@ -22043,6 +22308,22 @@ function App(props) {
22043
22308
  const [walletOpen, setWalletOpen] = React32.useState(false);
22044
22309
  const [moreOpen, setMoreOpen] = React32.useState(false);
22045
22310
  const [limitsOpen, setLimitsOpen] = React32.useState(false);
22311
+ const [confirmManaged, setConfirmManaged] = React32.useState(false);
22312
+ const custody = st?.custody === "managed" ? "managed" : "assisted";
22313
+ const chooseCustody = React32.useCallback(async (next, acknowledge = false) => {
22314
+ setMoreOpen(false);
22315
+ if (next === "managed" && store.get().status?.tradingMode === "live" && !acknowledge) {
22316
+ setConfirmManaged(true);
22317
+ return;
22318
+ }
22319
+ try {
22320
+ await setCustody(next, acknowledge);
22321
+ } catch (error) {
22322
+ notify(tr("\u5207\u6362\u6258\u7BA1\u6A21\u5F0F\u5931\u8D25", "Could not switch custody") + "\uFF1A" + String(error?.message ?? error));
22323
+ } finally {
22324
+ setConfirmManaged(false);
22325
+ }
22326
+ }, []);
22046
22327
  const moreRef = React32.useRef(null);
22047
22328
  const network = st?.network === "testnet" ? "testnet" : "mainnet";
22048
22329
  const mode = st?.tradingMode === "paper" ? "paper" : "live";
@@ -22150,12 +22431,12 @@ function App(props) {
22150
22431
  const showSessions = !layout.sessionsCollapsed;
22151
22432
  const showDesk = !layout.deskCollapsed;
22152
22433
  const showChat = !layout.chatCollapsed;
22153
- return /* @__PURE__ */ React32.createElement("div", { className: "dhl-root" }, /* @__PURE__ */ React32.createElement("div", { className: "dhl-topbar" }, /* @__PURE__ */ React32.createElement("span", { className: "dhl-brand" }, /* @__PURE__ */ React32.createElement(BrandMark, null), "Hyperliquid ", /* @__PURE__ */ React32.createElement("span", { style: { fontWeight: 400, color: "var(--dsw-alias-label-secondary)" } }, t("app.suffix", "\u4EA4\u6613\u53F0"))), /* @__PURE__ */ React32.createElement("div", { className: "dhl-spacer" }), s.statusError ? /* @__PURE__ */ React32.createElement("span", { className: "dhl-badge err" }, t("status.unavailable", "\u8FDC\u7AEF\u4E0D\u53EF\u7528")) : null, st && network === "testnet" ? /* @__PURE__ */ React32.createElement("span", { className: "dhl-badge paper" }, t("network.testnet", "\u6D4B\u8BD5\u7F51")) : null, st && mode === "paper" ? /* @__PURE__ */ React32.createElement("span", { className: "dhl-badge paper" }, t("mode.paper", "\u6A21\u62DF\u76D8")) : null, wsBadge, /* @__PURE__ */ React32.createElement("button", { type: "button", className: "dhl-badge dhl-wallet-trigger", onClick: () => setWalletOpen(true), "aria-haspopup": "dialog", "aria-label": tr("\u67E5\u770B\u94B1\u5305\u8BE6\u60C5\u548C\u6536\u76CA", "View wallet and P&L") }, mode === "paper" ? tr("\u6A21\u62DF\u8D26\u6237", "Paper account") : st?.address ? st.address.slice(0, 6) + "\u2026" + st.address.slice(-4) : t("account.none", "\u672A\u8FDE\u63A5\u94B1\u5305"), /* @__PURE__ */ React32.createElement(UiIcon, { name: "chevronDown", size: 14 })), /* @__PURE__ */ React32.createElement("div", { className: "dhl-more", ref: moreRef }, /* @__PURE__ */ React32.createElement("button", { type: "button", className: "dhl-top-control dhl-more-trigger", "aria-haspopup": "menu", "aria-expanded": moreOpen, "aria-label": tr("\u4EA4\u6613\u8BBE\u7F6E", "Trading settings"), title: tr("\u4EA4\u6613\u8BBE\u7F6E", "Trading settings"), onClick: () => setMoreOpen((value) => !value) }, /* @__PURE__ */ React32.createElement(UiIcon, { name: "settings", size: 17 })), moreOpen ? /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-popover", role: "menu" }, /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-group" }, /* @__PURE__ */ React32.createElement("span", null, t("network.label", "\u7F51\u7EDC")), /* @__PURE__ */ React32.createElement("div", { className: "dhl-segment" }, /* @__PURE__ */ React32.createElement("button", { type: "button", className: network === "mainnet" ? "on" : "", disabled: networkBusy || !st, onClick: () => network !== "mainnet" && requestEnv({ network: "mainnet", mode }) }, t("network.mainnet", "\u4E3B\u7F51")), /* @__PURE__ */ React32.createElement("button", { type: "button", className: network === "testnet" ? "on" : "", disabled: networkBusy || !st, onClick: () => network !== "testnet" && requestEnv({ network: "testnet", mode }) }, t("network.testnet", "\u6D4B\u8BD5\u7F51")))), /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-group" }, /* @__PURE__ */ React32.createElement("span", null, tr("\u8D26\u6237", "Account")), /* @__PURE__ */ React32.createElement("div", { className: "dhl-segment" }, /* @__PURE__ */ React32.createElement("button", { type: "button", className: mode === "live" ? "on" : "", disabled: networkBusy || !st, onClick: () => mode !== "live" && requestEnv({ network, mode: "live" }) }, t("mode.live", "\u5B9E\u76D8")), /* @__PURE__ */ React32.createElement("button", { type: "button", className: mode === "paper" ? "on" : "", disabled: networkBusy || !st, onClick: () => mode !== "paper" && requestEnv({ network, mode: "paper" }) }, t("mode.paper", "\u6A21\u62DF\u76D8")))), /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-group" }, /* @__PURE__ */ React32.createElement("span", null, tr("\u76D8\u53E3 / \u6210\u4EA4\u4F4D\u7F6E", "Book / trades position")), /* @__PURE__ */ React32.createElement("div", { className: "dhl-segment" }, /* @__PURE__ */ React32.createElement("button", { type: "button", className: s.tapePlacement === "auto" ? "on" : "", onClick: () => setTapePlacement("auto") }, tr("\u81EA\u9002\u5E94", "Auto")), /* @__PURE__ */ React32.createElement("button", { type: "button", className: s.tapePlacement === "below" ? "on" : "", onClick: () => setTapePlacement("below") }, tr("\u56FE\u8868\u4E0B\u65B9", "Below chart")))), /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-group" }, /* @__PURE__ */ React32.createElement("span", null, tr("\u4EA4\u6613\u8BBE\u7F6E", "Trading settings")), /* @__PURE__ */ React32.createElement("div", { className: "dhl-segment" }, /* @__PURE__ */ React32.createElement("button", { type: "button", onClick: () => {
22434
+ return /* @__PURE__ */ React32.createElement("div", { className: "dhl-root" }, /* @__PURE__ */ React32.createElement("div", { className: "dhl-topbar" }, /* @__PURE__ */ React32.createElement("span", { className: "dhl-brand" }, /* @__PURE__ */ React32.createElement(BrandMark, null), "Hyperliquid ", /* @__PURE__ */ React32.createElement("span", { style: { fontWeight: 400, color: "var(--dsw-alias-label-secondary)" } }, t("app.suffix", "\u4EA4\u6613\u53F0"))), /* @__PURE__ */ React32.createElement("div", { className: "dhl-spacer" }), s.statusError ? /* @__PURE__ */ React32.createElement("span", { className: "dhl-badge err" }, t("status.unavailable", "\u8FDC\u7AEF\u4E0D\u53EF\u7528")) : null, st && network === "testnet" ? /* @__PURE__ */ React32.createElement("span", { className: "dhl-badge paper" }, t("network.testnet", "\u6D4B\u8BD5\u7F51")) : null, st && mode === "paper" ? /* @__PURE__ */ React32.createElement("span", { className: "dhl-badge paper" }, t("mode.paper", "\u6A21\u62DF\u76D8")) : null, st && custody === "managed" ? /* @__PURE__ */ React32.createElement("span", { className: "dhl-badge managed", title: tr("\u5168\u6258\u7BA1\uFF1Aagent \u53EF\u81EA\u4E3B\u6267\u884C\u5B9E\u76D8\u3001\u8C03\u6574\u9650\u989D\u4E0E\u5212\u8F6C\u4FDD\u8BC1\u91D1", "Managed: the agent trades, adjusts limits and moves margin on its own") }, tr("\u5168\u6258\u7BA1", "Managed")) : null, wsBadge, /* @__PURE__ */ React32.createElement("button", { type: "button", className: "dhl-badge dhl-wallet-trigger", onClick: () => setWalletOpen(true), "aria-haspopup": "dialog", "aria-label": tr("\u67E5\u770B\u94B1\u5305\u8BE6\u60C5\u548C\u6536\u76CA", "View wallet and P&L") }, mode === "paper" ? tr("\u6A21\u62DF\u8D26\u6237", "Paper account") : st?.address ? st.address.slice(0, 6) + "\u2026" + st.address.slice(-4) : t("account.none", "\u672A\u8FDE\u63A5\u94B1\u5305"), /* @__PURE__ */ React32.createElement(UiIcon, { name: "chevronDown", size: 14 })), /* @__PURE__ */ React32.createElement("div", { className: "dhl-more", ref: moreRef }, /* @__PURE__ */ React32.createElement("button", { type: "button", className: "dhl-top-control dhl-more-trigger", "aria-haspopup": "menu", "aria-expanded": moreOpen, "aria-label": tr("\u4EA4\u6613\u8BBE\u7F6E", "Trading settings"), title: tr("\u4EA4\u6613\u8BBE\u7F6E", "Trading settings"), onClick: () => setMoreOpen((value) => !value) }, /* @__PURE__ */ React32.createElement(UiIcon, { name: "settings", size: 17 })), moreOpen ? /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-popover", role: "menu" }, /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-group" }, /* @__PURE__ */ React32.createElement("span", null, t("network.label", "\u7F51\u7EDC")), /* @__PURE__ */ React32.createElement("div", { className: "dhl-segment" }, /* @__PURE__ */ React32.createElement("button", { type: "button", className: network === "mainnet" ? "on" : "", disabled: networkBusy || !st, onClick: () => network !== "mainnet" && requestEnv({ network: "mainnet", mode }) }, t("network.mainnet", "\u4E3B\u7F51")), /* @__PURE__ */ React32.createElement("button", { type: "button", className: network === "testnet" ? "on" : "", disabled: networkBusy || !st, onClick: () => network !== "testnet" && requestEnv({ network: "testnet", mode }) }, t("network.testnet", "\u6D4B\u8BD5\u7F51")))), /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-group" }, /* @__PURE__ */ React32.createElement("span", null, tr("\u8D26\u6237", "Account")), /* @__PURE__ */ React32.createElement("div", { className: "dhl-segment" }, /* @__PURE__ */ React32.createElement("button", { type: "button", className: mode === "live" ? "on" : "", disabled: networkBusy || !st, onClick: () => mode !== "live" && requestEnv({ network, mode: "live" }) }, t("mode.live", "\u5B9E\u76D8")), /* @__PURE__ */ React32.createElement("button", { type: "button", className: mode === "paper" ? "on" : "", disabled: networkBusy || !st, onClick: () => mode !== "paper" && requestEnv({ network, mode: "paper" }) }, t("mode.paper", "\u6A21\u62DF\u76D8")))), /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-group" }, /* @__PURE__ */ React32.createElement("span", null, tr("\u6258\u7BA1\u6A21\u5F0F", "Custody")), /* @__PURE__ */ React32.createElement("div", { className: "dhl-segment", role: "group", "aria-label": tr("\u6258\u7BA1\u6A21\u5F0F", "Custody") }, /* @__PURE__ */ React32.createElement("button", { type: "button", className: custody === "assisted" ? "on" : "", "aria-pressed": custody === "assisted", disabled: !st, title: tr("\u5B9E\u76D8\u4E0B\u5355\u3001\u5212\u8F6C\u9700\u4F60\u6279\u51C6\uFF1B\u9650\u989D\u53EA\u7531\u4F60\u4FEE\u6539", "You approve live orders and transfers; only you change limits"), onClick: () => custody !== "assisted" && void chooseCustody("assisted") }, tr("\u534A\u6258\u7BA1", "Assisted")), /* @__PURE__ */ React32.createElement("button", { type: "button", className: custody === "managed" ? "on" : "", "aria-pressed": custody === "managed", disabled: !st, title: tr("agent \u81EA\u4E3B\u6267\u884C\u5B9E\u76D8\u3001\u8C03\u6574\u9650\u989D\u4E0E\u5212\u8F6C\u4FDD\u8BC1\u91D1", "The agent trades, adjusts limits and moves margin on its own"), onClick: () => custody !== "managed" && void chooseCustody("managed") }, tr("\u5168\u6258\u7BA1", "Managed")))), /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-group" }, /* @__PURE__ */ React32.createElement("span", null, tr("\u76D8\u53E3 / \u6210\u4EA4\u4F4D\u7F6E", "Book / trades position")), /* @__PURE__ */ React32.createElement("div", { className: "dhl-segment" }, /* @__PURE__ */ React32.createElement("button", { type: "button", className: s.tapePlacement === "auto" ? "on" : "", onClick: () => setTapePlacement("auto") }, tr("\u81EA\u9002\u5E94", "Auto")), /* @__PURE__ */ React32.createElement("button", { type: "button", className: s.tapePlacement === "below" ? "on" : "", onClick: () => setTapePlacement("below") }, tr("\u56FE\u8868\u4E0B\u65B9", "Below chart")))), /* @__PURE__ */ React32.createElement("div", { className: "dhl-more-group" }, /* @__PURE__ */ React32.createElement("span", null, tr("\u4EA4\u6613\u8BBE\u7F6E", "Trading settings")), /* @__PURE__ */ React32.createElement("div", { className: "dhl-segment" }, /* @__PURE__ */ React32.createElement("button", { type: "button", onClick: () => {
22154
22435
  setMoreOpen(false);
22155
22436
  setLimitsOpen(true);
22156
22437
  } }, tr("\u4EA4\u6613\u9650\u989D\u4E0E\u7B7E\u540D\u89C4\u5219", "Limits & signing rules"))))) : null)), /* @__PURE__ */ React32.createElement(import_dsh_client_ui_primitives10.Modal, { open: Boolean(confirmLive), onClose: () => setConfirmLive(null), title: tr("\u5207\u6362\u5230\u4E3B\u7F51\u5B9E\u76D8", "Switch to mainnet live"), closeLabel: t("modal.close", "\u5173\u95ED"), className: "dhl-network-modal", footer: /* @__PURE__ */ React32.createElement(React32.Fragment, null, /* @__PURE__ */ React32.createElement(import_dsh_client_ui_primitives10.Button, { variant: "outline", onClick: () => setConfirmLive(null) }, t("modal.cancel", "\u53D6\u6D88")), /* @__PURE__ */ React32.createElement(import_dsh_client_ui_primitives10.Button, { variant: "primary", disabled: networkBusy, onClick: () => {
22157
22438
  if (confirmLive) void applyEnv(confirmLive);
22158
- } }, tr("\u786E\u8BA4\u5207\u6362", "Switch"))) }, /* @__PURE__ */ React32.createElement("p", null, tr("\u4E3B\u7F51\u5B9E\u76D8\u4F1A\u663E\u793A\u771F\u5B9E\u884C\u60C5\u548C\u771F\u5B9E\u8D26\u6237\uFF0C\u540E\u7EED\u4EA4\u6613\u5C06\u4F7F\u7528\u771F\u5B9E\u8D44\u91D1\u3002", "Mainnet live shows real markets and your real account; trades use real funds."))), /* @__PURE__ */ React32.createElement(WalletDetails, { open: walletOpen, onClose: () => setWalletOpen(false) }), /* @__PURE__ */ React32.createElement(TradingLimits, { open: limitsOpen, onClose: () => setLimitsOpen(false) }), /* @__PURE__ */ React32.createElement("div", { className: "dhl-toasts" }, marketStale && !resyncing ? /* @__PURE__ */ React32.createElement("div", { className: "dhl-data-warning", role: "status" }, tr("\u884C\u60C5\u6216\u5BBF\u4E3B\u72B6\u6001\u672A\u53CA\u65F6\u66F4\u65B0\uFF1B\u5F53\u524D\u4EF7\u683C\u53EF\u80FD\u8FC7\u671F\u3002\u4EA4\u6613\u7968\u636E\u5DF2\u6682\u505C\u63D0\u4EA4\uFF0C\u7B49\u5F85\u6570\u636E\u6062\u590D\u3002", "Market or host data is stale. Ticket submission is paused until data recovers.")) : null, s.notice ? /* @__PURE__ */ React32.createElement("div", { className: "dhl-notice", onClick: () => notify(null) }, /* @__PURE__ */ React32.createElement(UiIcon, { name: "info", size: 16 }), /* @__PURE__ */ React32.createElement("span", { style: { flex: 1 } }, s.notice.text), /* @__PURE__ */ React32.createElement(UiIcon, { name: "close", size: 14 })) : null), /* @__PURE__ */ React32.createElement("div", { className: "dhl-body" }, showSessions ? /* @__PURE__ */ React32.createElement(SessionsColumn, { width: layout.sessionsWidth, fill: !showDesk && !showChat, onCollapse: toggleSessions }) : /* @__PURE__ */ React32.createElement("button", { className: "dhl-panel-rail", type: "button", onClick: toggleSessions, "aria-label": tr("\u5C55\u5F00\u4F1A\u8BDD\u680F", "Expand sessions"), title: tr("\u5C55\u5F00\u4F1A\u8BDD\u680F", "Expand sessions") }, /* @__PURE__ */ React32.createElement(PanelIcon, { kind: "sessions" })), showSessions && (showDesk || showChat) ? /* @__PURE__ */ React32.createElement(Divider, { side: "left", onResize: resizeSessions, onCommit: commitLayout, onReset: resetSessions }) : null, showDesk ? /* @__PURE__ */ React32.createElement(DeskColumn, { onCollapse: toggleDesk }) : /* @__PURE__ */ React32.createElement("button", { className: "dhl-panel-rail", type: "button", onClick: toggleDesk, "aria-label": tr("\u5C55\u5F00\u884C\u60C5\u533A", "Expand market panel"), title: tr("\u5C55\u5F00\u884C\u60C5\u533A", "Expand market panel") }, /* @__PURE__ */ React32.createElement(PanelIcon, { kind: "desk" })), showDesk && showChat ? /* @__PURE__ */ React32.createElement(Divider, { side: "right", onResize: resizeChat, onCommit: commitLayout, onReset: resetChat }) : null, showChat ? /* @__PURE__ */ React32.createElement(ChatColumn, { width: layout.chatWidth, fill: !showDesk, onCollapse: toggleChat, SessionProvider: props.SessionProvider ?? props.sessionProvider, renderFactorySlot: props.renderFactorySlot }) : /* @__PURE__ */ React32.createElement("button", { className: "dhl-panel-rail", type: "button", onClick: toggleChat, "aria-label": tr("\u5C55\u5F00\u5BF9\u8BDD\u680F", "Expand chat"), title: tr("\u5C55\u5F00\u5BF9\u8BDD\u680F", "Expand chat") }, /* @__PURE__ */ React32.createElement(PanelIcon, { kind: "chat" }))));
22439
+ } }, tr("\u786E\u8BA4\u5207\u6362", "Switch"))) }, /* @__PURE__ */ React32.createElement("p", null, tr("\u4E3B\u7F51\u5B9E\u76D8\u4F1A\u663E\u793A\u771F\u5B9E\u884C\u60C5\u548C\u771F\u5B9E\u8D26\u6237\uFF0C\u540E\u7EED\u4EA4\u6613\u5C06\u4F7F\u7528\u771F\u5B9E\u8D44\u91D1\u3002", "Mainnet live shows real markets and your real account; trades use real funds."))), /* @__PURE__ */ React32.createElement(import_dsh_client_ui_primitives10.Modal, { open: confirmManaged, onClose: () => setConfirmManaged(false), title: tr("\u5F00\u542F\u5168\u6258\u7BA1", "Turn on managed mode"), closeLabel: t("modal.close", "\u5173\u95ED"), className: "dhl-network-modal", footer: /* @__PURE__ */ React32.createElement(React32.Fragment, null, /* @__PURE__ */ React32.createElement(import_dsh_client_ui_primitives10.Button, { variant: "outline", onClick: () => setConfirmManaged(false) }, t("modal.cancel", "\u53D6\u6D88")), /* @__PURE__ */ React32.createElement(import_dsh_client_ui_primitives10.Button, { variant: "primary", onClick: () => void chooseCustody("managed", true) }, tr("\u5F00\u542F\u5168\u6258\u7BA1", "Turn on"))) }, /* @__PURE__ */ React32.createElement("p", null, tr("\u5168\u6258\u7BA1\u540E\uFF0Cagent \u5728\u5F53\u524D\u5B9E\u76D8\u8D26\u6237\u4E2D\u53EF\u4EE5\uFF1A", "In managed mode the agent can, on this live account:")), /* @__PURE__ */ React32.createElement("ul", { className: "dhl-managed-list" }, /* @__PURE__ */ React32.createElement("li", null, tr("\u4E0D\u7ECF\u9010\u7B14\u6279\u51C6\u76F4\u63A5\u4E0B\u5355\u3001\u64A4\u5355\u548C\u8C03\u6574\u6760\u6746\uFF1B", "place, cancel and re-lever orders without per-trade approval;")), /* @__PURE__ */ React32.createElement("li", null, tr("\u81EA\u884C\u4FEE\u6539\u5141\u8BB8\u4EA4\u6613\u7684\u5E01\u5BF9\u4E0E\u5355\u7B14\u3001\u6BCF\u65E5\u3001\u4ED3\u4F4D\u3001\u6760\u6746\u4E0A\u9650\uFF1B", "change the allowed coins and the order, daily, position and leverage limits;")), /* @__PURE__ */ React32.createElement("li", null, tr("\u5728\u4E3B\u6C38\u7EED\u8D26\u6237\u4E0E HIP-3 \u4EA4\u6613\u6240\u4E4B\u95F4\u5212\u8F6C USDC \u4FDD\u8BC1\u91D1\u3002", "move USDC margin between the core perp account and HIP-3 dexes."))), /* @__PURE__ */ React32.createElement("p", null, tr("dsh-web3 \u94B1\u5305\u81EA\u8EAB\u7684\u7B7E\u540D\u7B56\u7565\u4ECD\u7136\u751F\u6548\u3002\u4F60\u53EF\u4EE5\u968F\u65F6\u5207\u56DE\u534A\u6258\u7BA1\u3002", "The dsh-web3 wallet signing policy still applies. You can switch back to assisted at any time."))), /* @__PURE__ */ React32.createElement(WalletDetails, { open: walletOpen, onClose: () => setWalletOpen(false) }), /* @__PURE__ */ React32.createElement(TradingLimits, { open: limitsOpen, onClose: () => setLimitsOpen(false) }), /* @__PURE__ */ React32.createElement("div", { className: "dhl-toasts" }, marketStale && !resyncing ? /* @__PURE__ */ React32.createElement("div", { className: "dhl-data-warning", role: "status" }, tr("\u884C\u60C5\u6216\u5BBF\u4E3B\u72B6\u6001\u672A\u53CA\u65F6\u66F4\u65B0\uFF1B\u5F53\u524D\u4EF7\u683C\u53EF\u80FD\u8FC7\u671F\u3002\u4EA4\u6613\u7968\u636E\u5DF2\u6682\u505C\u63D0\u4EA4\uFF0C\u7B49\u5F85\u6570\u636E\u6062\u590D\u3002", "Market or host data is stale. Ticket submission is paused until data recovers.")) : null, s.notice ? /* @__PURE__ */ React32.createElement("div", { className: "dhl-notice", onClick: () => notify(null) }, /* @__PURE__ */ React32.createElement(UiIcon, { name: "info", size: 16 }), /* @__PURE__ */ React32.createElement("span", { style: { flex: 1 } }, s.notice.text), /* @__PURE__ */ React32.createElement(UiIcon, { name: "close", size: 14 })) : null), /* @__PURE__ */ React32.createElement("div", { className: "dhl-body" }, showSessions ? /* @__PURE__ */ React32.createElement(SessionsColumn, { width: layout.sessionsWidth, fill: !showDesk && !showChat, onCollapse: toggleSessions }) : /* @__PURE__ */ React32.createElement("button", { className: "dhl-panel-rail", type: "button", onClick: toggleSessions, "aria-label": tr("\u5C55\u5F00\u4F1A\u8BDD\u680F", "Expand sessions"), title: tr("\u5C55\u5F00\u4F1A\u8BDD\u680F", "Expand sessions") }, /* @__PURE__ */ React32.createElement(PanelIcon, { kind: "sessions" })), showSessions && (showDesk || showChat) ? /* @__PURE__ */ React32.createElement(Divider, { side: "left", onResize: resizeSessions, onCommit: commitLayout, onReset: resetSessions }) : null, showDesk ? /* @__PURE__ */ React32.createElement(DeskColumn, { onCollapse: toggleDesk }) : /* @__PURE__ */ React32.createElement("button", { className: "dhl-panel-rail", type: "button", onClick: toggleDesk, "aria-label": tr("\u5C55\u5F00\u884C\u60C5\u533A", "Expand market panel"), title: tr("\u5C55\u5F00\u884C\u60C5\u533A", "Expand market panel") }, /* @__PURE__ */ React32.createElement(PanelIcon, { kind: "desk" })), showDesk && showChat ? /* @__PURE__ */ React32.createElement(Divider, { side: "right", onResize: resizeChat, onCommit: commitLayout, onReset: resetChat }) : null, showChat ? /* @__PURE__ */ React32.createElement(ChatColumn, { width: layout.chatWidth, fill: !showDesk, onCollapse: toggleChat, SessionProvider: props.SessionProvider ?? props.sessionProvider, renderFactorySlot: props.renderFactorySlot }) : /* @__PURE__ */ React32.createElement("button", { className: "dhl-panel-rail", type: "button", onClick: toggleChat, "aria-label": tr("\u5C55\u5F00\u5BF9\u8BDD\u680F", "Expand chat"), title: tr("\u5C55\u5F00\u5BF9\u8BDD\u680F", "Expand chat") }, /* @__PURE__ */ React32.createElement(PanelIcon, { kind: "chat" }))));
22159
22440
  }
22160
22441
 
22161
22442
  // src/client/components/panel-entry.tsx