@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.
- package/assets/myclaw-inject.js +168 -28
- package/package.json +1 -1
- package/patch-manifest.json +1 -1
- package/start.js +21 -5
package/assets/myclaw-inject.js
CHANGED
|
@@ -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
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
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
|
-
"
|
|
46
|
+
"cursor: pointer",
|
|
47
|
+
"transition: color 0.2s",
|
|
51
48
|
].join(";");
|
|
52
|
-
bar.textContent = "\uD83D\uDC3E
|
|
49
|
+
bar.textContent = "\uD83D\uDC3E v" + MYCLAW_VERSION;
|
|
50
|
+
bar.title = "\u70B9\u51FB\u6D4B\u8BD5\u9EA6\u514B\u98CE";
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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.
|
|
66
|
-
|
|
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 =
|
|
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
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
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
package/patch-manifest.json
CHANGED
package/start.js
CHANGED
|
@@ -78,12 +78,19 @@ function startWindows() {
|
|
|
78
78
|
}
|
|
79
79
|
console.log('');
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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: 打开浏览器
|