@aiyiran/myclaw 1.0.98 → 1.0.100

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.
@@ -26,7 +26,7 @@
26
26
  var cursorOffset = 0; // 录音开始时光标在 textarea 中的位置
27
27
  var injected = false;
28
28
 
29
- // ═══ 1. 顶部版本号横条 ═══
29
+ // ═══ 1. 右下角版本标签(点击测试麦克风) ═══
30
30
  function createVersionBar() {
31
31
  if (document.querySelector("#myclaw-version-bar")) return;
32
32
 
@@ -34,36 +34,54 @@
34
34
  bar.id = "myclaw-version-bar";
35
35
  bar.style.cssText = [
36
36
  "position: fixed",
37
- "top: 0",
38
- "left: 0",
39
- "right: 0",
40
- "height: 28px",
41
- "line-height: 28px",
42
- "background: linear-gradient(90deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)",
43
- "color: #e94560",
44
- "font-size: 12px",
37
+ "bottom: 8px",
38
+ "right: 8px",
39
+ "padding: 2px 8px",
40
+ "background: none",
41
+ "color: rgba(150, 150, 150, 0.4)",
42
+ "font-size: 10px",
45
43
  "font-family: monospace",
46
- "text-align: center",
47
44
  "z-index: 99999",
48
- "box-shadow: 0 1px 4px rgba(0,0,0,0.3)",
49
45
  "user-select: none",
50
- "letter-spacing: 0.5px",
46
+ "cursor: pointer",
47
+ "transition: color 0.2s",
51
48
  ].join(";");
52
- bar.textContent = "\uD83D\uDC3E MyClaw v" + MYCLAW_VERSION;
49
+ bar.textContent = "\uD83D\uDC3E v" + MYCLAW_VERSION;
50
+ bar.title = "\u70B9\u51FB\u6D4B\u8BD5\u9EA6\u514B\u98CE";
53
51
 
54
- // 测试麦克风按钮
55
- var testBtn = document.createElement("button");
56
- testBtn.textContent = "\uD83D\uDD0A \u6D4B\u8BD5\u9EA6\u514B\u98CE";
57
- testBtn.style.cssText = "background:#e94560;color:white;border:none;padding:2px 8px;border-radius:4px;cursor:pointer;font-size:11px;font-family:monospace;";
58
- testBtn.onclick = function(e) {
52
+ bar.onmouseenter = function () { bar.style.color = "rgba(150, 150, 150, 0.8)"; };
53
+ bar.onmouseleave = function () { bar.style.color = "rgba(150, 150, 150, 0.4)"; };
54
+ bar.onclick = function (e) {
59
55
  e.stopPropagation();
60
- console.log("[myclaw] \u6D4B\u8BD5\u9EA6\u514B\u98CE\u70B9\u51FB");
61
56
  testMicrophone();
62
57
  };
63
- bar.appendChild(testBtn);
64
58
 
65
- document.body.prepend(bar);
66
- document.body.style.paddingTop = "28px";
59
+ document.body.appendChild(bar);
60
+
61
+ // 额外测试按钮:哈哈
62
+ if (!document.querySelector("#myclaw-test-haha")) {
63
+ var hahaBtn = document.createElement("button");
64
+ hahaBtn.id = "myclaw-test-haha";
65
+ hahaBtn.textContent = "哈哈";
66
+ hahaBtn.style.cssText = [
67
+ "position: fixed",
68
+ "bottom: 8px",
69
+ "right: 80px", // 放在版本号左边
70
+ "padding: 2px 8px",
71
+ "background: #10a37f",
72
+ "color: white",
73
+ "border: none",
74
+ "border-radius: 4px",
75
+ "font-size: 10px",
76
+ "cursor: pointer",
77
+ "z-index: 99999",
78
+ ].join(";");
79
+ hahaBtn.onclick = function (e) {
80
+ e.stopPropagation();
81
+ alert("测试更新机制:我是新加入的哈哈按钮!");
82
+ };
83
+ document.body.appendChild(hahaBtn);
84
+ }
67
85
  }
68
86
 
69
87
  // \u6D4B\u8BD5\u9EA6\u514B\u98CE\u51FD\u6570
@@ -183,6 +201,16 @@
183
201
  btn.classList.remove("agent-chat__input-btn--recording");
184
202
  btn.title = "\u8baf\u98de\u8bed\u97f3";
185
203
  }
204
+
205
+ // 同步 textarea 边框状态
206
+ var ta = document.querySelector(".agent-chat__input textarea");
207
+ if (ta) {
208
+ if (recording) {
209
+ ta.classList.add("myclaw-voice-active");
210
+ } else {
211
+ ta.classList.remove("myclaw-voice-active");
212
+ }
213
+ }
186
214
  }
187
215
 
188
216
  // ═══ 4. 录音控制 ═══
