modernx-gui 1.1.3 → 1.1.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/dist/index.html +337 -0
- package/package.json +1 -1
- package/src/lib/server.js +5 -0
package/dist/index.html
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>ModernX GUI</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
16
|
+
background: #f5f5f5;
|
|
17
|
+
color: #333;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.header {
|
|
21
|
+
background: #fff;
|
|
22
|
+
padding: 1rem;
|
|
23
|
+
border-bottom: 1px solid #e0e0e0;
|
|
24
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.header h1 {
|
|
28
|
+
font-size: 1.5rem;
|
|
29
|
+
color: #2196F3;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.container {
|
|
33
|
+
display: flex;
|
|
34
|
+
height: calc(100vh - 60px);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.sidebar {
|
|
38
|
+
width: 250px;
|
|
39
|
+
background: #fff;
|
|
40
|
+
border-right: 1px solid #e0e0e0;
|
|
41
|
+
padding: 1rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.main {
|
|
45
|
+
flex: 1;
|
|
46
|
+
display: flex;
|
|
47
|
+
flex-direction: column;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.tabs {
|
|
51
|
+
background: #fff;
|
|
52
|
+
border-bottom: 1px solid #e0e0e0;
|
|
53
|
+
display: flex;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tab {
|
|
57
|
+
padding: 1rem 2rem;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
border-bottom: 2px solid transparent;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.tab.active {
|
|
63
|
+
border-bottom-color: #2196F3;
|
|
64
|
+
color: #2196F3;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.content {
|
|
68
|
+
flex: 1;
|
|
69
|
+
padding: 1rem;
|
|
70
|
+
overflow-y: auto;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.state-view {
|
|
74
|
+
background: #fff;
|
|
75
|
+
border-radius: 8px;
|
|
76
|
+
padding: 1rem;
|
|
77
|
+
margin-bottom: 1rem;
|
|
78
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.state-view h3 {
|
|
82
|
+
margin-bottom: 0.5rem;
|
|
83
|
+
color: #333;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.state-json {
|
|
87
|
+
background: #f8f8f8;
|
|
88
|
+
padding: 1rem;
|
|
89
|
+
border-radius: 4px;
|
|
90
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
91
|
+
font-size: 0.9rem;
|
|
92
|
+
white-space: pre-wrap;
|
|
93
|
+
word-break: break-all;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.action-item {
|
|
97
|
+
background: #fff;
|
|
98
|
+
border-radius: 8px;
|
|
99
|
+
padding: 1rem;
|
|
100
|
+
margin-bottom: 0.5rem;
|
|
101
|
+
border-left: 4px solid #4CAF50;
|
|
102
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.action-type {
|
|
106
|
+
font-weight: bold;
|
|
107
|
+
color: #2196F3;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.action-time {
|
|
111
|
+
color: #666;
|
|
112
|
+
font-size: 0.8rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.status {
|
|
116
|
+
padding: 0.5rem 1rem;
|
|
117
|
+
border-radius: 4px;
|
|
118
|
+
font-size: 0.8rem;
|
|
119
|
+
font-weight: bold;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.status.connected {
|
|
123
|
+
background: #E8F5E8;
|
|
124
|
+
color: #2E7D32;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.status.disconnected {
|
|
128
|
+
background: #FFEBEE;
|
|
129
|
+
color: #C62828;
|
|
130
|
+
}
|
|
131
|
+
</style>
|
|
132
|
+
</head>
|
|
133
|
+
<body>
|
|
134
|
+
<div class="header">
|
|
135
|
+
<h1>🚀 ModernX GUI</h1>
|
|
136
|
+
<div class="status" id="connection-status">连接中...</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div class="container">
|
|
140
|
+
<div class="sidebar">
|
|
141
|
+
<h3>项目信息</h3>
|
|
142
|
+
<div id="project-info">加载中...</div>
|
|
143
|
+
|
|
144
|
+
<h3>模型列表</h3>
|
|
145
|
+
<div id="models-list">加载中...</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<div class="main">
|
|
149
|
+
<div class="tabs">
|
|
150
|
+
<div class="tab active" data-tab="state">状态</div>
|
|
151
|
+
<div class="tab" data-tab="actions">动作历史</div>
|
|
152
|
+
<div class="tab" data-tab="models">模型</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<div class="content">
|
|
156
|
+
<div id="state-content" class="tab-content">
|
|
157
|
+
<div class="state-view">
|
|
158
|
+
<h3>当前状态</h3>
|
|
159
|
+
<div class="state-json" id="current-state">等待数据...</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<div id="actions-content" class="tab-content" style="display: none;">
|
|
164
|
+
<div id="actions-list">等待动作数据...</div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div id="models-content" class="tab-content" style="display: none;">
|
|
168
|
+
<div id="models-detail">等待模型数据...</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<script>
|
|
175
|
+
class ModernXGUI {
|
|
176
|
+
constructor() {
|
|
177
|
+
this.ws = null;
|
|
178
|
+
this.projectInfo = null;
|
|
179
|
+
this.currentState = {};
|
|
180
|
+
this.actions = [];
|
|
181
|
+
this.models = [];
|
|
182
|
+
|
|
183
|
+
this.init();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
init() {
|
|
187
|
+
this.fetchProjectInfo();
|
|
188
|
+
this.setupWebSocket();
|
|
189
|
+
this.setupTabs();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async fetchProjectInfo() {
|
|
193
|
+
try {
|
|
194
|
+
const response = await fetch('/api/project');
|
|
195
|
+
this.projectInfo = await response.json();
|
|
196
|
+
this.renderProjectInfo();
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error('获取项目信息失败:', error);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
renderProjectInfo() {
|
|
203
|
+
const projectInfoEl = document.getElementById('project-info');
|
|
204
|
+
if (this.projectInfo && this.projectInfo.name) {
|
|
205
|
+
projectInfoEl.innerHTML = `
|
|
206
|
+
<div><strong>项目名:</strong> ${this.projectInfo.name}</div>
|
|
207
|
+
<div><strong>路径:</strong> ${this.projectInfo.path}</div>
|
|
208
|
+
<div><strong>ModernX:</strong> ${this.projectInfo.hasModernX ? '✅' : '❌'}</div>
|
|
209
|
+
`;
|
|
210
|
+
} else {
|
|
211
|
+
projectInfoEl.innerHTML = '<div>未检测到 ModernX 项目</div>';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
setupWebSocket() {
|
|
216
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
217
|
+
const wsUrl = `${protocol}//${window.location.host}`;
|
|
218
|
+
|
|
219
|
+
this.ws = new WebSocket(wsUrl);
|
|
220
|
+
|
|
221
|
+
this.ws.onopen = () => {
|
|
222
|
+
console.log('WebSocket 连接已建立');
|
|
223
|
+
this.updateConnectionStatus('connected');
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
this.ws.onmessage = (event) => {
|
|
227
|
+
try {
|
|
228
|
+
const data = JSON.parse(event.data);
|
|
229
|
+
this.handleMessage(data);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error('解析消息失败:', error);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
this.ws.onclose = () => {
|
|
236
|
+
console.log('WebSocket 连接已关闭');
|
|
237
|
+
this.updateConnectionStatus('disconnected');
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
this.ws.onerror = (error) => {
|
|
241
|
+
console.error('WebSocket 错误:', error);
|
|
242
|
+
this.updateConnectionStatus('disconnected');
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
updateConnectionStatus(status) {
|
|
247
|
+
const statusEl = document.getElementById('connection-status');
|
|
248
|
+
statusEl.className = `status ${status}`;
|
|
249
|
+
statusEl.textContent = status === 'connected' ? '已连接' : '连接断开';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
handleMessage(data) {
|
|
253
|
+
if (data.type === 'state') {
|
|
254
|
+
this.currentState = data.payload;
|
|
255
|
+
this.renderState();
|
|
256
|
+
} else if (data.type === 'action') {
|
|
257
|
+
this.actions.unshift(data.payload);
|
|
258
|
+
this.actions = this.actions.slice(0, 50); // 只保留最近50个动作
|
|
259
|
+
this.renderActions();
|
|
260
|
+
} else if (data.type === 'models') {
|
|
261
|
+
this.models = data.payload;
|
|
262
|
+
this.renderModels();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
renderState() {
|
|
267
|
+
const stateEl = document.getElementById('current-state');
|
|
268
|
+
stateEl.textContent = JSON.stringify(this.currentState, null, 2);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
renderActions() {
|
|
272
|
+
const actionsEl = document.getElementById('actions-list');
|
|
273
|
+
if (this.actions.length === 0) {
|
|
274
|
+
actionsEl.innerHTML = '<div>暂无动作记录</div>';
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
actionsEl.innerHTML = this.actions.map(action => `
|
|
279
|
+
<div class="action-item">
|
|
280
|
+
<div class="action-type">${action.type}</div>
|
|
281
|
+
<div class="action-time">${new Date(action.timestamp).toLocaleString()}</div>
|
|
282
|
+
${action.payload ? `<div>Payload: ${JSON.stringify(action.payload)}</div>` : ''}
|
|
283
|
+
</div>
|
|
284
|
+
`).join('');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
renderModels() {
|
|
288
|
+
const modelsEl = document.getElementById('models-detail');
|
|
289
|
+
const modelsListEl = document.getElementById('models-list');
|
|
290
|
+
|
|
291
|
+
if (this.models.length === 0) {
|
|
292
|
+
modelsEl.innerHTML = '<div>暂无模型数据</div>';
|
|
293
|
+
modelsListEl.innerHTML = '<div>无模型</div>';
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 渲染侧边栏模型列表
|
|
298
|
+
modelsListEl.innerHTML = this.models.map(model =>
|
|
299
|
+
`<div style="padding: 0.5rem 0; border-bottom: 1px solid #eee;">${model.namespace}</div>`
|
|
300
|
+
).join('');
|
|
301
|
+
|
|
302
|
+
// 渲染详细模型信息
|
|
303
|
+
modelsEl.innerHTML = this.models.map(model => `
|
|
304
|
+
<div class="state-view">
|
|
305
|
+
<h3>${model.namespace}</h3>
|
|
306
|
+
<div class="state-json">${JSON.stringify(model.state, null, 2)}</div>
|
|
307
|
+
</div>
|
|
308
|
+
`).join('');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
setupTabs() {
|
|
312
|
+
const tabs = document.querySelectorAll('.tab');
|
|
313
|
+
tabs.forEach(tab => {
|
|
314
|
+
tab.addEventListener('click', () => {
|
|
315
|
+
const tabName = tab.dataset.tab;
|
|
316
|
+
|
|
317
|
+
// 更新活动标签
|
|
318
|
+
tabs.forEach(t => t.classList.remove('active'));
|
|
319
|
+
tab.classList.add('active');
|
|
320
|
+
|
|
321
|
+
// 显示对应内容
|
|
322
|
+
document.querySelectorAll('.tab-content').forEach(content => {
|
|
323
|
+
content.style.display = 'none';
|
|
324
|
+
});
|
|
325
|
+
document.getElementById(`${tabName}-content`).style.display = 'block';
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 启动 GUI
|
|
332
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
333
|
+
new ModernXGUI();
|
|
334
|
+
});
|
|
335
|
+
</script>
|
|
336
|
+
</body>
|
|
337
|
+
</html>
|
package/package.json
CHANGED
package/src/lib/server.js
CHANGED
|
@@ -9,6 +9,11 @@ async function startServer(options = {}) {
|
|
|
9
9
|
// Serve static files from GUI dist
|
|
10
10
|
app.use(express.static(path.join(__dirname, '../dist')));
|
|
11
11
|
|
|
12
|
+
// Default route to serve index.html
|
|
13
|
+
app.get('/', (req, res) => {
|
|
14
|
+
res.sendFile(path.join(__dirname, '../dist/index.html'));
|
|
15
|
+
});
|
|
16
|
+
|
|
12
17
|
// API endpoint for project info
|
|
13
18
|
app.get('/api/project', (req, res) => {
|
|
14
19
|
res.json(options.projectInfo || {});
|