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.
- package/backend/__pycache__/panel.cpython-314.pyc +0 -0
- package/backend/panel.html +25 -11
- package/backend/panel.py +13 -1
- package/lib/client.js +15 -1
- package/lib/index.js +1 -1
- package/package.json +1 -1
|
Binary file
|
package/backend/panel.html
CHANGED
|
@@ -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)
|
|
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
|
-
|
|
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
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
-
|
|
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=
|
|
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 {
|
|
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(), '.
|
|
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.
|
|
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",
|