@@ -195,6 +223,8 @@
195
223
 
196
224
  voice = new window.VoiceInput({
197
225
  onResult: function (text) {
226
+ // 如果用户已点停止,忽略异步返回的残留结果(防止文字重复)
227
+ if (!recording) return;
198
228
  // 讯飞实时返回识别文字,替换到光标位置
199
229
  pendingText = text;
200
230
  updateTextAtCursor(pendingText);
@@ -262,15 +292,19 @@
262
292
  }
263
293
 
264
294
  function stopVoice() {
295
+ // 先关标志位,阻止 onResult 异步回调继续写入(核心防重复)
265
296
  recording = false;
266
297
  updateButtonUI();
267
298
 
299
+ // 立即快照当前 textarea 值作为最终文字
300
+ var finalText = getTextareaValue();
301
+
268
302
  if (voice) {
269
303
  voice.stop();
270
304
  }
271
305
 
272
- // 最终提交文字
273
- committedText = getTextareaValue();
306
+ // 用快照覆盖,确保后续异步返回不影响
307
+ committedText = finalText;
274
308
  pendingText = "";
275
309
 
276
310
  console.log("[myclaw-voice] \u505c\u6b62\u5f55\u97f3");
@@ -284,10 +318,9 @@
284
318
 
285
319
  var btn = createVoiceButton();
286
320
 
287
- // 插入到 token 计数之前,或追加到末尾
288
- var tokenCount = toolbar.querySelector(".agent-chat__token-count");
289
- if (tokenCount) {
290
- toolbar.insertBefore(btn, tokenCount);
321
+ // 插入到工具栏最前面(排在 Attach file 等按钮之前)
322
+ if (toolbar.firstChild) {
323
+ toolbar.insertBefore(btn, toolbar.firstChild);
291
324
  } else {
292
325
  toolbar.appendChild(btn);
293
326
  }
@@ -296,9 +329,113 @@
296
329
  console.log("[myclaw-inject] \u2705 \u8bed\u97f3\u6309\u94ae\u5df2\u6ce8\u5165");
297
330
  }
298
331
 
332
+ // ═══ 注入录音态样式 ═══
333
+ function injectStyles() {
334
+ if (document.querySelector("#myclaw-voice-styles")) return;
335
+ var style = document.createElement("style");
336
+ style.id = "myclaw-voice-styles";
337
+ style.textContent = [
338
+ /* 隐藏原生 Voice input 按钮(避免与 MyClaw 语音按钮混淆) */
339
+ "button[title=\"Voice input\"]:not(#myclaw-voice-btn) { display: none !important; }",
340
+ /* 按钮录音态:红色脉冲 */
341
+ ".agent-chat__input-btn--recording {",
342
+ " color: #ff4444 !important;",
343
+ " animation: myclaw-pulse 1s ease-in-out infinite !important;",
344
+ " position: relative;",
345
+ "}",
346
+ ".agent-chat__input-btn--recording::after {",
347
+ " content: '';",
348
+ " position: absolute;",
349
+ " top: -2px; left: -2px; right: -2px; bottom: -2px;",
350
+ " border-radius: 50%;",
351
+ " border: 2px solid #ff4444;",
352
+ " animation: myclaw-ring 1.5s ease-out infinite;",
353
+ " pointer-events: none;",
354
+ "}",
355
+ /* textarea 录音态:左侧红色竖线 + 微弱红色背景 */
356
+ ".myclaw-voice-active {",
357
+ " border-left: 3px solid #ff4444 !important;",
358
+ " background: rgba(255, 68, 68, 0.03) !important;",
359
+ " transition: all 0.2s ease;",
360
+ "}",
361
+ /* 脉冲动画 */
362
+ "@keyframes myclaw-pulse {",
363
+ " 0%, 100% { opacity: 1; transform: scale(1); }",
364
+ " 50% { opacity: 0.6; transform: scale(1.1); }",
365
+ "}",
366
+ /* 扩散环动画 */
367
+ "@keyframes myclaw-ring {",
368
+ " 0% { transform: scale(0.8); opacity: 0.8; }",
369
+ " 100% { transform: scale(1.6); opacity: 0; }",
370
+ "}",
371
+ ].join("\n");
372
+ document.head.appendChild(style);
373
+ }
374
+ // ═══ 6. 拦截发送按钮 ═══
375
+
376
+ var sendHooked = false;
377
+
378
+ function hookSendButton() {
379
+ if (sendHooked) return;
380
+ sendHooked = true;
381
+
382
+ // 用捕获阶段拦截,确保在原生 handler 之前执行
383
+ document.addEventListener("click", function (e) {
384
+ var btn = e.target.closest("button.chat-send-btn, button[title=\"Send\"]");
385
+ if (!btn) return;
386
+
387
+ var text = getTextareaValue();
388
+ if (!text || !text.trim()) return; // 空文字不处理
389
+
390
+ // 1) 停止语音输入
391
+ if (recording) {
392
+ stopVoice();
393
+ }
394
+
395
+ // 2) 复制到剪贴板
396
+ try {
397
+ navigator.clipboard.writeText(text).then(function () {
398
+ console.log("[myclaw-send] 📋 已复制到剪贴板:", text.substring(0, 50) + (text.length > 50 ? "..." : ""));
399
+ }).catch(function () {
400
+ // fallback: 老方法
401
+ fallbackCopy(text);
402
+ });
403
+ } catch (ex) {
404
+ fallbackCopy(text);
405
+ }
406
+
407
+ // 3) 让原生 click 继续走(发送消息)
408
+ // 不 preventDefault,不 stopPropagation
409
+
410
+ // 4) 延迟清空 textarea(等原生 handler 读完值后再清)
411
+ setTimeout(function () {
412
+ setTextareaValue("");
413
+ committedText = "";
414
+ pendingText = "";
415
+ cursorOffset = 0;
416
+ console.log("[myclaw-send] ✅ 发送完成,输入框已清空");
417
+ }, 100);
418
+
419
+ }, true); // true = 捕获阶段
420
+
421
+ console.log("[myclaw-inject] ✅ 发送按钮拦截器已挂载");
422
+ }
423
+
424
+ function fallbackCopy(text) {
425
+ var ta = document.createElement("textarea");
426
+ ta.value = text;
427
+ ta.style.cssText = "position:fixed;left:-9999px;";
428
+ document.body.appendChild(ta);
429
+ ta.select();
430
+ try { document.execCommand("copy"); } catch (e) {}
431
+ document.body.removeChild(ta);
432
+ console.log("[myclaw-send] 📋 已复制(fallback)");
433
+ }
434
+
299
435
  // ═══ 启动 ═══
300
436
  function init() {
301
437
  createVersionBar();
438
+ injectStyles();
302
439
 
303
440
  // 初始化 VoiceInput SDK
304
441
  initVoice();
@@ -315,6 +452,9 @@
315
452
 
316
453
  // 尝试立即注入
317
454
  injectButton();
455
+
456
+ // 挂载发送拦截
457
+ hookSendButton();
318
458
  }
319
459
 
320
460
  if (document.readyState === "loading") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.0.98",
3
+ "version": "1.0.100",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "_doc": "MyClaw 注入清单 (auto-generated)。strategy: auto | on | off",
3
- "_generated": "2026-04-01T15:56:26.709Z",
3
+ "_generated": "2026-04-01T17:01:33.260Z",
4
4
  "agents": [
5
5
  {
6
6
  "id": "danci",
package/start.js CHANGED
@@ -78,12 +78,19 @@ function startWindows() {
78
78
  }
79
79
  console.log('');
80
80
 
81
- if (!gatewayRunning) {
82
- // Gateway 未运行: 先升级,再在新窗口启动
83
- console.log('[update] 检查 MyClaw 更新...');
84
- try { execSync('myclaw update', { stdio: 'inherit', timeout: 120000 }); } catch {}
85
- console.log('');
81
+ // 无论 Gateway 是否已运行,都先检查更新 + patch
82
+ console.log('[update] 检查 MyClaw 更新...');
83
+ try { execSync('myclaw update', { stdio: 'inherit', timeout: 120000 }); } catch {}
84
+ console.log('');
85
+
86
+ console.log('[patch] 刷新 UI 插件...');
87
+ try {
88
+ execSync('wsl -d OpenClaw -- myclaw patch', { stdio: 'inherit', timeout: 30000 });
89
+ } catch {}
90
+ console.log('');
86
91
 
92
+ if (!gatewayRunning) {
93
+ // Gateway 未运行: 在新窗口启动(launch 内部会再 patch 一次,幂等无害)
87
94
  console.log('[launch] 在新窗口启动 Gateway...');
88
95
  try {
89
96
  execSync('start "OpenClaw Gateway" wsl -d OpenClaw -- myclaw launch', {
@@ -94,6 +101,15 @@ function startWindows() {
94
101
  } catch (err) {
95
102
  console.error(' ' + C.r + '[失败]' + C.nc + ' ' + err.message);
96
103
  }
104
+ } else {
105
+ // Gateway 已在运行,重启使 patch 生效
106
+ console.log('[restart] 重启 Gateway 使插件生效...');
107
+ try {
108
+ execSync('wsl -d OpenClaw -- openclaw gateway restart', { stdio: 'pipe', timeout: 15000 });
109
+ console.log(' ' + C.g + '[OK]' + C.nc);
110
+ } catch {
111
+ console.log(' ' + C.y + '[跳过]' + C.nc + ' 重启失败,手动 myclaw restart');
112
+ }
97
113
  }
98
114
 
99
115
  // Step 4: 打开浏览器