@xcanwin/manyoyo 5.8.11 → 5.9.0

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.
@@ -1140,6 +1140,177 @@ body.mobile-actions-open .header-actions {
1140
1140
  background: #131923;
1141
1141
  }
1142
1142
 
1143
+ .files-pane {
1144
+ min-height: 0;
1145
+ border: 1px solid var(--line);
1146
+ border-radius: 14px;
1147
+ overflow: hidden;
1148
+ background:
1149
+ linear-gradient(180deg, rgba(255, 255, 255, 0.88) 0%, rgba(252, 246, 236, 0.88) 100%);
1150
+ }
1151
+
1152
+ .files-browser {
1153
+ height: 100%;
1154
+ min-height: 0;
1155
+ display: grid;
1156
+ grid-template-rows: auto minmax(0, 1fr);
1157
+ }
1158
+
1159
+ .files-toolbar {
1160
+ display: flex;
1161
+ align-items: center;
1162
+ gap: 8px;
1163
+ padding: 12px 14px;
1164
+ border-bottom: 1px solid var(--line);
1165
+ background: rgba(255, 251, 244, 0.96);
1166
+ }
1167
+
1168
+ .files-toolbar-path {
1169
+ flex: 1;
1170
+ min-width: 0;
1171
+ padding: 8px 10px;
1172
+ border: 1px solid rgba(181, 146, 99, 0.35);
1173
+ border-radius: 10px;
1174
+ background: #fffdf9;
1175
+ color: var(--text);
1176
+ font-family: var(--font-mono);
1177
+ font-size: 12px;
1178
+ line-height: 1.5;
1179
+ word-break: break-all;
1180
+ }
1181
+
1182
+ .files-toolbar-status {
1183
+ color: var(--muted);
1184
+ font-size: 12px;
1185
+ }
1186
+
1187
+ .files-layout {
1188
+ min-height: 0;
1189
+ display: grid;
1190
+ grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
1191
+ }
1192
+
1193
+ .files-sidebar {
1194
+ min-height: 0;
1195
+ border-right: 1px solid var(--line);
1196
+ background: rgba(255, 251, 244, 0.84);
1197
+ overflow: auto;
1198
+ }
1199
+
1200
+ .files-list {
1201
+ display: flex;
1202
+ flex-direction: column;
1203
+ gap: 6px;
1204
+ padding: 12px;
1205
+ }
1206
+
1207
+ .files-entry {
1208
+ width: 100%;
1209
+ display: flex;
1210
+ align-items: center;
1211
+ justify-content: space-between;
1212
+ gap: 10px;
1213
+ padding: 10px 12px;
1214
+ border: 1px solid rgba(181, 146, 99, 0.28);
1215
+ border-radius: 12px;
1216
+ background: rgba(255, 255, 255, 0.92);
1217
+ color: var(--text);
1218
+ text-align: left;
1219
+ cursor: pointer;
1220
+ }
1221
+
1222
+ .files-entry.is-active {
1223
+ border-color: rgba(120, 78, 27, 0.45);
1224
+ background: rgba(255, 244, 224, 0.9);
1225
+ }
1226
+
1227
+ .files-entry-name {
1228
+ min-width: 0;
1229
+ display: flex;
1230
+ flex-direction: column;
1231
+ gap: 0;
1232
+ }
1233
+
1234
+ .files-entry-title {
1235
+ font-size: 13px;
1236
+ font-weight: 600;
1237
+ word-break: break-word;
1238
+ }
1239
+
1240
+ .files-entry-meta {
1241
+ color: var(--muted);
1242
+ font-size: 11px;
1243
+ white-space: nowrap;
1244
+ }
1245
+
1246
+ .files-preview {
1247
+ min-height: 0;
1248
+ display: grid;
1249
+ grid-template-rows: auto minmax(0, 1fr);
1250
+ }
1251
+
1252
+ .files-preview-head {
1253
+ display: flex;
1254
+ flex-direction: column;
1255
+ gap: 6px;
1256
+ padding: 12px 14px;
1257
+ border-bottom: 1px solid var(--line);
1258
+ background: rgba(255, 251, 244, 0.96);
1259
+ }
1260
+
1261
+ .files-preview-title {
1262
+ font-family: var(--font-display);
1263
+ font-size: 15px;
1264
+ font-weight: 700;
1265
+ }
1266
+
1267
+ .files-preview-meta {
1268
+ color: var(--muted);
1269
+ font-size: 12px;
1270
+ word-break: break-all;
1271
+ }
1272
+
1273
+ .files-preview-body {
1274
+ min-height: 0;
1275
+ overflow: auto;
1276
+ padding: 14px;
1277
+ }
1278
+
1279
+ .files-empty,
1280
+ .files-note {
1281
+ padding: 18px;
1282
+ border: 1px dashed rgba(181, 146, 99, 0.45);
1283
+ border-radius: 12px;
1284
+ background: rgba(255, 255, 255, 0.7);
1285
+ color: var(--muted);
1286
+ line-height: 1.6;
1287
+ }
1288
+
1289
+ .files-pre {
1290
+ margin: 0;
1291
+ white-space: pre-wrap;
1292
+ word-break: break-word;
1293
+ font-family: var(--font-mono);
1294
+ font-size: 12px;
1295
+ line-height: 1.6;
1296
+ }
1297
+
1298
+ .files-editor-host {
1299
+ min-height: 100%;
1300
+ }
1301
+
1302
+ .files-editor-host .cm-editor {
1303
+ height: 100%;
1304
+ border: 1px solid rgba(181, 146, 99, 0.32);
1305
+ border-radius: 12px;
1306
+ overflow: hidden;
1307
+ background: rgba(255, 255, 255, 0.94);
1308
+ }
1309
+
1310
+ .files-editor-host .cm-scroller {
1311
+ font-family: var(--font-mono);
1312
+ }
1313
+
1143
1314
  .inspector-pane {
1144
1315
  overflow-y: auto;
1145
1316
  padding: 0;
@@ -1939,7 +2110,7 @@ details.trace-card > .trace-card-summary {
1939
2110
 
1940
2111
  .workbench-tabs {
1941
2112
  display: grid;
1942
- grid-template-columns: repeat(5, minmax(0, 1fr));
2113
+ grid-template-columns: repeat(6, minmax(0, 1fr));
1943
2114
  gap: 6px;
1944
2115
  }
1945
2116
 
@@ -1953,6 +2124,16 @@ details.trace-card > .trace-card-summary {
1953
2124
  align-items: flex-start;
1954
2125
  }
1955
2126
 
2127
+ .files-layout {
2128
+ grid-template-columns: minmax(0, 1fr);
2129
+ grid-template-rows: minmax(180px, 38%) minmax(0, 1fr);
2130
+ }
2131
+
2132
+ .files-sidebar {
2133
+ border-right: 0;
2134
+ border-bottom: 1px solid var(--line);
2135
+ }
2136
+
1956
2137
  .composer-toolbar-meta {
1957
2138
  width: 100%;
1958
2139
  justify-content: space-between;
@@ -68,6 +68,7 @@
68
68
  <div class="workbench-tabs" id="workbenchTabs" aria-label="工作台视图">
69
69
  <button type="button" id="viewActivityBtn" class="secondary is-active">活动</button>
70
70
  <button type="button" id="viewTerminalBtn" class="secondary">终端</button>
71
+ <button type="button" id="viewFilesBtn" class="secondary">文件</button>
71
72
  <button type="button" id="viewDetailBtn" class="secondary">详情</button>
72
73
  <button type="button" id="viewConfigBtn" class="secondary">配置</button>
73
74
  <button type="button" id="viewCheckBtn" class="secondary">检查</button>
@@ -90,6 +91,7 @@
90
91
  </div>
91
92
  <div id="terminalScreen" aria-label="终端输出区域"></div>
92
93
  </section>
94
+ <section id="filesPanel" class="workspace-pane files-pane" hidden></section>
93
95
  <section id="detailPanel" class="workspace-pane inspector-pane" hidden>
94
96
  <div id="detailSummary" class="inspector-stack"></div>
95
97
  </section>
@@ -297,6 +299,8 @@
297
299
  <script src="/app/vendor/xterm-addon-fit.js"></script>
298
300
  <script src="/app/vendor/marked.min.js"></script>
299
301
  <script src="/app/frontend/markdown-renderer.js"></script>
302
+ <script src="/app/frontend/codemirror.bundle.js"></script>
303
+ <script src="/app/frontend/file-browser.js"></script>
300
304
  <script src="/app/frontend/app.js"></script>
301
305
  </body>
302
306
  </html>
@@ -118,6 +118,7 @@
118
118
  const headerActions = document.getElementById('headerActions');
119
119
  const viewActivityBtn = document.getElementById('viewActivityBtn');
120
120
  const viewTerminalBtn = document.getElementById('viewTerminalBtn');
121
+ const viewFilesBtn = document.getElementById('viewFilesBtn');
121
122
  const viewDetailBtn = document.getElementById('viewDetailBtn');
122
123
  const viewConfigBtn = document.getElementById('viewConfigBtn');
123
124
  const viewCheckBtn = document.getElementById('viewCheckBtn');
@@ -174,6 +175,7 @@
174
175
  const activityModelChip = document.getElementById('activityModelChip');
175
176
  const messagesNode = document.getElementById('messages');
176
177
  const terminalPanel = document.getElementById('terminalPanel');
178
+ const filesPanel = document.getElementById('filesPanel');
177
179
  const detailPanel = document.getElementById('detailPanel');
178
180
  const configPanel = document.getElementById('configPanel');
179
181
  const checkPanel = document.getElementById('checkPanel');
@@ -248,6 +250,7 @@
248
250
  && typeof window.ManyoyoMarkdown.render === 'function'
249
251
  ? window.ManyoyoMarkdown
250
252
  : null;
253
+ let fileBrowser = null;
251
254
  function normalizeBooleanMap(source) {
252
255
  const result = {};
253
256
  if (!source || typeof source !== 'object' || Array.isArray(source)) {
@@ -1723,6 +1726,7 @@
1723
1726
  const VIEW_LABELS = {
1724
1727
  activity: '活动',
1725
1728
  terminal: '终端',
1729
+ files: '文件',
1726
1730
  detail: '详情',
1727
1731
  config: '配置',
1728
1732
  check: '检查'
@@ -2001,6 +2005,7 @@
2001
2005
 
2002
2006
  const activityTab = state.activeTab === 'activity';
2003
2007
  const terminalTab = state.activeTab === 'terminal';
2008
+ const filesTab = state.activeTab === 'files';
2004
2009
  const detailTab = state.activeTab === 'detail';
2005
2010
  const configTab = state.activeTab === 'config';
2006
2011
  const checkTab = state.activeTab === 'check';
@@ -2011,6 +2016,7 @@
2011
2016
  document.body.classList.toggle('command-mode', commandMode);
2012
2017
  document.body.classList.toggle('agent-mode', agentMode);
2013
2018
  document.body.classList.toggle('terminal-mode', terminalTab);
2019
+ document.body.classList.toggle('files-tab', filesTab);
2014
2020
  document.body.classList.toggle('detail-tab', detailTab);
2015
2021
  document.body.classList.toggle('config-tab', configTab);
2016
2022
  document.body.classList.toggle('check-tab', checkTab);
@@ -2036,6 +2042,7 @@
2036
2042
  }
2037
2043
  if (viewActivityBtn) viewActivityBtn.classList.toggle('is-active', activityTab);
2038
2044
  if (viewTerminalBtn) viewTerminalBtn.classList.toggle('is-active', terminalTab);
2045
+ if (viewFilesBtn) viewFilesBtn.classList.toggle('is-active', filesTab);
2039
2046
  if (viewDetailBtn) viewDetailBtn.classList.toggle('is-active', detailTab);
2040
2047
  if (viewConfigBtn) viewConfigBtn.classList.toggle('is-active', configTab);
2041
2048
  if (viewCheckBtn) viewCheckBtn.classList.toggle('is-active', checkTab);
@@ -2045,6 +2052,9 @@
2045
2052
  if (terminalPanel) {
2046
2053
  terminalPanel.hidden = !terminalTab;
2047
2054
  }
2055
+ if (filesPanel) {
2056
+ filesPanel.hidden = !filesTab;
2057
+ }
2048
2058
  if (detailPanel) {
2049
2059
  detailPanel.hidden = !detailTab;
2050
2060
  }
@@ -2057,6 +2067,14 @@
2057
2067
  if (terminalTab && state.terminal.terminalReady) {
2058
2068
  scheduleTerminalFit(false);
2059
2069
  }
2070
+ if (fileBrowser) {
2071
+ fileBrowser.sync({
2072
+ visible: filesTab,
2073
+ session: getActiveSession(),
2074
+ detail: state.sessionDetail,
2075
+ historyOnly: isActiveSessionHistoryOnly()
2076
+ });
2077
+ }
2060
2078
 
2061
2079
  const activeAgentRunning = isAgentRunActiveForSession(state.active);
2062
2080
  const busy = state.loadingSessions || state.loadingMessages || state.sending;
@@ -3949,6 +3967,12 @@
3949
3967
  });
3950
3968
  }
3951
3969
 
3970
+ if (viewFilesBtn) {
3971
+ viewFilesBtn.addEventListener('click', function () {
3972
+ setActiveTab('files');
3973
+ });
3974
+ }
3975
+
3952
3976
  if (viewDetailBtn) {
3953
3977
  viewDetailBtn.addEventListener('click', function () {
3954
3978
  setActiveTab('detail');
@@ -4210,6 +4234,16 @@
4210
4234
  });
4211
4235
  }
4212
4236
 
4237
+ if (window.ManyoyoFileBrowser && typeof window.ManyoyoFileBrowser.create === 'function') {
4238
+ fileBrowser = window.ManyoyoFileBrowser.create({
4239
+ root: filesPanel,
4240
+ api,
4241
+ onError: function (message) {
4242
+ alert(message);
4243
+ }
4244
+ });
4245
+ }
4246
+
4213
4247
  window.addEventListener('beforeunload', function () {
4214
4248
  disconnectTerminal('', true);
4215
4249
  });
@@ -0,0 +1,98 @@
1
+ import { basicSetup } from 'codemirror';
2
+ import { Compartment, EditorState } from '@codemirror/state';
3
+ import { EditorView } from '@codemirror/view';
4
+ import { css } from '@codemirror/lang-css';
5
+ import { html } from '@codemirror/lang-html';
6
+ import { javascript } from '@codemirror/lang-javascript';
7
+ import { json } from '@codemirror/lang-json';
8
+ import { markdown } from '@codemirror/lang-markdown';
9
+ import { python } from '@codemirror/lang-python';
10
+ import { yaml } from '@codemirror/lang-yaml';
11
+
12
+ function resolveLanguageExtension(language) {
13
+ switch (String(language || '').trim()) {
14
+ case 'css':
15
+ return css();
16
+ case 'html':
17
+ return html();
18
+ case 'javascript':
19
+ return javascript({ jsx: true, typescript: true });
20
+ case 'json':
21
+ return json();
22
+ case 'markdown':
23
+ return markdown();
24
+ case 'python':
25
+ return python();
26
+ case 'yaml':
27
+ return yaml();
28
+ default:
29
+ return [];
30
+ }
31
+ }
32
+
33
+ function createEditor(parent, options = {}) {
34
+ const target = parent;
35
+ const initialDoc = String(options.doc || '');
36
+ const initialLanguage = String(options.language || 'text').trim();
37
+ const initialReadOnly = options.readOnly !== false;
38
+ const languageCompartment = new Compartment();
39
+ const readOnlyCompartment = new Compartment();
40
+ const view = new EditorView({
41
+ parent: target,
42
+ state: EditorState.create({
43
+ doc: initialDoc,
44
+ extensions: [
45
+ basicSetup,
46
+ EditorView.lineWrapping,
47
+ readOnlyCompartment.of([
48
+ EditorState.readOnly.of(initialReadOnly),
49
+ EditorView.editable.of(!initialReadOnly)
50
+ ]),
51
+ languageCompartment.of(resolveLanguageExtension(initialLanguage)),
52
+ EditorView.theme({
53
+ '&': {
54
+ height: '100%',
55
+ fontSize: '12px'
56
+ },
57
+ '.cm-scroller': {
58
+ overflow: 'auto'
59
+ }
60
+ })
61
+ ]
62
+ })
63
+ });
64
+
65
+ return {
66
+ setValue(nextValue) {
67
+ const text = String(nextValue == null ? '' : nextValue);
68
+ view.dispatch({
69
+ changes: {
70
+ from: 0,
71
+ to: view.state.doc.length,
72
+ insert: text
73
+ }
74
+ });
75
+ },
76
+ setLanguage(nextLanguage) {
77
+ view.dispatch({
78
+ effects: languageCompartment.reconfigure(resolveLanguageExtension(nextLanguage))
79
+ });
80
+ },
81
+ setReadOnly(readOnly) {
82
+ const value = readOnly !== false;
83
+ view.dispatch({
84
+ effects: readOnlyCompartment.reconfigure([
85
+ EditorState.readOnly.of(value),
86
+ EditorView.editable.of(!value)
87
+ ])
88
+ });
89
+ },
90
+ destroy() {
91
+ view.destroy();
92
+ }
93
+ };
94
+ }
95
+
96
+ window.ManyoyoCodeEditor = {
97
+ create: createEditor
98
+ };