dsh-xueqiu 1.22.0 → 1.22.2

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/README.en.md CHANGED
@@ -135,6 +135,9 @@ The data layer is defended in depth — safe to leave open for hours:
135
135
 
136
136
  ## Changelog
137
137
 
138
+ - **1.22.2** (2026-08-24) — **Fix: MA / avg-price lines landed in sub-panes instead of overlaying the main chart**. KLineChart v10's `createIndicator('MA')` opens a new pane by default — MAs were not on the candle pane (squeezed to 72px) and the minute chart's avg/prev-close lived in their own sub-pane. The fix passes `{ name, paneId: 'candle_pane' }` to explicitly overlay the main pane (pane id confirmed from engine source). Result: candlestick pane at 173px with MA5/10/30/60 overlaid (volume remains the only sub-pane); minute chart is a single pane with price + avg + prev-close overlaid, matching Xueqiu's layout. Also a Xueqiu-inspired detail-page restyle: hero quote header (large red/green price + change + pct), lighter 16-stat grid (tinted cards, tabular numerals), accent bar on card titles, theme-tinted active states for mode/period buttons, chart height 264→300px, harmonized 4-color MA palette, and high/low duplicate price marks disabled.
139
+ - **1.22.1** (2026-08-24) — chore: drop peerDependencies to silence install warnings; static smoke script falls back to local `npx dsh` (dsh not on PATH under prepublishOnly).
140
+ - **1.22.0** (2026-08-24) — **K-line / minute charts migrated to the KLineChart v10 engine** (canvas rendering, built-in light/dark theming): candles + volume + MA overlays + crosshair legend across 7 periods (5m/15m/30m/60m/day/week/month); wheel zoom, drag pan, and auto-fetch of earlier history on left-drag (500 bars per fetch); minute chart as a price-colored area with avg line and prev-close baseline.
138
141
  - **1.21.0** (2025-08-21) — **Windows support**. DSH's Windows shell layer is PowerShell (`pwsh -Command`), whose single-quote literal semantics match POSIX — the original "cmd quoting incompatible" obstacle turned out not to exist. Three adaptations: ① explicit `curl.exe` call (dodges PS 5.1's `curl` = `Invoke-WebRequest` alias; curl.exe ships with Win10+); ② the cookie-seeding `-o /dev/null` becomes `NUL` on Windows; ③ quote characters stripped from Cookie headers against injection. The `realShell` test stub now mirrors the platform (pwsh on win32, same as DSH's win32 layer). CI gains a `windows-latest` job: unit + cookie tests + live.mjs against real Xueqiu (51 assertions incl. anonymous seeding) — all green. README platform requirement updated to macOS / Linux / Windows.
139
142
  - **1.20.6** (2025-08-21) — **Cloud watchlist contract test wired into CI** (`qa/contract.mjs`, 7 assertions): hits the real endpoints (`portfolio/list.json`, `portfolio/stock/list.json`, `add.json`, `cancel.json`) including a genuine add→cancel round-trip with restore. Runs daily (UTC 21:00) via the `XQ_COOKIE` repo secret so any Xueqiu API change fails here first — preventing another `watch.json`-style phantom-endpoint incident. Skips automatically on push/PR without the secret; the same scheduled job also runs live.mjs (51 assertions). Requires setting the `XQ_COOKIE` secret (full browser Cookie header containing `xq_a_token`).
140
143
  - **1.20.5** (2025-08-21) — **Cloud watchlist dual-write never worked**: the previously used `watch.json` endpoint does not exist (the WAF's blanket 403 on unknown paths was misread as "Xueqiu blocks third-party writes"). The real endpoints are `portfolio/stock/add.json` / `cancel.json` (POST form, symbols param). Fixed and verified with a real-account full round-trip: plugin add → cloud 116 ✓ → plugin remove → cloud restored 115 ✓. Local add/remove now genuinely syncs both ways; the 1.20.4 "writes are blocked" claim is retracted.
package/README.md CHANGED
@@ -190,6 +190,13 @@ dsh-xueqiu/
190
190
 
191
191
  ## 📋 更新日志
192
192
 
193
+ - **1.22.2**(2026-08-24)
194
+ - 修复:**K线均线 / 分时均价误落附图**。KLineChart v10 的 `createIndicator('MA')` 默认给指标新开 pane——均线不在蜡烛主图上(主图被挤到 72px),分时均价/昨收也在独立附图。须传 `{ name, paneId: 'candle_pane' }` 显式叠加到主图(pane id 从引擎源码确认)。修复后:K线主图 173px 蜡烛+MA5/10/30/60 叠加、附图仅成交量;分时只剩一张主图(分时曲线+均价+昨收全部叠加),与雪球布局一致。
195
+ - 样式:**详情页参考雪球重排版**——顶部行情主标题(大号红/绿现价 + 涨跌额 + 涨跌幅,如 `1304.66 +31.83 +2.50%`);16 格 stat 卡片更轻盈(浅底/圆角/等宽数字/标签次要色);卡片标题左侧主题色强调条;K线/分时与周期按钮选中态改主题色 tint 填充;K线画布 264→300px;MA 均线和谐四色(蓝/橙/紫/青);关闭最高/最低点重复标注(消除窄图顶部杂乱数字)。
196
+ - **1.22.1**(2026-08-24)
197
+ - chore:移除 peerDependencies 避免安装警告;静态冒烟脚本自动兜底本地 `npx dsh`(prepublishOnly 下找不到 dsh 命令)。
198
+ - **1.22.0**(2026-08-24)
199
+ - 新增:**K线/分时切换 KLineChart v10 引擎**(canvas 渲染、内置明暗主题适配):蜡烛图 + 成交量 + MA 均线 + 十字光标,5分/15分/30分/60分/日K/周K/月K 7 档;滚轮缩放、拖拽平移、左拖触底自动加载更早历史(500 根/次);分时面积图(涨跌色)+ 均价线 + 昨收虚线。
193
200
  - **1.21.7**(2025-08-24)
194
201
  - 修复:**越屏钳制改为逐渲染执行**。1.21.6 的"effect 依赖 badgePos + ResizeObserver"方案在 CI 慢环境仍漏过"渲染后才长高"的时序(数据异步到达→徽章长高→无重触发钳制)。现在 `useLayoutEffect` 每次渲染后钳制(数据到达必伴随重渲染,无时序死角;ui.set 仅在有变更时触发下一轮,收敛不循环)。CI 实测踩坑修复。
195
202
  - 新增:**浏览器 E2E 进 CI**(`browser-e2e` job)。此前这层(唯一抓到过钳制死代码真 bug 的测试)只在本机手动跑。`scripts/e2e-ci-boot.sh` 合成"已完成引导"的隔离 DSH 实例(settings 跳过 onboarding + workspace 表 + 含完整一轮对话的逐行 zstd 会话日志——踩坑记录:DSH 逐记录写日志,每 zstd 帧恰好一行;slug 规则 cwd 去首 `/`、余 `/`→`-`、两端包 `--`),agent-browser 跑全部 18 项断言。E2E 脚本兜底:全新实例无活动对话时先展开会话分组再点开一个会话(conversation.input.dock 槽需要对话视图)。
package/dynamic/client.js CHANGED
@@ -121,20 +121,25 @@ return {
121
121
  '.xq-news-time{font-size:10.5px;color:var(--dsw-alias-label-secondary);margin-top:3px;}\n' +
122
122
  '.xq-news-more{font-size:11.5px;color:var(--dsw-alias-label-secondary);text-align:center;padding:6px 0 2px;cursor:pointer;user-select:none;}\n' +
123
123
  '.xq-news-more:hover{color:var(--dsw-alias-label-primary);}\n' +
124
- '.xq-detail-head{display:flex;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap;}\n' +
125
- '.xq-detail-name{font-size:15px;font-weight:700;}\n' +
126
- '.xq-detail-code{font-size:11px;color:var(--dsw-alias-label-secondary);}\n' +
127
- '.xq-detail-cur{font-size:20px;font-weight:800;}\n' +
128
- '.xq-detail-pct{font-size:13px;}\n' +
129
- '.xq-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:8px;}\n' +
130
- '.xq-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;padding:4px 7px;}\n' +
131
- '.xq-stat-k{font-size:10.5px;color:var(--dsw-alias-label-secondary);}\n' +
132
- '.xq-stat-v{font-size:12.5px;font-weight:600;}\n' +
133
- '.xq-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:8px 10px;margin-bottom:8px;}\n' +
134
- '.xq-card-t{font-size:12px;font-weight:700;margin-bottom:6px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;}\n' +
135
- '.xq-periods{display:inline-flex;flex-wrap:wrap;gap:2px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:1px;}\n' +
136
- '.xq-periods button,.xq-modes button{font-size:11.5px;border:none;background:none;color:var(--dsw-alias-label-secondary);padding:2px 9px;border-radius:5px;cursor:pointer;}\n' +
137
- '.xq-periods button.xq-on,.xq-modes button.xq-on{background:var(--dsw-alias-bg-overlay);color:var(--dsw-alias-label-primary);font-weight:600;}\n' +
124
+ '.xq-detail-head{display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap;}\n' +
125
+ '.xq-detail-name{font-size:16px;font-weight:700;letter-spacing:.2px;}\n' +
126
+ '.xq-detail-code{font-size:11px;color:var(--dsw-alias-label-tertiary);letter-spacing:.3px;}\n' +
127
+ '.xq-detail-price{display:flex;align-items:baseline;gap:10px;}\n' +
128
+ '.xq-detail-delta{display:flex;align-items:baseline;gap:6px;}\n' +
129
+ '.xq-detail-cur{font-size:27px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums;}\n' +
130
+ '.xq-detail-chg{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;}\n' +
131
+ '.xq-detail-pct{font-size:14px;font-weight:700;font-variant-numeric:tabular-nums;}\n' +
132
+ '.xq-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:12px;}\n' +
133
+ '.xq-stat{background:color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:6px 8px;min-width:0;}\n' +
134
+ '.xq-stat-k{font-size:10px;color:var(--dsw-alias-label-tertiary);margin-bottom:3px;letter-spacing:.2px;}\n' +
135
+ '.xq-stat-v{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\n' +
136
+ '.xq-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:10px 12px;margin-bottom:12px;}\n' +
137
+ '.xq-card-t{font-size:12.5px;font-weight:700;margin-bottom:8px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;}\n' +
138
+ '.xq-card-t::before{content:"";width:3px;height:12px;border-radius:2px;background:var(--dsw-alias-brand-primary);flex:none;}\n' +
139
+ '.xq-periods{display:inline-flex;flex-wrap:wrap;gap:2px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;padding:2px;background:var(--dsw-alias-bg-layer-2);}\n' +
140
+ '.xq-periods button,.xq-modes button{font-size:11.5px;border:none;background:none;color:var(--dsw-alias-label-secondary);padding:3px 9px;border-radius:6px;cursor:pointer;transition:color .12s,background .12s;}\n' +
141
+ '.xq-periods button:hover,.xq-modes button:hover{color:var(--dsw-alias-label-primary);}\n' +
142
+ '.xq-periods button.xq-on,.xq-modes button.xq-on{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 16%, transparent);color:var(--dsw-alias-brand-primary);font-weight:700;}\n' +
138
143
  '.xq-chart{width:100%;height:auto;display:block;}\n' +
139
144
  '.xq-chart-pan{cursor:crosshair;touch-action:none;}\n' +
140
145
  '.xq-chart-pan:active{cursor:grabbing;}\n' +
@@ -143,10 +148,10 @@ return {
143
148
  '.xq-tip-d{font-weight:700;}\n' +
144
149
  '.xq-tip-r{display:flex;gap:8px;}\n' +
145
150
  '.xq-tip-k{color:var(--dsw-alias-label-secondary);}\n' +
146
- '.xq-chart-labels{display:flex;justify-content:space-between;font-size:10.5px;color:var(--dsw-alias-label-secondary);margin-top:2px;}\n' +
151
+ '.xq-chart-labels{display:flex;justify-content:space-between;font-size:10.5px;color:var(--dsw-alias-label-tertiary);margin-top:3px;}\n' +
147
152
  '.xq-ma-legend{display:flex;gap:10px;font-size:10.5px;color:var(--dsw-alias-label-secondary);}\n' +
148
- '.xq-klc{height:264px;width:100%;min-width:0;}\n' +
149
- '.xq-klc-min{height:216px;}\n' +
153
+ '.xq-klc{height:300px;width:100%;min-width:0;}\n' +
154
+ '.xq-klc-min{height:230px;}\n' +
150
155
  '.xq-kol{display:flex;flex-wrap:wrap;gap:6px;}\n' +
151
156
  '.xq-kol-chip{font-size:12px;padding:4px 10px;border-radius:14px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);cursor:pointer;}\n' +
152
157
  '.xq-kol-chip:hover{border-color:var(--dsw-alias-brand-primary);}\n' +
@@ -351,13 +356,15 @@ return {
351
356
  upWickColor: pal.up, downWickColor: pal.down, noChangeWickColor: pal.text
352
357
  },
353
358
  priceMark: {
354
- high: { color: pal.text }, low: { color: pal.text },
359
+ // 关闭最高/最低点重复标注(窄图时易与坐标轴重叠成杂乱数字),保留最新价标
360
+ high: { show: false }, low: { show: false },
355
361
  last: { upColor: pal.up, downColor: pal.down, noChangeColor: pal.text, text: { color: pal.text } }
356
362
  }
357
363
  },
358
364
  indicator: {
359
365
  bars: [{ upColor: pal.up, downColor: pal.down, noChangeColor: pal.text }],
360
- lines: [{ color: pal.warn }, { color: '#3b82f6' }, { color: '#a855f7' }],
366
+ // 和谐 MA 配色:蓝/橙/紫/青,多均线更易区分(对齐雪球观感)
367
+ lines: [{ color: '#3b82f6' }, { color: '#f59e0b' }, { color: '#8b5cf6' }, { color: '#06b6d4' }],
361
368
  tooltip: { show: false } // 指标数值走蜡烛 legend,避免双份
362
369
  },
363
370
  xAxis: { axisLine: { color: pal.grid }, tickLine: { color: pal.grid }, tickText: { color: pal.text } },
@@ -465,7 +472,8 @@ return {
465
472
  }, function () { busy = false; done([], { forward: false, backward: false }) })
466
473
  }
467
474
  })
