dsh-project-deck 0.1.4 → 0.1.6

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.
@@ -357,7 +357,11 @@ function render(){
357
357
  const banner = $('wxBanner');
358
358
  if (banner) {
359
359
  banner.style.display = wxStale ? 'block' : 'none';
360
- if (wxStale) banner.innerHTML = '⚠️ 微信通道 context_token 已过期:这段时间的播报<b>不会排队堆积</b>。在微信里给 bot 发任意一句话,下一轮播报自动恢复。';
360
+ if (wxStale) {
361
+ banner.innerHTML = (WX && WX.mode === 'ilink')
362
+ ? '⚠️ 微信还没连接:点下面「连接微信」扫码,<b>扫码后还要在微信里给 bot 发一句话</b>,之后播报就能推送到微信。'
363
+ : '⚠️ 微信通道 context_token 已过期:这段时间的播报<b>不会排队堆积</b>。在微信里给 bot 发任意一句话,下一轮播报自动恢复。';
364
+ }
361
365
  }
362
366
  $('nextText').textContent = S.daemon.nextRun ? shortTime(S.daemon.nextRun) : '—';
363
367
 
@@ -621,8 +625,7 @@ async function refreshWechat(){
621
625
  const needQr = WX.mode === 'ilink' && !WX.logged && WX.qrUrl;
622
626
  $('wxQrWrap').style.display = needQr ? 'block' : 'none';
623
627
  if (needQr) $('wxQr').src = '/api/wechat/qr.png?t=' + Date.now();
624
- $('wxLoginBtn').style.display = (WX.mode === 'ilink' && !WX.logged) ? '' : 'none';
625
- $('wxLogoutBtn').style.display = (WX.mode === 'ilink' && WX.logged) ? '' : 'none';
628
+ updateWxButtons();
626
629
  if (WX.mode === 'ilink' && !WX.ok) {
627
630
  if (!wxTimer) wxTimer = setInterval(refreshWechat, 2500);
628
631
  } else if (wxTimer) { clearInterval(wxTimer); wxTimer = null; }
@@ -641,6 +644,13 @@ const WX_FIELD = {
641
644
  ilink: null,
642
645
  };
643
646
 
647
+ function updateWxButtons(){
648
+ const type = $('wxType') ? $('wxType').value : 'wxpush';
649
+ const logged = !!(WX && WX.logged);
650
+ $('wxLoginBtn').style.display = (type === 'ilink' && !logged) ? '' : 'none';
651
+ $('wxLogoutBtn').style.display = (type === 'ilink' && logged) ? '' : 'none';
652
+ }
653
+
644
654
  function onWxTypeChange(){
645
655
  const type = $('wxType').value;
646
656
  const f = WX_FIELD[type];
@@ -651,20 +661,23 @@ function onWxTypeChange(){
651
661
  $('wxField').placeholder = f[1];
652
662
  $('wxField').value = cur[f[0]] || '';
653
663
  }
654
- const isIlink = type === 'ilink';
655
- $('wxLoginBtn').style.display = isIlink ? '' : 'none';
656
- $('wxLogoutBtn').style.display = 'none';
664
+ // 内置微信没有额外参数:下拉即切换并落盘,随后出现「连接微信」
665
+ if (!f) applyDelivery({ type });
666
+ }
667
+
668
+ async function applyDelivery(delivery){
669
+ try{
670
+ const r = await api('/api/config', { delivery });
671
+ S = r.state; render(); refreshWechat();
672
+ toast('通知渠道 → ' + delivery.type);
673
+ }catch(e){ toast('切换失败:' + e.message, true); }
657
674
  }
658
675
 
659
676
  async function saveWx(){
660
677
  const type = $('wxType').value;
661
678
  const f = WX_FIELD[type];
662
679
  if (!f) return;
663
- const delivery = { type, [f[0]]: $('wxField').value.trim() };
664
- try{
665
- const r = await api('/api/config', { delivery });
666
- S = r.state; render(); refreshWechat(); toast('通知渠道已更新');
667
- }catch(e){ toast('保存失败:' + e.message, true); }
680
+ await applyDelivery({ type, [f[0]]: $('wxField').value.trim() });
668
681
  }
669
682
 
670
683
  function setFabPos(pos){
@@ -932,6 +945,7 @@ refresh().then(() => {
932
945
  const h = location.hash;
933
946
  if (h.startsWith('#p=')) openDetail(decodeURIComponent(h.slice(3)));
934
947
  });
948
+ refreshWechat();
935
949
  loadScan();
936
950
  loadImages();
937
951
  setInterval(tick, 1000);
package/backend/panel.py CHANGED
@@ -47,6 +47,16 @@ P = load_deck()
47
47
 
48
48
 
49
49
  # ── 数据汇总 ────────────────────────────────────────────────
50
+ def channel_stale_now():
51
+ d = P.delivery_cfg()
52
+ if d['type'] == 'ilink':
53
+ w = P.wechat_status()
54
+ return not (w['logged'] and w['hasContext'])
55
+ if d['type'] == 'wxpush':
56
+ return P.channel_stale()
57
+ return not P.delivery_status().get('ok')
58
+
59
+
50
60
  def wechat_view():
51
61
  d = P.delivery_cfg()
52
62
  if d['type'] == 'ilink':
@@ -127,7 +137,7 @@ def snapshot():
127
137
  'daemon': {'running': bool(P.daemon_status()), 'pid': P.daemon_status() or 0,
128
138
  'lastRun': last, 'nextRun': (last + interval * 3600) if last else 0},
129
139
  'wechat': dict(wechat_state(),
130
- stale=P.channel_stale(),
140
+ stale=channel_stale_now(),
131
141
  fails=P.channel_state().get('fails', 0),
132
142
  lastError=P.channel_state().get('lastError', '')),
133
143
  'counts': {
@@ -784,6 +794,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
784
794
  self.send_header('Content-Type', 'application/json; charset=utf-8')
785
795
  self.send_header('Content-Length', str(len(body)))
786
796
  self.send_header('Access-Control-Allow-Origin', '*')
797
+ self.send_header('Cache-Control', 'no-store')
787
798
  self.end_headers()
788
799
  self.wfile.write(body)
789
800
 
@@ -796,6 +807,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
796
807
  self.send_response(200)
797
808
  self.send_header('Content-Type', 'text/html; charset=utf-8')
798
809
  self.send_header('Content-Length', str(len(body)))
810
+ self.send_header('Cache-Control', 'no-store')
799
811
  self.end_headers()
800
812
  self.wfile.write(body)
801
813
 
package/lib/client.js CHANGED
@@ -135,7 +135,15 @@ window.__ModuleLoader__.load({
135
135
 
136
136
  // 悬浮按钮可拖到页面任意位置(位置持久化)
137
137
  const [pos, setPos] = React.useState(() => {
138
- try { const v = JSON.parse(localStorage.getItem('deck-fab-pos') || 'null'); return v && typeof v.x === 'number' ? v : null } catch { return null }
138
+ try {
139
+ const v = JSON.parse(localStorage.getItem('deck-fab-pos') || 'null')
140
+ if (!v || typeof v.x !== 'number' || typeof v.y !== 'number') return null
141
+ // 旧版本可能存过越界坐标,载入时先夹到可视区域内
142
+ return {
143
+ x: Math.max(6, Math.min(v.x, window.innerWidth - 54)),
144
+ y: Math.max(6, Math.min(v.y, window.innerHeight - 54)),
145
+ }
146
+ } catch { return null }
139
147
  })
140
148
  const [summary, setSummary] = React.useState({ auto: 0, assist: 0, pending: 0 })
141
149
  React.useEffect(() => {
@@ -167,6 +175,12 @@ window.__ModuleLoader__.load({
167
175
  y: Math.max(6, Math.min(y, window.innerHeight - 54)),
168
176
  })
169
177
 
178
+ React.useEffect(() => {
179
+ const onResize = () => setPos((cur) => (cur ? clamp(cur.x, cur.y) : cur))
180
+ window.addEventListener('resize', onResize)
181
+ return () => window.removeEventListener('resize', onResize)
182
+ }, [])
183
+
170
184
  // 用 window 级监听,避免指针移出按钮就丢事件(之前只能按住按钮原地挪)
171
185
  React.useEffect(() => {
172
186
  const onMove = (e) => {
package/lib/index.js CHANGED
@@ -9,7 +9,7 @@ import { join } from 'node:path'
9
9
  export const name = 'dsh-project-deck'
10
10
  export const inject = ['tools']
11
11
 
12
- const PAGENT = join(homedir(), '.person-agent', 'bin', 'deck')
12
+ const PAGENT = join(process.env.DECK_HOME || join(homedir(), '.project-deck'), 'bin', 'deck')
13
13
 
14
14
  function run(args, timeout = 60000) {
15
15
  return new Promise((resolve) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-project-deck",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "DeepSeek Harness plugin: a project deck for your repos — registry, dashboard, scheduled WeChat reports, auto-discovery, multi-node scanning.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",