@xcanwin/manyoyo 6.0.5 → 6.1.3
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/bin/manyoyo.js +58 -48
- package/lib/agent-adapters/index.js +47 -0
- package/lib/container-modes.js +37 -0
- package/lib/core/app-error.js +81 -0
- package/lib/core/event-store.js +106 -0
- package/lib/core/events.js +149 -0
- package/lib/doctor.js +84 -0
- package/lib/web/frontend/app.css +168 -12
- package/lib/web/frontend/app.html +19 -5
- package/lib/web/frontend/app.js +150 -10
- package/lib/web/frontend/chat-behavior.js +30 -0
- package/lib/web/frontend/codemirror.bundle.js +31 -31800
- package/lib/web/server.js +111 -29
- package/package.json +3 -1
package/lib/web/frontend/app.css
CHANGED
|
@@ -14,9 +14,12 @@
|
|
|
14
14
|
--line: #d9c7ad;
|
|
15
15
|
--line-strong: #b59263;
|
|
16
16
|
--line-soft: rgba(181, 146, 99, 0.26);
|
|
17
|
+
--line-medium: rgba(181, 146, 99, 0.4);
|
|
17
18
|
--tree-guide: rgba(181, 146, 99, 0.44);
|
|
18
19
|
--tree-guide-soft: rgba(181, 146, 99, 0.44);
|
|
19
20
|
|
|
21
|
+
--surface-panel: linear-gradient(180deg, rgba(255, 255, 255, 0.88) 0%, rgba(252, 246, 236, 0.88) 100%);
|
|
22
|
+
|
|
20
23
|
--text: #1f1a14;
|
|
21
24
|
--muted: #6a5f52;
|
|
22
25
|
|
|
@@ -594,7 +597,7 @@ textarea:focus-visible {
|
|
|
594
597
|
}
|
|
595
598
|
|
|
596
599
|
.session-status {
|
|
597
|
-
border-radius:
|
|
600
|
+
border-radius: 999px;
|
|
598
601
|
padding: 2px 8px;
|
|
599
602
|
font-weight: 700;
|
|
600
603
|
font-size: 11px;
|
|
@@ -1005,7 +1008,7 @@ button.tree-toggle:active {
|
|
|
1005
1008
|
align-items: flex-start;
|
|
1006
1009
|
gap: 12px;
|
|
1007
1010
|
padding: 6px 8px 12px;
|
|
1008
|
-
border-bottom: 1px solid
|
|
1011
|
+
border-bottom: 1px solid var(--line-medium);
|
|
1009
1012
|
}
|
|
1010
1013
|
|
|
1011
1014
|
.header-main {
|
|
@@ -1062,6 +1065,56 @@ button.tree-toggle:active {
|
|
|
1062
1065
|
border-color: var(--accent-strong);
|
|
1063
1066
|
}
|
|
1064
1067
|
|
|
1068
|
+
.workspace-switcher {
|
|
1069
|
+
position: relative;
|
|
1070
|
+
display: inline-flex;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
.workspace-switcher-btn {
|
|
1074
|
+
width: 34px;
|
|
1075
|
+
height: 34px;
|
|
1076
|
+
padding: 0;
|
|
1077
|
+
display: inline-flex;
|
|
1078
|
+
align-items: center;
|
|
1079
|
+
justify-content: center;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
.workspace-switcher-btn svg {
|
|
1083
|
+
width: 16px;
|
|
1084
|
+
height: 16px;
|
|
1085
|
+
fill: currentColor;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
.workspace-switcher-btn.is-active {
|
|
1089
|
+
color: #ffffff;
|
|
1090
|
+
background: var(--accent);
|
|
1091
|
+
border-color: var(--accent-strong);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
.workspace-switcher-panel {
|
|
1095
|
+
display: none;
|
|
1096
|
+
position: absolute;
|
|
1097
|
+
top: calc(100% + 8px);
|
|
1098
|
+
left: 0;
|
|
1099
|
+
z-index: 8;
|
|
1100
|
+
width: max-content;
|
|
1101
|
+
padding: 8px;
|
|
1102
|
+
border: 1px solid var(--line);
|
|
1103
|
+
border-radius: 10px;
|
|
1104
|
+
background: #fffaf2;
|
|
1105
|
+
box-shadow: var(--shadow-strong);
|
|
1106
|
+
flex-direction: column;
|
|
1107
|
+
gap: 6px;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
body.workspace-switcher-open .workspace-switcher-panel {
|
|
1111
|
+
display: flex;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
.workspace-switcher-panel button {
|
|
1115
|
+
text-align: left;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1065
1118
|
.header-actions {
|
|
1066
1119
|
display: none;
|
|
1067
1120
|
position: absolute;
|
|
@@ -1120,8 +1173,7 @@ body.mobile-actions-open .header-actions {
|
|
|
1120
1173
|
flex-direction: column;
|
|
1121
1174
|
gap: 14px;
|
|
1122
1175
|
scroll-behavior: smooth;
|
|
1123
|
-
background:
|
|
1124
|
-
linear-gradient(180deg, rgba(255, 255, 255, 0.88) 0%, rgba(252, 246, 236, 0.88) 100%);
|
|
1176
|
+
background: var(--surface-panel);
|
|
1125
1177
|
}
|
|
1126
1178
|
|
|
1127
1179
|
#terminalPanel {
|
|
@@ -1139,8 +1191,7 @@ body.mobile-actions-open .header-actions {
|
|
|
1139
1191
|
border: 1px solid var(--line);
|
|
1140
1192
|
border-radius: 14px;
|
|
1141
1193
|
overflow: hidden;
|
|
1142
|
-
background:
|
|
1143
|
-
linear-gradient(180deg, rgba(255, 255, 255, 0.88) 0%, rgba(252, 246, 236, 0.88) 100%);
|
|
1194
|
+
background: var(--surface-panel);
|
|
1144
1195
|
}
|
|
1145
1196
|
|
|
1146
1197
|
.files-browser {
|
|
@@ -1382,8 +1433,7 @@ body.mobile-actions-open .header-actions {
|
|
|
1382
1433
|
padding: 0;
|
|
1383
1434
|
border: 1px solid var(--line);
|
|
1384
1435
|
border-radius: 14px;
|
|
1385
|
-
background:
|
|
1386
|
-
linear-gradient(180deg, rgba(255, 255, 255, 0.88) 0%, rgba(252, 246, 236, 0.88) 100%);
|
|
1436
|
+
background: var(--surface-panel);
|
|
1387
1437
|
}
|
|
1388
1438
|
|
|
1389
1439
|
.inspector-stack {
|
|
@@ -1657,6 +1707,47 @@ body.terminal-mode .composer {
|
|
|
1657
1707
|
font-weight: 700;
|
|
1658
1708
|
}
|
|
1659
1709
|
|
|
1710
|
+
.msg-actions {
|
|
1711
|
+
display: flex;
|
|
1712
|
+
gap: 6px;
|
|
1713
|
+
opacity: 0;
|
|
1714
|
+
transition: opacity 140ms ease;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
.msg:hover .msg-actions,
|
|
1718
|
+
.msg:focus-within .msg-actions {
|
|
1719
|
+
opacity: 1;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
@media (hover: none) {
|
|
1723
|
+
.msg-actions {
|
|
1724
|
+
opacity: 1;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
.msg-copy-btn {
|
|
1729
|
+
border: 1px solid var(--line);
|
|
1730
|
+
border-radius: 10px;
|
|
1731
|
+
background: var(--panel-soft);
|
|
1732
|
+
color: var(--muted);
|
|
1733
|
+
font-size: 11px;
|
|
1734
|
+
font-weight: 600;
|
|
1735
|
+
padding: 2px 8px;
|
|
1736
|
+
cursor: pointer;
|
|
1737
|
+
transition: color 140ms ease, border-color 140ms ease, background-color 140ms ease;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
.msg-copy-btn:hover {
|
|
1741
|
+
color: var(--accent-strong);
|
|
1742
|
+
border-color: var(--line-strong);
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
.msg-copy-btn.is-copied {
|
|
1746
|
+
color: var(--subaccent-strong);
|
|
1747
|
+
border-color: var(--subaccent);
|
|
1748
|
+
background: var(--subaccent-soft);
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1660
1751
|
.msg-exit {
|
|
1661
1752
|
font-family: var(--font-mono);
|
|
1662
1753
|
font-size: 11px;
|
|
@@ -1739,6 +1830,52 @@ body.command-mode .msg.origin-agent .bubble {
|
|
|
1739
1830
|
gap: 8px;
|
|
1740
1831
|
}
|
|
1741
1832
|
|
|
1833
|
+
.trace-flow-toggle {
|
|
1834
|
+
border: 1px solid rgba(181, 146, 99, 0.26);
|
|
1835
|
+
border-radius: 10px;
|
|
1836
|
+
background: rgba(255, 250, 242, 0.7);
|
|
1837
|
+
overflow: hidden;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
.trace-flow-toggle[open] {
|
|
1841
|
+
background: transparent;
|
|
1842
|
+
border-color: transparent;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
.trace-flow-toggle-summary {
|
|
1846
|
+
list-style: none;
|
|
1847
|
+
display: flex;
|
|
1848
|
+
align-items: center;
|
|
1849
|
+
gap: 6px;
|
|
1850
|
+
padding: 8px 10px;
|
|
1851
|
+
cursor: pointer;
|
|
1852
|
+
font-size: 12px;
|
|
1853
|
+
color: var(--muted);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
.trace-flow-toggle-summary::-webkit-details-marker {
|
|
1857
|
+
display: none;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
.trace-flow-toggle-summary::before {
|
|
1861
|
+
content: '';
|
|
1862
|
+
width: 8px;
|
|
1863
|
+
height: 8px;
|
|
1864
|
+
flex: 0 0 auto;
|
|
1865
|
+
border-right: 1.6px solid currentColor;
|
|
1866
|
+
border-bottom: 1.6px solid currentColor;
|
|
1867
|
+
transform: rotate(-45deg);
|
|
1868
|
+
transition: transform 140ms ease;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
.trace-flow-toggle[open] > .trace-flow-toggle-summary::before {
|
|
1872
|
+
transform: rotate(45deg);
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
.trace-flow-toggle > .trace-flow {
|
|
1876
|
+
padding: 0 10px 10px;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1742
1879
|
.trace-card {
|
|
1743
1880
|
border: 1px solid rgba(181, 146, 99, 0.32);
|
|
1744
1881
|
border-left-width: 3px;
|
|
@@ -1861,7 +1998,7 @@ details.trace-card > .trace-card-summary {
|
|
|
1861
1998
|
}
|
|
1862
1999
|
|
|
1863
2000
|
.composer {
|
|
1864
|
-
border-top: 1px solid
|
|
2001
|
+
border-top: 1px solid var(--line-medium);
|
|
1865
2002
|
margin-top: 12px;
|
|
1866
2003
|
padding: 12px 8px 4px;
|
|
1867
2004
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.32) 0%, rgba(255, 249, 240, 0.78) 100%);
|
|
@@ -1872,7 +2009,17 @@ details.trace-card > .trace-card-summary {
|
|
|
1872
2009
|
align-items: center;
|
|
1873
2010
|
justify-content: space-between;
|
|
1874
2011
|
gap: 10px;
|
|
2012
|
+
max-height: 0;
|
|
2013
|
+
margin-bottom: 0;
|
|
2014
|
+
opacity: 0;
|
|
2015
|
+
overflow: hidden;
|
|
2016
|
+
transition: max-height 180ms ease, opacity 160ms ease, margin-bottom 180ms ease;
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
.composer.is-expanded .composer-toolbar {
|
|
2020
|
+
max-height: 56px;
|
|
1875
2021
|
margin-bottom: 10px;
|
|
2022
|
+
opacity: 1;
|
|
1876
2023
|
}
|
|
1877
2024
|
|
|
1878
2025
|
.composer-toolbar-meta {
|
|
@@ -2134,21 +2281,30 @@ details.trace-card > .trace-card-summary {
|
|
|
2134
2281
|
}
|
|
2135
2282
|
|
|
2136
2283
|
.workbench-tabs {
|
|
2137
|
-
display:
|
|
2138
|
-
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
2284
|
+
display: flex;
|
|
2139
2285
|
gap: 6px;
|
|
2140
2286
|
}
|
|
2141
2287
|
|
|
2142
|
-
.workbench-tabs
|
|
2288
|
+
.workbench-tabs > #viewActivityBtn {
|
|
2289
|
+
flex: 1;
|
|
2143
2290
|
padding: 8px 10px;
|
|
2144
2291
|
font-size: 12px;
|
|
2145
2292
|
}
|
|
2146
2293
|
|
|
2294
|
+
.workspace-switcher-panel {
|
|
2295
|
+
left: auto;
|
|
2296
|
+
right: 0;
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2147
2299
|
.composer-toolbar {
|
|
2148
2300
|
flex-direction: column;
|
|
2149
2301
|
align-items: flex-start;
|
|
2150
2302
|
}
|
|
2151
2303
|
|
|
2304
|
+
.composer.is-expanded .composer-toolbar {
|
|
2305
|
+
max-height: 120px;
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2152
2308
|
.files-layout {
|
|
2153
2309
|
grid-template-columns: minmax(0, 1fr);
|
|
2154
2310
|
grid-template-rows: minmax(180px, 38%) minmax(0, 1fr);
|
|
@@ -68,11 +68,24 @@
|
|
|
68
68
|
<div id="activeMeta">请选择左侧会话</div>
|
|
69
69
|
<div class="workbench-tabs" id="workbenchTabs" aria-label="工作台视图">
|
|
70
70
|
<button type="button" id="viewActivityBtn" class="secondary is-active">活动</button>
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
<div class="workspace-switcher">
|
|
72
|
+
<button
|
|
73
|
+
type="button"
|
|
74
|
+
id="workspaceSwitcherToggle"
|
|
75
|
+
class="secondary workspace-switcher-btn"
|
|
76
|
+
aria-haspopup="true"
|
|
77
|
+
aria-expanded="false"
|
|
78
|
+
aria-controls="workspaceSwitcherPanel"
|
|
79
|
+
aria-label="更多视图:终端、文件、详情、配置、检查"
|
|
80
|
+
><svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"><circle cx="3" cy="8" r="1.4"></circle><circle cx="8" cy="8" r="1.4"></circle><circle cx="13" cy="8" r="1.4"></circle></svg></button>
|
|
81
|
+
<div class="workspace-switcher-panel" id="workspaceSwitcherPanel" hidden>
|
|
82
|
+
<button type="button" id="viewTerminalBtn" class="secondary">终端</button>
|
|
83
|
+
<button type="button" id="viewFilesBtn" class="secondary">文件</button>
|
|
84
|
+
<button type="button" id="viewDetailBtn" class="secondary">详情</button>
|
|
85
|
+
<button type="button" id="viewConfigBtn" class="secondary">配置</button>
|
|
86
|
+
<button type="button" id="viewCheckBtn" class="secondary">检查</button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
76
89
|
</div>
|
|
77
90
|
</div>
|
|
78
91
|
</header>
|
|
@@ -306,6 +319,7 @@
|
|
|
306
319
|
<script src="/app/vendor/xterm-addon-fit.js"></script>
|
|
307
320
|
<script src="/app/vendor/marked.min.js"></script>
|
|
308
321
|
<script src="/app/frontend/markdown-renderer.js"></script>
|
|
322
|
+
<script src="/app/frontend/chat-behavior.js"></script>
|
|
309
323
|
<script src="/app/frontend/codemirror.bundle.js"></script>
|
|
310
324
|
<script src="/app/frontend/file-browser.js"></script>
|
|
311
325
|
<script src="/app/frontend/app.js"></script>
|
package/lib/web/frontend/app.js
CHANGED
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
loadingSessionDetail: false,
|
|
49
49
|
mobileSidebarOpen: false,
|
|
50
50
|
mobileActionsOpen: false,
|
|
51
|
+
workspaceSwitcherOpen: false,
|
|
51
52
|
configModalOpen: false,
|
|
52
53
|
createModalOpen: false,
|
|
53
54
|
agentTemplateModalOpen: false,
|
|
@@ -125,6 +126,8 @@
|
|
|
125
126
|
const mobileActionsToggle = document.getElementById('mobileActionsToggle');
|
|
126
127
|
const headerActions = document.getElementById('headerActions');
|
|
127
128
|
const viewActivityBtn = document.getElementById('viewActivityBtn');
|
|
129
|
+
const workspaceSwitcherToggle = document.getElementById('workspaceSwitcherToggle');
|
|
130
|
+
const workspaceSwitcherPanel = document.getElementById('workspaceSwitcherPanel');
|
|
128
131
|
const viewTerminalBtn = document.getElementById('viewTerminalBtn');
|
|
129
132
|
const viewFilesBtn = document.getElementById('viewFilesBtn');
|
|
130
133
|
const viewDetailBtn = document.getElementById('viewDetailBtn');
|
|
@@ -318,6 +321,40 @@
|
|
|
318
321
|
bubble.appendChild(pre);
|
|
319
322
|
}
|
|
320
323
|
|
|
324
|
+
function copyTextToClipboard(text) {
|
|
325
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
326
|
+
return navigator.clipboard.writeText(text);
|
|
327
|
+
}
|
|
328
|
+
return Promise.reject(new Error('clipboard unavailable'));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function appendMessageCopyActions(meta, items) {
|
|
332
|
+
const actions = document.createElement('div');
|
|
333
|
+
actions.className = 'msg-actions';
|
|
334
|
+
items.forEach(function (item) {
|
|
335
|
+
const btn = document.createElement('button');
|
|
336
|
+
btn.type = 'button';
|
|
337
|
+
btn.className = 'msg-copy-btn';
|
|
338
|
+
btn.textContent = item.label;
|
|
339
|
+
btn.addEventListener('click', function () {
|
|
340
|
+
const text = String(item.getText() || '');
|
|
341
|
+
if (!text) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
copyTextToClipboard(text).then(function () {
|
|
345
|
+
btn.textContent = '已复制';
|
|
346
|
+
btn.classList.add('is-copied');
|
|
347
|
+
window.setTimeout(function () {
|
|
348
|
+
btn.textContent = item.label;
|
|
349
|
+
btn.classList.remove('is-copied');
|
|
350
|
+
}, 1200);
|
|
351
|
+
}).catch(function () {});
|
|
352
|
+
});
|
|
353
|
+
actions.appendChild(btn);
|
|
354
|
+
});
|
|
355
|
+
meta.appendChild(actions);
|
|
356
|
+
}
|
|
357
|
+
|
|
321
358
|
function stringifyPrettyJson(value) {
|
|
322
359
|
if (value === undefined || value === null) {
|
|
323
360
|
return '';
|
|
@@ -545,6 +582,8 @@
|
|
|
545
582
|
return card;
|
|
546
583
|
}
|
|
547
584
|
|
|
585
|
+
const traceFlowExpandedState = new Map();
|
|
586
|
+
|
|
548
587
|
function appendStructuredTraceContent(bubble, message) {
|
|
549
588
|
bubble.classList.add('trace-bubble');
|
|
550
589
|
const container = document.createElement('div');
|
|
@@ -555,10 +594,33 @@
|
|
|
555
594
|
buildStructuredTraceResidualLines(message).forEach(function (line) {
|
|
556
595
|
flow.appendChild(createResidualTraceCard(line));
|
|
557
596
|
});
|
|
558
|
-
|
|
597
|
+
const traceEvents = Array.isArray(message && message.traceEvents) ? message.traceEvents : [];
|
|
598
|
+
traceEvents.forEach(function (traceEvent) {
|
|
559
599
|
flow.appendChild(createTraceEventCard(traceEvent));
|
|
560
600
|
});
|
|
561
|
-
|
|
601
|
+
|
|
602
|
+
const messageId = message && message.id ? message.id : '';
|
|
603
|
+
const pending = Boolean(state.agentRun.active && state.agentRun.traceMessageId === messageId);
|
|
604
|
+
const summaryInfo = window.ManyoyoChatBehavior.summarizeTraceFlow(traceEvents, { pending });
|
|
605
|
+
const hasError = traceEvents.some(function (event) {
|
|
606
|
+
return event && event.kind === 'error';
|
|
607
|
+
});
|
|
608
|
+
const defaultOpen = hasError || traceEvents.length === 0;
|
|
609
|
+
const toggle = document.createElement('details');
|
|
610
|
+
toggle.className = 'trace-flow-toggle';
|
|
611
|
+
toggle.open = traceFlowExpandedState.has(messageId) ? traceFlowExpandedState.get(messageId) : defaultOpen;
|
|
612
|
+
toggle.addEventListener('toggle', function () {
|
|
613
|
+
if (messageId) {
|
|
614
|
+
traceFlowExpandedState.set(messageId, toggle.open);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
const summary = document.createElement('summary');
|
|
619
|
+
summary.className = 'trace-flow-toggle-summary';
|
|
620
|
+
summary.textContent = summaryInfo.label;
|
|
621
|
+
toggle.appendChild(summary);
|
|
622
|
+
toggle.appendChild(flow);
|
|
623
|
+
container.appendChild(toggle);
|
|
562
624
|
|
|
563
625
|
bubble.appendChild(container);
|
|
564
626
|
}
|
|
@@ -1930,6 +1992,21 @@
|
|
|
1930
1992
|
setMobileActionsMenu(false);
|
|
1931
1993
|
}
|
|
1932
1994
|
|
|
1995
|
+
function setWorkspaceSwitcherMenu(open) {
|
|
1996
|
+
state.workspaceSwitcherOpen = Boolean(open);
|
|
1997
|
+
document.body.classList.toggle('workspace-switcher-open', state.workspaceSwitcherOpen);
|
|
1998
|
+
if (workspaceSwitcherToggle) {
|
|
1999
|
+
workspaceSwitcherToggle.setAttribute('aria-expanded', state.workspaceSwitcherOpen ? 'true' : 'false');
|
|
2000
|
+
}
|
|
2001
|
+
if (workspaceSwitcherPanel) {
|
|
2002
|
+
workspaceSwitcherPanel.hidden = !state.workspaceSwitcherOpen;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
function closeWorkspaceSwitcherMenu() {
|
|
2007
|
+
setWorkspaceSwitcherMenu(false);
|
|
2008
|
+
}
|
|
2009
|
+
|
|
1933
2010
|
const VIEW_LABELS = {
|
|
1934
2011
|
activity: '活动',
|
|
1935
2012
|
terminal: '终端',
|
|
@@ -2197,6 +2274,20 @@
|
|
|
2197
2274
|
}
|
|
2198
2275
|
}
|
|
2199
2276
|
|
|
2277
|
+
function syncComposerExpanded() {
|
|
2278
|
+
if (!composer || !commandInput) {
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
// 判定条件必须是"焦点是否还在 composer 内部"而不是"是否等于 commandInput":
|
|
2282
|
+
// 点击 Agent/命令 切换或 CLI/模型 按钮时,commandInput 会先 blur,
|
|
2283
|
+
// 若立刻按 commandInput 是否聚焦来收起,会在 mousedown 与 click 之间把工具栏收起,
|
|
2284
|
+
// 导致按钮的点击事件因布局变化而落空(点了没反应)。
|
|
2285
|
+
const focused = composer.contains(document.activeElement);
|
|
2286
|
+
const hasDraft = Boolean(commandInput.value && commandInput.value.trim());
|
|
2287
|
+
const expanded = window.ManyoyoChatBehavior.shouldExpandComposer({ focused, hasDraft });
|
|
2288
|
+
composer.classList.toggle('is-expanded', expanded);
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2200
2291
|
function syncUi() {
|
|
2201
2292
|
if (!state.active) {
|
|
2202
2293
|
activeTitle.textContent = '未选择会话';
|
|
@@ -2253,6 +2344,7 @@
|
|
|
2253
2344
|
if (viewDetailBtn) viewDetailBtn.classList.toggle('is-active', detailTab);
|
|
2254
2345
|
if (viewConfigBtn) viewConfigBtn.classList.toggle('is-active', configTab);
|
|
2255
2346
|
if (viewCheckBtn) viewCheckBtn.classList.toggle('is-active', checkTab);
|
|
2347
|
+
if (workspaceSwitcherToggle) workspaceSwitcherToggle.classList.toggle('is-active', !activityTab);
|
|
2256
2348
|
if (messagesNode) {
|
|
2257
2349
|
messagesNode.hidden = !activityTab;
|
|
2258
2350
|
}
|
|
@@ -2310,6 +2402,7 @@
|
|
|
2310
2402
|
? 'Enter 发送提示词 · Shift/Alt + Enter 换行 · 执行中可停止'
|
|
2311
2403
|
: 'Enter 发送 · Shift/Alt + Enter 换行';
|
|
2312
2404
|
}
|
|
2405
|
+
syncComposerExpanded();
|
|
2313
2406
|
if (openCreateBtn) {
|
|
2314
2407
|
openCreateBtn.disabled = state.createLoading || state.createSubmitting;
|
|
2315
2408
|
}
|
|
@@ -3376,9 +3469,13 @@
|
|
|
3376
3469
|
}
|
|
3377
3470
|
|
|
3378
3471
|
function isMessagesNearBottom(thresholdPx) {
|
|
3379
|
-
const threshold = Number.isFinite(thresholdPx) ? thresholdPx : 40;
|
|
3380
3472
|
if (!messagesNode) return true;
|
|
3381
|
-
return
|
|
3473
|
+
return window.ManyoyoChatBehavior.isNearBottom(
|
|
3474
|
+
messagesNode.scrollTop,
|
|
3475
|
+
messagesNode.scrollHeight,
|
|
3476
|
+
messagesNode.clientHeight,
|
|
3477
|
+
thresholdPx
|
|
3478
|
+
);
|
|
3382
3479
|
}
|
|
3383
3480
|
|
|
3384
3481
|
function scrollMessagesToBottomImmediate() {
|
|
@@ -3404,7 +3501,8 @@
|
|
|
3404
3501
|
if (msg && msg.id && msg.streamTrace) {
|
|
3405
3502
|
const content = msg.content ? String(msg.content) : '';
|
|
3406
3503
|
const timestamp = msg.timestamp ? String(msg.timestamp) : '';
|
|
3407
|
-
|
|
3504
|
+
const running = (state.agentRun.active && state.agentRun.traceMessageId === msg.id) ? '1' : '0';
|
|
3505
|
+
return `id:${msg.id}|trace|${timestamp}|${content}|${running}`;
|
|
3408
3506
|
}
|
|
3409
3507
|
if (msg && msg.id) {
|
|
3410
3508
|
return `id:${msg.id}`;
|
|
@@ -3498,11 +3596,20 @@
|
|
|
3498
3596
|
if (renderedMarkdown) {
|
|
3499
3597
|
markdownNode.innerHTML = renderedMarkdown;
|
|
3500
3598
|
bubble.appendChild(markdownNode);
|
|
3599
|
+
appendMessageCopyActions(meta, [
|
|
3600
|
+
{ label: '复制文本', getText: function () { return markdownNode.innerText || markdownNode.textContent || ''; } },
|
|
3601
|
+
{ label: '复制 Markdown', getText: function () { return msg.content; } }
|
|
3602
|
+
]);
|
|
3501
3603
|
} else {
|
|
3502
3604
|
appendPlainMessageContent(bubble, msg.content);
|
|
3503
3605
|
}
|
|
3504
3606
|
} else {
|
|
3505
3607
|
appendPlainMessageContent(bubble, msg.content);
|
|
3608
|
+
if (msg.role === 'user') {
|
|
3609
|
+
appendMessageCopyActions(meta, [
|
|
3610
|
+
{ label: '复制', getText: function () { return msg.content; } }
|
|
3611
|
+
]);
|
|
3612
|
+
}
|
|
3506
3613
|
}
|
|
3507
3614
|
|
|
3508
3615
|
row.appendChild(meta);
|
|
@@ -3932,7 +4039,7 @@
|
|
|
3932
4039
|
traceLines.push(line);
|
|
3933
4040
|
updateAgentTraceMessageLocal(sessionName, traceMessageId, traceLines.join('\n'), traceEvent);
|
|
3934
4041
|
if (state.active === sessionName) {
|
|
3935
|
-
renderMessages(state.messages
|
|
4042
|
+
renderMessages(state.messages);
|
|
3936
4043
|
}
|
|
3937
4044
|
}
|
|
3938
4045
|
|
|
@@ -3971,7 +4078,7 @@
|
|
|
3971
4078
|
}
|
|
3972
4079
|
updateStreamingReplyLocal(sessionName, streamingReplyId, content);
|
|
3973
4080
|
if (state.active === sessionName) {
|
|
3974
|
-
renderMessages(state.messages
|
|
4081
|
+
renderMessages(state.messages);
|
|
3975
4082
|
}
|
|
3976
4083
|
return;
|
|
3977
4084
|
}
|
|
@@ -4007,6 +4114,9 @@
|
|
|
4007
4114
|
}
|
|
4008
4115
|
}
|
|
4009
4116
|
finalizeAgentRunState();
|
|
4117
|
+
if (state.active === sessionName) {
|
|
4118
|
+
renderMessages(state.messages);
|
|
4119
|
+
}
|
|
4010
4120
|
syncUi();
|
|
4011
4121
|
}
|
|
4012
4122
|
|
|
@@ -4019,7 +4129,7 @@
|
|
|
4019
4129
|
|
|
4020
4130
|
appendAssistantMessageLocal(sessionName, finalResult, 'agent');
|
|
4021
4131
|
if (state.active === sessionName) {
|
|
4022
|
-
renderMessages(state.messages
|
|
4132
|
+
renderMessages(state.messages);
|
|
4023
4133
|
}
|
|
4024
4134
|
bumpSessionMetaAfterSend(sessionName);
|
|
4025
4135
|
refreshSessionsSilent({ preferredName: sessionName }).catch(function () {
|
|
@@ -4285,7 +4395,7 @@
|
|
|
4285
4395
|
}
|
|
4286
4396
|
appendAssistantMessageLocal(submitSession, runResult, mode);
|
|
4287
4397
|
if (state.active === submitSession) {
|
|
4288
|
-
renderMessages(state.messages
|
|
4398
|
+
renderMessages(state.messages);
|
|
4289
4399
|
}
|
|
4290
4400
|
bumpSessionMetaAfterSend(submitSession);
|
|
4291
4401
|
refreshSessionsSilent({ preferredName: submitSession }).catch(function () {
|
|
@@ -4303,7 +4413,7 @@
|
|
|
4303
4413
|
state.messages = state.messages.filter(function (message) {
|
|
4304
4414
|
return !(message && message.id === pendingMessage.id);
|
|
4305
4415
|
});
|
|
4306
|
-
renderMessages(state.messages
|
|
4416
|
+
renderMessages(state.messages);
|
|
4307
4417
|
}
|
|
4308
4418
|
alert(e.message);
|
|
4309
4419
|
}
|
|
@@ -4334,6 +4444,16 @@
|
|
|
4334
4444
|
});
|
|
4335
4445
|
}
|
|
4336
4446
|
|
|
4447
|
+
if (composer) {
|
|
4448
|
+
composer.addEventListener('focusin', syncComposerExpanded);
|
|
4449
|
+
composer.addEventListener('focusout', function () {
|
|
4450
|
+
// 延后到下一个宏任务再判断,让浏览器先把焦点落到新的目标元素上
|
|
4451
|
+
// (比如 composer 内的 CLI/Agent 按钮),避免过早收起导致点击落空。
|
|
4452
|
+
window.setTimeout(syncComposerExpanded, 0);
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4455
|
+
commandInput.addEventListener('input', syncComposerExpanded);
|
|
4456
|
+
|
|
4337
4457
|
commandInput.addEventListener('keydown', function (event) {
|
|
4338
4458
|
if (event.key !== 'Enter' || event.isComposing) {
|
|
4339
4459
|
return;
|
|
@@ -4372,6 +4492,7 @@
|
|
|
4372
4492
|
|
|
4373
4493
|
if (viewActivityBtn) {
|
|
4374
4494
|
viewActivityBtn.addEventListener('click', function () {
|
|
4495
|
+
closeWorkspaceSwitcherMenu();
|
|
4375
4496
|
setActiveTab('activity');
|
|
4376
4497
|
commandInput.focus();
|
|
4377
4498
|
});
|
|
@@ -4379,30 +4500,35 @@
|
|
|
4379
4500
|
|
|
4380
4501
|
if (viewTerminalBtn) {
|
|
4381
4502
|
viewTerminalBtn.addEventListener('click', function () {
|
|
4503
|
+
closeWorkspaceSwitcherMenu();
|
|
4382
4504
|
setActiveTab('terminal');
|
|
4383
4505
|
});
|
|
4384
4506
|
}
|
|
4385
4507
|
|
|
4386
4508
|
if (viewFilesBtn) {
|
|
4387
4509
|
viewFilesBtn.addEventListener('click', function () {
|
|
4510
|
+
closeWorkspaceSwitcherMenu();
|
|
4388
4511
|
setActiveTab('files');
|
|
4389
4512
|
});
|
|
4390
4513
|
}
|
|
4391
4514
|
|
|
4392
4515
|
if (viewDetailBtn) {
|
|
4393
4516
|
viewDetailBtn.addEventListener('click', function () {
|
|
4517
|
+
closeWorkspaceSwitcherMenu();
|
|
4394
4518
|
setActiveTab('detail');
|
|
4395
4519
|
});
|
|
4396
4520
|
}
|
|
4397
4521
|
|
|
4398
4522
|
if (viewConfigBtn) {
|
|
4399
4523
|
viewConfigBtn.addEventListener('click', function () {
|
|
4524
|
+
closeWorkspaceSwitcherMenu();
|
|
4400
4525
|
setActiveTab('config');
|
|
4401
4526
|
});
|
|
4402
4527
|
}
|
|
4403
4528
|
|
|
4404
4529
|
if (viewCheckBtn) {
|
|
4405
4530
|
viewCheckBtn.addEventListener('click', function () {
|
|
4531
|
+
closeWorkspaceSwitcherMenu();
|
|
4406
4532
|
setActiveTab('check');
|
|
4407
4533
|
});
|
|
4408
4534
|
}
|
|
@@ -4459,6 +4585,12 @@
|
|
|
4459
4585
|
});
|
|
4460
4586
|
}
|
|
4461
4587
|
|
|
4588
|
+
if (workspaceSwitcherToggle) {
|
|
4589
|
+
workspaceSwitcherToggle.addEventListener('click', function () {
|
|
4590
|
+
setWorkspaceSwitcherMenu(!state.workspaceSwitcherOpen);
|
|
4591
|
+
});
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4462
4594
|
if (mobileSidebarClose) {
|
|
4463
4595
|
mobileSidebarClose.addEventListener('click', function () {
|
|
4464
4596
|
closeMobileSessionPanel();
|
|
@@ -4553,6 +4685,9 @@
|
|
|
4553
4685
|
if (event.key === 'Escape' && state.mobileActionsOpen) {
|
|
4554
4686
|
closeMobileActionsMenu();
|
|
4555
4687
|
}
|
|
4688
|
+
if (event.key === 'Escape' && state.workspaceSwitcherOpen) {
|
|
4689
|
+
closeWorkspaceSwitcherMenu();
|
|
4690
|
+
}
|
|
4556
4691
|
});
|
|
4557
4692
|
|
|
4558
4693
|
function onLayoutMediaChange() {
|
|
@@ -4606,6 +4741,11 @@
|
|
|
4606
4741
|
if (headerActions && headerActions.contains(target)) return;
|
|
4607
4742
|
closeMobileActionsMenu();
|
|
4608
4743
|
}
|
|
4744
|
+
if (state.workspaceSwitcherOpen) {
|
|
4745
|
+
if (workspaceSwitcherToggle && workspaceSwitcherToggle.contains(target)) return;
|
|
4746
|
+
if (workspaceSwitcherPanel && workspaceSwitcherPanel.contains(target)) return;
|
|
4747
|
+
closeWorkspaceSwitcherMenu();
|
|
4748
|
+
}
|
|
4609
4749
|
});
|
|
4610
4750
|
|
|
4611
4751
|
removeBtn.addEventListener('click', async function () {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
function isNearBottom(scrollTop, scrollHeight, clientHeight, thresholdPx) {
|
|
3
|
+
const threshold = Number.isFinite(thresholdPx) ? thresholdPx : 40;
|
|
4
|
+
const distance = scrollHeight - (scrollTop + clientHeight);
|
|
5
|
+
return distance <= threshold;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function summarizeTraceFlow(traceEvents, options) {
|
|
9
|
+
const events = Array.isArray(traceEvents) ? traceEvents : [];
|
|
10
|
+
const count = events.length;
|
|
11
|
+
if (count === 0) {
|
|
12
|
+
return { count: 0, label: '暂无步骤' };
|
|
13
|
+
}
|
|
14
|
+
const opts = options && typeof options === 'object' ? options : {};
|
|
15
|
+
const hasError = events.some(event => event && event.kind === 'error');
|
|
16
|
+
const status = hasError ? '有错误' : (opts.pending === true ? '进行中' : '已完成');
|
|
17
|
+
return { count, label: `${count} 步 · ${status}` };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function shouldExpandComposer(state) {
|
|
21
|
+
const opts = state && typeof state === 'object' ? state : {};
|
|
22
|
+
return Boolean(opts.focused || opts.hasDraft);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
window.ManyoyoChatBehavior = {
|
|
26
|
+
isNearBottom,
|
|
27
|
+
summarizeTraceFlow,
|
|
28
|
+
shouldExpandComposer
|
|
29
|
+
};
|
|
30
|
+
}());
|