468
- chart.createIndicator('MA')
475
+ // 均线叠加到主图蜡烛 pane(paneId 指定已有主图,不传则默认新开附图 pane)——与雪球一致
476
+ chart.createIndicator({ name: 'MA', paneId: 'candle_pane' })
469
477
  chart.createIndicator('VOL')
470
478
  chart.setOffsetRightDistance(16)
471
479
  return h.dispose
@@ -496,7 +504,7 @@ return {
496
504
  React.useEffect(function () {
497
505
  const K = klcLib()
498
506
  if (!K || !boxRef.current) { setNoLib(true); return undefined }
499
- // 自定义指标:昨收虚线 + 均价线(series normal,不干扰价格轴刻度)
507
+ // 自定义指标:昨收虚线 + 均价线(series normal → 单独附图,避免干扰价格轴刻度)
500
508
  if (K.getSupportedIndicators().indexOf('xq-minute') < 0) {
501
509
  K.registerIndicator({
502
510
  name: 'xq-minute', shortName: '分时', series: 'normal',
@@ -541,7 +549,7 @@ return {
541
549
  const up = lastClose ? last >= lastClose : true
542
550
  // 涨跌色改面积线色(palette up 色对A股即"涨")
543
551
  chart.setStyles({ candle: { type: 'area', area: { lineColor: up ? klcPalette().up : klcPalette().down, backgroundColor: up ? 'rgba(239,68,68,0.10)' : 'rgba(34,197,94,0.10)' } } })
544
- chart.createIndicator('xq-minute')
552
+ chart.createIndicator({ name: 'xq-minute', paneId: 'candle_pane' }) // 均价/昨收叠加到主图分时曲线(与雪球一致)
545
553
  // 分时固定视角:禁缩放拖拽,全部笔数铺满宽度
546
554
  chart.setZoomEnabled(false)
547
555
  chart.setScrollEnabled(false)
@@ -1013,14 +1021,23 @@ return {
1013
1021
  ' · ' + fmtBig(u.followers_count) + '粉'
1014
1022
  ])
1015
1023
  })
1024
+ // 行情主标题:现价 + 涨跌额 + 涨跌幅(参考雪球 ¥18.23 +0.14 +0.77% 层级)
1025
+ const chgRaw = (q.current != null && q.last_close != null) ? (Number(q.current) - Number(q.last_close)) : null
1026
+ const chgStr = (chgRaw != null && isFinite(chgRaw)) ? (chgRaw > 0 ? '+' + fmt(chgRaw) : (chgRaw < 0 ? '-' + fmt(Math.abs(chgRaw)) : '0.00')) : ''
1027
+ const priceCls = colorOf(q.percent)
1016
1028
  return el('div', null, [
1017
1029
  el('div', { key: 'h', className: 'xq-detail-head' }, [
1018
1030
  el('button', { key: 'b', className: 'xq-btn-mini', onClick: function () { setView(null) } }, '← 返回'),
1019
1031
  el('span', { key: 'n', className: 'xq-detail-name' }, q.name || view),
1020
1032
  el('span', { key: 'c', className: 'xq-detail-code' }, view),
1021
1033
  el('span', { key: 'sp', className: 'xq-spacer' }),
1022
- el('span', { key: 'cur', className: 'xq-detail-cur ' + colorOf(q.percent) }, fmt(q.current)),
1023
- el('span', { key: 'pct', className: 'xq-detail-pct ' + colorOf(q.percent) }, fmtPct(q.percent))
1034
+ el('div', { key: 'px', className: 'xq-detail-price' }, [
1035
+ el('span', { key: 'cur', className: 'xq-detail-cur ' + priceCls }, fmt(q.current)),
1036
+ el('span', { key: 'dl', className: 'xq-detail-delta' }, [
1037
+ chgStr ? el('span', { key: 'chg', className: 'xq-detail-chg ' + priceCls }, chgStr) : null,
1038
+ el('span', { key: 'pct', className: 'xq-detail-pct ' + priceCls }, fmtPct(q.percent))
1039
+ ])
1040
+ ])
1024
1041
  ]),
1025
1042
  el('div', { key: 'st', className: 'xq-stats' }, stats.map(function (s, i) {
1026
1043
  return el('div', { key: i, className: 'xq-stat' }, [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-xueqiu",
3
- "version": "1.22.0",
3
+ "version": "1.22.2",
4
4
  "description": "雪球 mini 行情面板 — DeepSeek Harness 免登录 A股/港美股实时行情、K线(蜡烛/成交量/均线)、分时图、热榜、搜索、7x24 快讯、热议用户。嵌入式面板 + 可拖拽行情徽标,内置请求节流、TTL 缓存与 Cookie 自动续期。",
5
5
  "keywords": [
6
6
  "dsh",
@@ -41,12 +41,6 @@
41
41
  "patch": "./cordis.patch.yml"
42
42
  }
43
43
  },
44
- "peerDependencies": {
45
- "@deepseek-ai/cordis": "^4.0.1",
46
- "@deepseek-ai/dsh-client-connection": "^0.1.0-rc.6",
47
- "react": "^18.3.1",
48
- "@deepseek-ai/dsh-tools": ">=0.1.0-rc.1 <0.2.0"
49
- },
50
44
  "files": [
51
45
  "src/index.js",
52
46
  "src/client/index.js",
@@ -152,20 +152,25 @@ exports.default = {
152
152
  '.xq-news-time{font-size:10.5px;color:var(--dsw-alias-label-secondary);margin-top:3px;}\n' +
153
153
  '.xq-news-more{font-size:11.5px;color:var(--dsw-alias-label-secondary);text-align:center;padding:6px 0 2px;cursor:pointer;user-select:none;}\n' +
154
154
  '.xq-news-more:hover{color:var(--dsw-alias-label-primary);}\n' +
155
- '.xq-detail-head{display:flex;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap;}\n' +
156
- '.xq-detail-name{font-size:15px;font-weight:700;}\n' +
157
- '.xq-detail-code{font-size:11px;color:var(--dsw-alias-label-secondary);}\n' +
158
- '.xq-detail-cur{font-size:20px;font-weight:800;}\n' +
159
- '.xq-detail-pct{font-size:13px;}\n' +
160
- '.xq-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:8px;}\n' +
161
- '.xq-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;padding:4px 7px;}\n' +
162
- '.xq-stat-k{font-size:10.5px;color:var(--dsw-alias-label-secondary);}\n' +
163
- '.xq-stat-v{font-size:12.5px;font-weight:600;}\n' +
164
- '.xq-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:8px 10px;margin-bottom:8px;}\n' +
165
- '.xq-card-t{font-size:12px;font-weight:700;margin-bottom:6px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;}\n' +
166
- '.xq-periods{display:inline-flex;flex-wrap:wrap;gap:2px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:1px;}\n' +
167
- '.xq-periods button,.xq-modes button{font-size:11.5px;border:none;background:none;color:var(--dsw-alias-label-secondary);padding:2px 9px;border-radius:5px;cursor:pointer;}\n' +
168
- '.xq-periods button.xq-on,.xq-modes button.xq-on{background:var(--dsw-alias-bg-overlay);color:var(--dsw-alias-label-primary);font-weight:600;}\n' +
155
+ '.xq-detail-head{display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap;}\n' +
156
+ '.xq-detail-name{font-size:16px;font-weight:700;letter-spacing:.2px;}\n' +
157
+ '.xq-detail-code{font-size:11px;color:var(--dsw-alias-label-tertiary);letter-spacing:.3px;}\n' +
158
+ '.xq-detail-price{display:flex;align-items:baseline;gap:10px;}\n' +
159
+ '.xq-detail-delta{display:flex;align-items:baseline;gap:6px;}\n' +
160
+ '.xq-detail-cur{font-size:27px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums;}\n' +
161
+ '.xq-detail-chg{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;}\n' +
162
+ '.xq-detail-pct{font-size:14px;font-weight:700;font-variant-numeric:tabular-nums;}\n' +
163
+ '.xq-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:12px;}\n' +
164
+ '.xq-stat{background:color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:6px 8px;min-width:0;}\n' +
165
+ '.xq-stat-k{font-size:10px;color:var(--dsw-alias-label-tertiary);margin-bottom:3px;letter-spacing:.2px;}\n' +
166
+ '.xq-stat-v{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\n' +
167
+ '.xq-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:10px 12px;margin-bottom:12px;}\n' +
168
+ '.xq-card-t{font-size:12.5px;font-weight:700;margin-bottom:8px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;}\n' +
169
+ '.xq-card-t::before{content:"";width:3px;height:12px;border-radius:2px;background:var(--dsw-alias-brand-primary);flex:none;}\n' +
170
+ '.xq-periods{display:inline-flex;flex-wrap:wrap;gap:2px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;padding:2px;background:var(--dsw-alias-bg-layer-2);}\n' +
171
+ '.xq-periods button,.xq-modes button{font-size:11.5px;border:none;background:none;color:var(--dsw-alias-label-secondary);padding:3px 9px;border-radius:6px;cursor:pointer;transition:color .12s,background .12s;}\n' +
172
+ '.xq-periods button:hover,.xq-modes button:hover{color:var(--dsw-alias-label-primary);}\n' +
173
+ '.xq-periods button.xq-on,.xq-modes button.xq-on{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 16%, transparent);color:var(--dsw-alias-brand-primary);font-weight:700;}\n' +
169
174
  '.xq-chart{width:100%;height:auto;display:block;}\n' +
170
175
  '.xq-chart-pan{cursor:crosshair;touch-action:none;}\n' +
171
176
  '.xq-chart-pan:active{cursor:grabbing;}\n' +
@@ -174,10 +179,10 @@ exports.default = {
174
179
  '.xq-tip-d{font-weight:700;}\n' +
175
180
  '.xq-tip-r{display:flex;gap:8px;}\n' +
176
181
  '.xq-tip-k{color:var(--dsw-alias-label-secondary);}\n' +
177
- '.xq-chart-labels{display:flex;justify-content:space-between;font-size:10.5px;color:var(--dsw-alias-label-secondary);margin-top:2px;}\n' +
182
+ '.xq-chart-labels{display:flex;justify-content:space-between;font-size:10.5px;color:var(--dsw-alias-label-tertiary);margin-top:3px;}\n' +
178
183
  '.xq-ma-legend{display:flex;gap:10px;font-size:10.5px;color:var(--dsw-alias-label-secondary);}\n' +
179
- '.xq-klc{height:264px;width:100%;min-width:0;}\n' +
180
- '.xq-klc-min{height:216px;}\n' +
184
+ '.xq-klc{height:300px;width:100%;min-width:0;}\n' +
185
+ '.xq-klc-min{height:230px;}\n' +
181
186
  '.xq-kol{display:flex;flex-wrap:wrap;gap:6px;}\n' +
182
187
  '.xq-kol-chip{font-size:12px;padding:4px 10px;border-radius:14px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);cursor:pointer;}\n' +
183
188
  '.xq-kol-chip:hover{border-color:var(--dsw-alias-brand-primary);}\n' +
@@ -382,13 +387,15 @@ exports.default = {
382
387
  upWickColor: pal.up, downWickColor: pal.down, noChangeWickColor: pal.text
383
388
  },
384
389
  priceMark: {
385
- high: { color: pal.text }, low: { color: pal.text },
390
+ // 关闭最高/最低点重复标注(窄图时易与坐标轴重叠成杂乱数字),保留最新价标
391
+ high: { show: false }, low: { show: false },
386
392
  last: { upColor: pal.up, downColor: pal.down, noChangeColor: pal.text, text: { color: pal.text } }
387
393
  }
388
394
  },
389
395
  indicator: {
390
396
  bars: [{ upColor: pal.up, downColor: pal.down, noChangeColor: pal.text }],
391
- lines: [{ color: pal.warn }, { color: '#3b82f6' }, { color: '#a855f7' }],
397
+ // 和谐 MA 配色:蓝/橙/紫/青,多均线更易区分(对齐雪球观感)
398
+ lines: [{ color: '#3b82f6' }, { color: '#f59e0b' }, { color: '#8b5cf6' }, { color: '#06b6d4' }],
392
399
  tooltip: { show: false } // 指标数值走蜡烛 legend,避免双份
393
400
  },
394
401
  xAxis: { axisLine: { color: pal.grid }, tickLine: { color: pal.grid }, tickText: { color: pal.text } },
@@ -496,7 +503,8 @@ exports.default = {
496
503
  }, function () { busy = false; done([], { forward: false, backward: false }) })
497
504
  }
498
505
  })
499
- chart.createIndicator('MA')
506
+ // 均线叠加到主图蜡烛 pane(paneId 指定已有主图,不传则默认新开附图 pane)——与雪球一致
507
+ chart.createIndicator({ name: 'MA', paneId: 'candle_pane' })
500
508
  chart.createIndicator('VOL')
501
509
  chart.setOffsetRightDistance(16)
502
510
  return h.dispose
@@ -527,7 +535,7 @@ exports.default = {
527
535
  React.useEffect(function () {
528
536
  const K = klcLib()
529
537
  if (!K || !boxRef.current) { setNoLib(true); return undefined }
530
- // 自定义指标:昨收虚线 + 均价线(series normal,不干扰价格轴刻度)
538
+ // 自定义指标:昨收虚线 + 均价线(series normal → 单独附图,避免干扰价格轴刻度)
531
539
  if (K.getSupportedIndicators().indexOf('xq-minute') < 0) {
532
540
  K.registerIndicator({
533
541
  name: 'xq-minute', shortName: '分时', series: 'normal',
@@ -572,7 +580,7 @@ exports.default = {
572
580
  const up = lastClose ? last >= lastClose : true
573
581
  // 涨跌色改面积线色(palette up 色对A股即"涨")
574
582
  chart.setStyles({ candle: { type: 'area', area: { lineColor: up ? klcPalette().up : klcPalette().down, backgroundColor: up ? 'rgba(239,68,68,0.10)' : 'rgba(34,197,94,0.10)' } } })
575
- chart.createIndicator('xq-minute')
583
+ chart.createIndicator({ name: 'xq-minute', paneId: 'candle_pane' }) // 均价/昨收叠加到主图分时曲线(与雪球一致)
576
584
  // 分时固定视角:禁缩放拖拽,全部笔数铺满宽度
577
585
  chart.setZoomEnabled(false)
578
586
  chart.setScrollEnabled(false)
@@ -1044,14 +1052,23 @@ exports.default = {
1044
1052
  ' · ' + fmtBig(u.followers_count) + '粉'
1045
1053
  ])
1046
1054
  })
1055
+ // 行情主标题:现价 + 涨跌额 + 涨跌幅(参考雪球 ¥18.23 +0.14 +0.77% 层级)
1056
+ const chgRaw = (q.current != null && q.last_close != null) ? (Number(q.current) - Number(q.last_close)) : null
1057
+ const chgStr = (chgRaw != null && isFinite(chgRaw)) ? (chgRaw > 0 ? '+' + fmt(chgRaw) : (chgRaw < 0 ? '-' + fmt(Math.abs(chgRaw)) : '0.00')) : ''
1058
+ const priceCls = colorOf(q.percent)
1047
1059
  return el('div', null, [
1048
1060
  el('div', { key: 'h', className: 'xq-detail-head' }, [
1049
1061
  el('button', { key: 'b', className: 'xq-btn-mini', onClick: function () { setView(null) } }, '← 返回'),
1050
1062
  el('span', { key: 'n', className: 'xq-detail-name' }, q.name || view),
1051
1063
  el('span', { key: 'c', className: 'xq-detail-code' }, view),
1052
1064
  el('span', { key: 'sp', className: 'xq-spacer' }),
1053
- el('span', { key: 'cur', className: 'xq-detail-cur ' + colorOf(q.percent) }, fmt(q.current)),
1054
- el('span', { key: 'pct', className: 'xq-detail-pct ' + colorOf(q.percent) }, fmtPct(q.percent))
1065
+ el('div', { key: 'px', className: 'xq-detail-price' }, [
1066
+ el('span', { key: 'cur', className: 'xq-detail-cur ' + priceCls }, fmt(q.current)),
1067
+ el('span', { key: 'dl', className: 'xq-detail-delta' }, [
1068
+ chgStr ? el('span', { key: 'chg', className: 'xq-detail-chg ' + priceCls }, chgStr) : null,
1069
+ el('span', { key: 'pct', className: 'xq-detail-pct ' + priceCls }, fmtPct(q.percent))
1070
+ ])
1071
+ ])
1055
1072
  ]),
1056
1073
  el('div', { key: 'st', className: 'xq-stats' }, stats.map(function (s, i) {
1057
1074
  return el('div', { key: i, className: 'xq-stat' }, [