nx-kv 0.8.2 → 0.8.4
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/CHANGELOG.md +44 -0
- package/package.json +1 -1
- package/src/modules/localtask/index.js +26 -0
- package/src/modules/localtask/service.js +291 -118
- package/src/modules/localtask/view.jsx +102 -9
- package/src/runtime/api.js +5 -0
- package/src/web/public/assets/{CliHints-DNK3TaU5.js → CliHints-D70_R4vQ.js} +1 -1
- package/src/web/public/assets/{index-Iz1qH6TC.js → index-2OK9SG2y.js} +2 -2
- package/src/web/public/assets/{view-7pNoeCnZ.js → view-C5ictDJi.js} +1 -1
- package/src/web/public/assets/view-DxwcTgBZ.js +1 -0
- package/src/web/public/assets/{view-D_gCbqXF.js → view-gzBv_kjb.js} +1 -1
- package/src/web/public/assets/{view-Dy0w1I4l.js → view-l_lnGEBC.js} +1 -1
- package/src/web/public/index.html +1 -1
- package/src/web/public/assets/view-CCkuLOoM.js +0 -1
|
@@ -54,6 +54,11 @@ export default function SessionTaskView() {
|
|
|
54
54
|
const [suffixEditing, setSuffixEditing] = useState(false);
|
|
55
55
|
const [suffixDraft, setSuffixDraft] = useState('');
|
|
56
56
|
const [suffixSaving, setSuffixSaving] = useState(false);
|
|
57
|
+
// 探活状态:'connecting' | 'ok' | 'reconnecting'(数据轮询 + 后续 SSE 接入)
|
|
58
|
+
const [liveStatus, setLiveStatus] = useState('connecting');
|
|
59
|
+
// 「只读弹窗」:点击现状/上一轮总结/任务文本时显示聚焦阅读的模态框
|
|
60
|
+
// modal = { title, body } | null
|
|
61
|
+
const [modal, setModal] = useState(null);
|
|
57
62
|
const [editingId, setEditingId] = useState(null);
|
|
58
63
|
const [editText, setEditText] = useState('');
|
|
59
64
|
const [editSaving, setEditSaving] = useState(false);
|
|
@@ -69,11 +74,14 @@ export default function SessionTaskView() {
|
|
|
69
74
|
|
|
70
75
|
const load = useCallback(async () => {
|
|
71
76
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
const data = await api('/api/localtasks');
|
|
78
|
+
setOverview(data);
|
|
79
|
+
setLiveStatus('ok');
|
|
80
|
+
} catch {
|
|
81
|
+
// 轮询失败 → 进入「重连中」态;指示灯变红,避免每 1.5s 弹错误 toast
|
|
82
|
+
setLiveStatus('reconnecting');
|
|
75
83
|
}
|
|
76
|
-
}, [
|
|
84
|
+
}, []);
|
|
77
85
|
|
|
78
86
|
useEffect(() => {
|
|
79
87
|
load();
|
|
@@ -85,6 +93,16 @@ export default function SessionTaskView() {
|
|
|
85
93
|
return () => clearInterval(t);
|
|
86
94
|
}, [load]);
|
|
87
95
|
|
|
96
|
+
// 只读弹窗:按 Esc 关闭
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (!modal) return;
|
|
99
|
+
const onKey = (e) => {
|
|
100
|
+
if (e.key === 'Escape') setModal(null);
|
|
101
|
+
};
|
|
102
|
+
window.addEventListener('keydown', onKey);
|
|
103
|
+
return () => window.removeEventListener('keydown', onKey);
|
|
104
|
+
}, [modal]);
|
|
105
|
+
|
|
88
106
|
const topics = overview ? overview.topics : [];
|
|
89
107
|
|
|
90
108
|
// 同步页面上的超时配置(来源:后端 settings)
|
|
@@ -495,7 +513,35 @@ export default function SessionTaskView() {
|
|
|
495
513
|
style={{ margin: 0 }}
|
|
496
514
|
/>
|
|
497
515
|
<h3>主题总览(按工作目录)</h3>
|
|
498
|
-
<span
|
|
516
|
+
<span
|
|
517
|
+
className="muted"
|
|
518
|
+
style={{
|
|
519
|
+
fontSize: 11,
|
|
520
|
+
display: 'inline-flex',
|
|
521
|
+
alignItems: 'center',
|
|
522
|
+
gap: 4,
|
|
523
|
+
color: liveStatus === 'ok' ? '#2a9d5c' : liveStatus === 'reconnecting' ? '#c0392b' : '#888',
|
|
524
|
+
}}
|
|
525
|
+
title={
|
|
526
|
+
liveStatus === 'ok'
|
|
527
|
+
? '实时连接正常(数据轮询可达后端)'
|
|
528
|
+
: liveStatus === 'reconnecting'
|
|
529
|
+
? '连接断开,正在重连…'
|
|
530
|
+
: '正在连接…'
|
|
531
|
+
}
|
|
532
|
+
>
|
|
533
|
+
<span
|
|
534
|
+
style={{
|
|
535
|
+
display: 'inline-block',
|
|
536
|
+
width: 8,
|
|
537
|
+
height: 8,
|
|
538
|
+
borderRadius: '50%',
|
|
539
|
+
background: liveStatus === 'ok' ? '#2a9d5c' : liveStatus === 'reconnecting' ? '#c0392b' : '#aaa',
|
|
540
|
+
}}
|
|
541
|
+
/>
|
|
542
|
+
{liveStatus === 'ok' ? '实时连接正常' : liveStatus === 'reconnecting' ? '连接断开,正在重连…' : '正在连接…'}
|
|
543
|
+
</span>
|
|
544
|
+
<span className="muted">{overview ? ` · ${topics.length} 个 · 等待中 ${overview.waiting}` : ''}</span>
|
|
499
545
|
<span style={{ marginLeft: 'auto', display: 'flex', gap: 8 }}>
|
|
500
546
|
<button
|
|
501
547
|
className="btn small ghost"
|
|
@@ -557,7 +603,12 @@ export default function SessionTaskView() {
|
|
|
557
603
|
</div>
|
|
558
604
|
</div>
|
|
559
605
|
{t.summary && (
|
|
560
|
-
<div
|
|
606
|
+
<div
|
|
607
|
+
className="muted"
|
|
608
|
+
style={{ fontSize: 11, whiteSpace: 'pre-wrap', marginBottom: 6, cursor: 'zoom-in' }}
|
|
609
|
+
title="点击查看完整现状"
|
|
610
|
+
onClick={(e) => { e.stopPropagation(); setModal({ title: `主题「${t.name}」现状`, body: t.summary }); }}
|
|
611
|
+
>
|
|
561
612
|
现状:{t.summary}
|
|
562
613
|
</div>
|
|
563
614
|
)}
|
|
@@ -712,12 +763,22 @@ export default function SessionTaskView() {
|
|
|
712
763
|
</>
|
|
713
764
|
) : (
|
|
714
765
|
<>
|
|
715
|
-
|
|
766
|
+
<span
|
|
767
|
+
style={{ cursor: t.text.length > 50 ? 'zoom-in' : 'default' }}
|
|
768
|
+
onClick={(e) => {
|
|
769
|
+
e.stopPropagation();
|
|
770
|
+
if (t.text.length > 50) setModal({ title: `任务 #${t.id} 全文`, body: t.text });
|
|
771
|
+
}}
|
|
772
|
+
title={t.text.length > 50 ? '点击查看完整任务文本' : undefined}
|
|
773
|
+
>
|
|
774
|
+
{t.text}
|
|
775
|
+
</span>
|
|
716
776
|
{t.roundSummary && (
|
|
717
777
|
<div
|
|
718
778
|
className="muted"
|
|
719
|
-
style={{ fontSize: 11, marginTop: 4, paddingTop: 4, borderTop: '1px dashed var(--line,#e6e8eb)', whiteSpace: 'pre-wrap' }}
|
|
720
|
-
title="
|
|
779
|
+
style={{ fontSize: 11, marginTop: 4, paddingTop: 4, borderTop: '1px dashed var(--line,#e6e8eb)', whiteSpace: 'pre-wrap', cursor: 'zoom-in' }}
|
|
780
|
+
title="点击查看完整上一轮总结"
|
|
781
|
+
onClick={(e) => { e.stopPropagation(); setModal({ title: '上一轮完成总结', body: t.roundSummary }); }}
|
|
721
782
|
>
|
|
722
783
|
上一轮完成:{t.roundSummary}
|
|
723
784
|
</div>
|
|
@@ -777,6 +838,38 @@ export default function SessionTaskView() {
|
|
|
777
838
|
)}
|
|
778
839
|
|
|
779
840
|
<CliHints module="localtask" />
|
|
841
|
+
|
|
842
|
+
{modal && (
|
|
843
|
+
<div
|
|
844
|
+
onClick={() => setModal(null)}
|
|
845
|
+
style={{
|
|
846
|
+
position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)',
|
|
847
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000,
|
|
848
|
+
}}
|
|
849
|
+
>
|
|
850
|
+
<div
|
|
851
|
+
onClick={(e) => e.stopPropagation()}
|
|
852
|
+
style={{
|
|
853
|
+
background: '#fff', borderRadius: 8, padding: 20, maxWidth: 'min(720px, 90vw)',
|
|
854
|
+
maxHeight: '80vh', overflow: 'auto', boxShadow: '0 12px 40px rgba(0,0,0,0.25)',
|
|
855
|
+
}}
|
|
856
|
+
>
|
|
857
|
+
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 12 }}>
|
|
858
|
+
<strong style={{ fontSize: 14 }}>{modal.title}</strong>
|
|
859
|
+
<button
|
|
860
|
+
className="btn small ghost"
|
|
861
|
+
onClick={() => setModal(null)}
|
|
862
|
+
style={{ marginLeft: 'auto' }}
|
|
863
|
+
>
|
|
864
|
+
关闭
|
|
865
|
+
</button>
|
|
866
|
+
</div>
|
|
867
|
+
<div style={{ fontSize: 13, lineHeight: 1.6, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
|
|
868
|
+
{modal.body}
|
|
869
|
+
</div>
|
|
870
|
+
</div>
|
|
871
|
+
</div>
|
|
872
|
+
)}
|
|
780
873
|
</>
|
|
781
874
|
);
|
|
782
875
|
}
|
package/src/runtime/api.js
CHANGED
|
@@ -92,6 +92,11 @@ export async function handleApi(req, res, url) {
|
|
|
92
92
|
} else {
|
|
93
93
|
Object.assign(raw, await readBody(req).catch(() => ({})));
|
|
94
94
|
}
|
|
95
|
+
// stream 模式:把 req/res/url 透传给 run,让 action 自己接管响应(SSE 等)
|
|
96
|
+
if (action.stream) {
|
|
97
|
+
await action.run({ ...applySpec(action, raw), __raw: { req, res, url } }, { transport: 'http' });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
95
100
|
const data = await action.run(applySpec(action, raw), { transport: 'http' });
|
|
96
101
|
sendJson(res, 200, { ok: true, data });
|
|
97
102
|
} catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e,s as t,t as n}from"./index-
|
|
1
|
+
import{o as e,s as t,t as n}from"./index-2OK9SG2y.js";var r=t();function i({module:t}){let{boot:i}=e(),a=(i?.commands||[]).filter(e=>e.module===t);return a.length?(0,r.jsxs)(`div`,{className:`cli-hint`,children:[(0,r.jsx)(`span`,{className:`cli-hint-label`,children:`CLI 等价(同构命令,加 --json 得机器可读输出):`}),a.map(e=>(0,r.jsx)(n,{className:`cli-cmd`,text:e.command,title:`点击复制:${e.usage}`,children:e.command},e.id))]}):null}export{i as t};
|