@aiyiran/myclaw 1.0.197 → 1.0.199

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.
@@ -1,17 +1,20 @@
1
1
  /**
2
2
  * ============================================================================
3
- * MyClaw Artifacts — 学生作品展示面板
3
+ * MyClaw Artifacts — 学生作品展示
4
4
  * ============================================================================
5
5
  *
6
6
  * 功能:
7
- * 右下角 "🎨 作品" 按钮,点击弹出作品面板
8
- * workspace .myclaw/__MY_ARTIFACTS__.json 读取作品清单
7
+ * 右下角 "🎨 作品" 按钮,点击后右侧常驻显示作品列表面板
8
+ * 列表展示:编号 / 标题 / 文件名 / 更新时间
9
+ * 点击条目弹出 iframe 预览弹框,点击遮罩或叉号关闭
9
10
  *
10
- * JSON 数据格式:
11
+ * JSON 数据格式(.myclaw/__MY_ARTIFACTS__.json):
11
12
  * {
12
13
  * "workspace_id": "main",
14
+ * "title": "我的小游戏",
15
+ * "updated_at": "2026-04-12T21:35:00+08:00",
13
16
  * "assets": [
14
- * { "name": "射击游戏", "type": "html", "path": "myclaw/shooting-game.html" }
17
+ * { "id": "asset-001", "type": "html", "name": "射击游戏", "path": "myclaw/shooting-game.html" }
15
18
  * ]
16
19
  * }
17
20
  *
@@ -24,12 +27,12 @@
24
27
  'use strict';
25
28
 
26
29
  // ═══ 状态 ═══
27
- var artifactsOpen = false;
30
+ var panelVisible = false;
31
+ var cachedData = null;
28
32
 
29
33
  // ═══ 工具:从 URL 解析 agent 名称 ═══
30
34
  function getAgentName() {
31
35
  var params = new URLSearchParams(window.location.search);
32
- // 优先从 session 参数解析: agent:main:feishu:... → main
33
36
  var session = params.get('session');
34
37
  if (session) {
35
38
  var decoded = decodeURIComponent(session);
@@ -38,12 +41,18 @@
38
41
  if (parts.length >= 2) return parts[1];
39
42
  }
40
43
  }
41
- // fallback: agent 参数
42
44
  var agent = params.get('agent');
43
45
  if (agent) return agent;
44
46
  return '';
45
47
  }
46
48
 
49
+ // ═══ 构建预览 URL ═══
50
+ function buildPreviewUrl(data, assetPath) {
51
+ var wsName = data.workspace_id || '';
52
+ var wsPrefix = wsName === 'main' ? 'workspace' : 'workspace-' + wsName;
53
+ return window.location.origin + '/cmd/api/preview?path=' + wsPrefix + '/' + assetPath;
54
+ }
55
+
47
56
  // ═══ 创建按钮 ═══
48
57
  function createArtifactsButton() {
49
58
  if (document.querySelector('#myclaw-artifacts-btn')) return;
@@ -71,10 +80,10 @@
71
80
  btn.title = '\u67E5\u770B\u5B66\u751F\u4F5C\u54C1';
72
81
 
73
82
  btn.onmouseenter = function () { btn.style.background = 'rgba(80, 80, 80, 0.95)'; btn.style.transform = 'scale(1.05)'; };
74
- btn.onmouseleave = function () { if (!artifactsOpen) { btn.style.background = 'rgba(100, 100, 100, 0.7)'; } btn.style.transform = 'scale(1)'; };
83
+ btn.onmouseleave = function () { if (!panelVisible) { btn.style.background = 'rgba(100, 100, 100, 0.7)'; } btn.style.transform = 'scale(1)'; };
75
84
  btn.onclick = function (e) {
76
85
  e.stopPropagation();
77
- if (artifactsOpen) {
86
+ if (panelVisible) {
78
87
  closeArtifactsPanel();
79
88
  } else {
80
89
  openArtifactsPanel();
@@ -84,43 +93,31 @@
84
93
  document.body.appendChild(btn);
85
94
  }
86
95
 
87
- // ═══ 打开面板 ═══
96
+ // ═══ 打开右侧列表面板 ═══
88
97
  function openArtifactsPanel() {
89
- if (document.querySelector('#myclaw-artifacts-modal')) return;
90
- artifactsOpen = true;
98
+ if (document.querySelector('#myclaw-artifacts-panel')) return;
99
+ panelVisible = true;
91
100
 
92
- // 按钮 active 态
93
101
  var btn = document.querySelector('#myclaw-artifacts-btn');
94
102
  if (btn) btn.style.background = 'rgba(80, 80, 80, 0.95)';
95
103
 
96
- // 遮罩
97
- var overlay = document.createElement('div');
98
- overlay.id = 'myclaw-artifacts-modal';
99
- overlay.style.cssText = [
104
+ // 面板
105
+ var panel = document.createElement('div');
106
+ panel.id = 'myclaw-artifacts-panel';
107
+ panel.style.cssText = [
100
108
  'position: fixed',
101
- 'top: 0',
102
- 'left: 0',
103
- 'width: 100vw',
104
- 'height: 100vh',
105
- 'background: rgba(0, 0, 0, 0.3)',
106
- 'z-index: 99998',
107
- 'display: flex',
108
- 'align-items: center',
109
- 'justify-content: center',
110
- 'animation: myclaw-fade-in 0.15s ease',
111
- ].join(';');
112
-
113
- // 面板容器
114
- var box = document.createElement('div');
115
- box.style.cssText = [
116
- 'width: 480px',
117
- 'max-height: 70vh',
109
+ 'top: 400px',
110
+ 'right: 0',
111
+ 'width: 380px',
112
+ 'max-height: calc(100vh - 420px)',
118
113
  'background: #1e1e2e',
119
- 'border-radius: 8px',
114
+ 'border-radius: 8px 0 0 8px',
120
115
  'overflow: hidden',
121
116
  'display: flex',
122
117
  'flex-direction: column',
123
- 'box-shadow: 0 8px 32px rgba(0,0,0,0.4)',
118
+ 'box-shadow: -4px 0 24px rgba(0,0,0,0.3)',
119
+ 'z-index: 99997',
120
+ 'animation: myclaw-slide-in-right 0.2s ease',
124
121
  ].join(';');
125
122
 
126
123
  // 标题栏
@@ -129,12 +126,13 @@
129
126
  'display: flex',
130
127
  'align-items: center',
131
128
  'justify-content: space-between',
132
- 'padding: 10px 16px',
129
+ 'padding: 10px 14px',
133
130
  'background: #2d2d3f',
134
131
  'color: #cdd6f4',
135
- 'font-size: 14px',
132
+ 'font-size: 13px',
136
133
  'font-family: monospace',
137
134
  'user-select: none',
135
+ 'flex-shrink: 0',
138
136
  ].join(';');
139
137
  header.innerHTML = '<span>\uD83C\uDFA8 \u5B66\u751F\u4F5C\u54C1</span>';
140
138
 
@@ -152,39 +150,48 @@
152
150
  content.style.cssText = [
153
151
  'flex: 1',
154
152
  'overflow-y: auto',
155
- 'padding: 16px',
153
+ 'padding: 8px',
156
154
  'color: #cdd6f4',
157
155
  'font-family: monospace',
158
- 'font-size: 13px',
156
+ 'font-size: 12px',
159
157
  ].join(';');
160
158
 
161
- // 加载中
162
159
  content.innerHTML = '<div style="text-align:center;padding:32px;color:#888;">加载中...</div>';
163
160
 
164
- box.appendChild(header);
165
- box.appendChild(content);
166
- overlay.appendChild(box);
161
+ panel.appendChild(header);
162
+ panel.appendChild(content);
163
+ document.body.appendChild(panel);
167
164
 
168
- // 点击遮罩关闭
169
- overlay.onclick = function (e) {
170
- if (e.target === overlay) closeArtifactsPanel();
171
- };
165
+ // 请求数据
166
+ if (cachedData) {
167
+ renderArtifactsList(content, cachedData);
168
+ } else {
169
+ fetchArtifacts(content);
170
+ }
171
+ }
172
172
 
173
- document.body.appendChild(overlay);
173
+ // ═══ 关闭右侧面板 ═══
174
+ function closeArtifactsPanel() {
175
+ var panel = document.querySelector('#myclaw-artifacts-panel');
176
+ if (panel) panel.remove();
177
+ panelVisible = false;
174
178
 
175
- // 异步请求数据
176
- fetchArtifacts(content);
179
+ var btn = document.querySelector('#myclaw-artifacts-btn');
180
+ if (btn) btn.style.background = 'rgba(100, 100, 100, 0.7)';
177
181
  }
178
182
 
179
183
  // ═══ 请求数据 ═══
180
184
  function fetchArtifacts(contentEl) {
181
- var url = window.location.origin + '/cmd/api/preview?path=.myclaw/__MY_ARTIFACTS__.json';
185
+ var agentName = getAgentName();
186
+ var wsPrefix = agentName === 'main' ? 'workspace' : 'workspace-' + agentName;
187
+ var url = window.location.origin + '/cmd/api/preview?path=' + wsPrefix + '/.myclaw/__MY_ARTIFACTS__.json';
182
188
  fetch(url)
183
189
  .then(function (res) {
184
190
  if (!res.ok) throw new Error('HTTP ' + res.status);
185
191
  return res.json();
186
192
  })
187
193
  .then(function (data) {
194
+ cachedData = data;
188
195
  if (!data || !data.assets || !data.assets.length) {
189
196
  contentEl.innerHTML = '<div style="text-align:center;padding:32px;color:#888;">暂无作品</div>';
190
197
  return;
@@ -200,57 +207,185 @@
200
207
  // ═══ 渲染列表 ═══
201
208
  function renderArtifactsList(container, data) {
202
209
  container.innerHTML = '';
203
- // 拼接: base + workspace前缀 + / + 资源路径
204
- // main → workspace, 其他 → workspace-{name}
205
- var wsName = data.workspace_id || '';
206
- var wsPrefix = wsName === 'main' ? 'workspace' : 'workspace-' + wsName;
207
- var baseUrl = window.location.origin + '/cmd/api/preview?path=' + wsPrefix + '/';
208
-
209
- data.assets.forEach(function (asset) {
210
- var card = document.createElement('a');
211
- card.href = baseUrl + asset.path;
212
- card.target = '_blank';
213
- card.rel = 'noopener noreferrer';
214
- card.style.cssText = [
215
- 'display: block',
216
- 'padding: 12px 14px',
217
- 'margin-bottom: 10px',
218
- 'background: #2d2d3f',
219
- 'border-radius: 6px',
220
- 'color: #cdd6f4',
221
- 'text-decoration: none',
222
- 'transition: background 0.15s',
210
+
211
+ // 表头
212
+ var tableHeader = document.createElement('div');
213
+ tableHeader.style.cssText = [
214
+ 'display: flex',
215
+ 'padding: 6px 10px',
216
+ 'background: #252536',
217
+ 'color: #888',
218
+ 'font-size: 11px',
219
+ 'border-bottom: 1px solid #3d3d5c',
220
+ 'flex-shrink: 0',
221
+ ].join(';');
222
+ tableHeader.innerHTML = [
223
+ '<span style="width:30px;text-align:center;">#</span>',
224
+ '<span style="flex:2;">标题</span>',
225
+ '<span style="flex:1.5;">文件名</span>',
226
+ '<span style="flex:1;text-align:right;">更新时间</span>',
227
+ ].join('');
228
+ container.appendChild(tableHeader);
229
+
230
+ data.assets.forEach(function (asset, idx) {
231
+ var row = document.createElement('div');
232
+ row.style.cssText = [
233
+ 'display: flex',
234
+ 'align-items: center',
235
+ 'padding: 8px 10px',
236
+ 'border-bottom: 1px solid rgba(255,255,255,0.05)',
223
237
  'cursor: pointer',
238
+ 'transition: background 0.15s',
224
239
  ].join(';');
225
- card.onmouseenter = function () { card.style.background = '#3d3d5c'; };
226
- card.onmouseleave = function () { card.style.background = '#2d2d3f'; };
240
+ row.onmouseenter = function () { row.style.background = '#2d2d3f'; };
241
+ row.onmouseleave = function () { row.style.background = 'transparent'; };
242
+ row.onclick = function () { openPreviewModal(data, asset); };
243
+
244
+ // 编号
245
+ var num = document.createElement('span');
246
+ num.style.cssText = 'width:30px;text-align:center;color:#888;flex-shrink:0;';
247
+ num.textContent = String(idx + 1);
227
248
 
228
- var title = document.createElement('div');
229
- title.style.cssText = 'font-size: 14px; font-weight: bold; margin-bottom: 4px;';
230
- title.textContent = asset.name || '未命名作品';
249
+ // 标题
250
+ var title = document.createElement('span');
251
+ title.style.cssText = 'flex:2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
252
+ title.textContent = asset.name || '未命名';
253
+ title.title = asset.name || '';
231
254
 
232
- var meta = document.createElement('div');
233
- meta.style.cssText = 'font-size: 11px; color: #888;';
234
- meta.textContent = asset.type ? asset.type.toUpperCase() : '';
255
+ // 文件名
256
+ var fname = document.createElement('span');
257
+ fname.style.cssText = 'flex:1.5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#888;';
258
+ // 从 path 中提取文件名
259
+ var pathParts = (asset.path || '').split('/');
260
+ fname.textContent = pathParts[pathParts.length - 1] || '';
261
+ fname.title = asset.path || '';
235
262
 
236
- card.appendChild(title);
237
- card.appendChild(meta);
238
- container.appendChild(card);
263
+ // 更新时间
264
+ var time = document.createElement('span');
265
+ time.style.cssText = 'flex:1;text-align:right;color:#888;font-size:11px;flex-shrink:0;';
266
+ // 优先用 asset 自身的,fallback 到 data 级别的 updated_at
267
+ var updatedAt = asset.updated_at || data.updated_at || '';
268
+ if (updatedAt) {
269
+ try {
270
+ var d = new Date(updatedAt);
271
+ time.textContent = (d.getMonth() + 1) + '/' + d.getDate() + ' ' + String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
272
+ } catch (e) {
273
+ time.textContent = '';
274
+ }
275
+ }
276
+
277
+ row.appendChild(num);
278
+ row.appendChild(title);
279
+ row.appendChild(fname);
280
+ row.appendChild(time);
281
+ container.appendChild(row);
239
282
  });
240
283
  }
241
284
 
242
- // ═══ 关闭面板 ═══
243
- function closeArtifactsPanel() {
244
- var modal = document.querySelector('#myclaw-artifacts-modal');
285
+ // ═══ 预览弹框(iframe) ═══
286
+ function openPreviewModal(data, asset) {
287
+ if (document.querySelector('#myclaw-artifacts-preview')) return;
288
+
289
+ var overlay = document.createElement('div');
290
+ overlay.id = 'myclaw-artifacts-preview';
291
+ overlay.style.cssText = [
292
+ 'position: fixed',
293
+ 'top: 0',
294
+ 'left: 0',
295
+ 'width: 100vw',
296
+ 'height: 100vh',
297
+ 'background: rgba(0, 0, 0, 0.4)',
298
+ 'z-index: 99999',
299
+ 'display: flex',
300
+ 'align-items: center',
301
+ 'justify-content: center',
302
+ 'animation: myclaw-fade-in 0.15s ease',
303
+ ].join(';');
304
+
305
+ var box = document.createElement('div');
306
+ box.style.cssText = [
307
+ 'width: 90vw',
308
+ 'height: 85vh',
309
+ 'background: #1e1e2e',
310
+ 'border-radius: 8px',
311
+ 'overflow: hidden',
312
+ 'display: flex',
313
+ 'flex-direction: column',
314
+ 'box-shadow: 0 8px 32px rgba(0,0,0,0.5)',
315
+ ].join(';');
316
+
317
+ // 标题栏
318
+ var header = document.createElement('div');
319
+ header.style.cssText = [
320
+ 'display: flex',
321
+ 'align-items: center',
322
+ 'justify-content: space-between',
323
+ 'padding: 8px 14px',
324
+ 'background: #2d2d3f',
325
+ 'color: #cdd6f4',
326
+ 'font-size: 13px',
327
+ 'font-family: monospace',
328
+ 'user-select: none',
329
+ 'flex-shrink: 0',
330
+ ].join(';');
331
+ header.innerHTML = '<span>\uD83C\uDFA8 ' + (asset.name || '预览') + '</span>';
332
+
333
+ var closeBtn = document.createElement('span');
334
+ closeBtn.textContent = '\u2715';
335
+ closeBtn.style.cssText = 'cursor:pointer;padding:2px 6px;border-radius:3px;font-size:14px;transition:background 0.15s;';
336
+ closeBtn.onmouseenter = function () { closeBtn.style.background = 'rgba(255,255,255,0.1)'; };
337
+ closeBtn.onmouseleave = function () { closeBtn.style.background = 'none'; };
338
+ closeBtn.onclick = function () { closePreviewModal(); };
339
+ header.appendChild(closeBtn);
340
+
341
+ // iframe
342
+ var iframe = document.createElement('iframe');
343
+ iframe.src = buildPreviewUrl(data, asset.path);
344
+ iframe.style.cssText = [
345
+ 'flex: 1',
346
+ 'width: 100%',
347
+ 'border: none',
348
+ 'background: #fff',
349
+ ].join(';');
350
+
351
+ box.appendChild(header);
352
+ box.appendChild(iframe);
353
+ overlay.appendChild(box);
354
+
355
+ // 点击遮罩关闭
356
+ overlay.onclick = function (e) {
357
+ if (e.target === overlay) closePreviewModal();
358
+ };
359
+
360
+ document.body.appendChild(overlay);
361
+ }
362
+
363
+ function closePreviewModal() {
364
+ var modal = document.querySelector('#myclaw-artifacts-preview');
245
365
  if (modal) modal.remove();
246
- artifactsOpen = false;
366
+ }
247
367
 
248
- var btn = document.querySelector('#myclaw-artifacts-btn');
249
- if (btn) btn.style.background = 'rgba(100, 100, 100, 0.7)';
368
+ // ═══ 注入样式 ═══
369
+ function injectStyles() {
370
+ if (document.querySelector('#myclaw-artifacts-styles')) return;
371
+ var style = document.createElement('style');
372
+ style.id = 'myclaw-artifacts-styles';
373
+ style.textContent = [
374
+ '@keyframes myclaw-slide-in-right {',
375
+ ' from { transform: translateX(100%); }',
376
+ ' to { transform: translateX(0); }',
377
+ '}',
378
+ '@keyframes myclaw-fade-in {',
379
+ ' from { opacity: 0; }',
380
+ ' to { opacity: 1; }',
381
+ '}',
382
+ ].join('\n');
383
+ document.head.appendChild(style);
250
384
  }
251
385
 
252
386
  // ═══ 启动 ═══
253
387
  function init() {
388
+ injectStyles();
254
389
  createArtifactsButton();
255
390
  console.log('[myclaw-artifacts] ✅ 初始化完成');
256
391
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.0.197",
3
+ "version": "1.0.199",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {