@aiyiran/myclaw 1.1.76 → 1.1.77

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.
@@ -1664,7 +1664,11 @@
1664
1664
  entryDefaultOpt.value = '';
1665
1665
  entryDefaultOpt.textContent = '选择作品的网页入口文件';
1666
1666
  entrySelect.appendChild(entryDefaultOpt);
1667
- var htmlAssets = (cachedData.assets || []).filter(function (a) { return a.type && a.type.toLowerCase() === 'html'; });
1667
+ var htmlAssets = (cachedData.assets || []).filter(function (a) {
1668
+ if (!(a.type && a.type.toLowerCase() === 'html')) return false;
1669
+ var filename = a.path.split('/').pop();
1670
+ return !filename.startsWith('__');
1671
+ });
1668
1672
  htmlAssets.forEach(function (asset) {
1669
1673
  var opt = document.createElement('option');
1670
1674
  opt.value = asset.path;
@@ -313,507 +313,10 @@
313
313
  } catch (e) {}
314
314
  }
315
315
 
316
- // ═══ 3. Prompt 小助手按钮 ═══
317
-
318
- var promptOpen = false;
319
- var modalVoiceDestroy = null; // 弹框内语音组件的销毁函数
316
+ // ═══ 3. Prompt 小助手按钮(实现见 myclaw-iteration.js) ═══
320
317
 
321
318
  function createPromptButton() {
322
- var btn = document.createElement("button");
323
- btn.id = "myclaw-prompt-btn";
324
- btn.className = "agent-chat__input-btn";
325
- btn.title = "\u63d0\u95ee prompt \u5c0f\u52a9\u624b";
326
- btn.setAttribute("aria-label", "\u63d0\u95ee prompt \u5c0f\u52a9\u624b");
327
- btn.innerHTML = [
328
- '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"',
329
- ' viewBox="0 0 24 24" fill="none" stroke="currentColor"',
330
- ' stroke-width="2" stroke-linecap="round" stroke-linejoin="round">',
331
- ' <circle cx="12" cy="12" r="10"/>',
332
- ' <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>',
333
- ' <line x1="12" y1="17" x2="12.01" y2="17"/>',
334
- '</svg>',
335
- ].join("");
336
-
337
- btn.addEventListener("click", function (e) {
338
- e.stopPropagation();
339
- if (promptOpen) {
340
- closePromptModal();
341
- } else {
342
- openPromptModal();
343
- }
344
- });
345
-
346
- return btn;
347
- }
348
-
349
- // ── 文案配置(集中管理,方便后续修改) ──
350
-
351
- var ITERATION_CONFIG = {
352
- problems: ["不好看", "不好玩", "太简单", "不清楚", "没反应", "不像我想的", "太乱了", "不知道接下来做什么"],
353
- actions: ["加一个新东西", "改一下样子", "改一下内容", "改一下功能", "让它动起来", "让它更有趣", "让它更清楚", "先做一个最简单的新版本"],
354
- };
355
-
356
- function openPromptModal() {
357
- if (document.querySelector("#myclaw-prompt-modal")) return;
358
- promptOpen = true;
359
-
360
- var iconBtn = document.querySelector("#myclaw-prompt-btn");
361
- if (iconBtn) iconBtn.style.color = "#6366f1";
362
-
363
- // ── 状态(关闭即清空) ──
364
- var state = { problem: "", action: "", note: "" };
365
-
366
- // ── 文本生成 ──
367
- function buildText() {
368
- if (!state.problem || !state.action) return "";
369
- var t = "我现在这个作品的问题是【" + state.problem + "】。\n"
370
- + "我下一步想先【" + state.action + "】。\n"
371
- + "请根据我当前正在做的内容,帮我只改一个点,给我一个最简单、最容易成功的下一步方案。";
372
- if (state.note.trim()) t += "\n补充说明:" + state.note.trim();
373
- return t;
374
- }
375
-
376
- function buildPayload() {
377
- return {
378
- type: "iteration_helper",
379
- problem: state.problem,
380
- next_action: state.action,
381
- extra_note: state.note,
382
- generated_text: buildText(),
383
- };
384
- }
385
-
386
- // ── 刷新预览 & 按钮可用性 ──
387
- var previewEl, actionSendBtn, actionInsertBtn;
388
- function refresh() {
389
- var text = buildText();
390
- var ready = !!(state.problem && state.action);
391
- if (previewEl) {
392
- previewEl.textContent = text || "先选上面两项,这里会自动生成要发给 AI 的内容 ✨";
393
- previewEl.style.color = ready ? "#cdd6f4" : "#555";
394
- }
395
- if (actionSendBtn) {
396
- actionSendBtn.disabled = !ready;
397
- actionSendBtn.style.opacity = ready ? "1" : "0.4";
398
- actionSendBtn.style.cursor = ready ? "pointer" : "not-allowed";
399
- }
400
- if (actionInsertBtn) {
401
- actionInsertBtn.disabled = !ready;
402
- actionInsertBtn.style.opacity = ready ? "1" : "0.4";
403
- actionInsertBtn.style.cursor = ready ? "pointer" : "not-allowed";
404
- }
405
- }
406
-
407
- // ── 芯片选择组(单选,再点可取消) ──
408
- function makeChipGroup(items, activeColor, onChange) {
409
- var wrap = document.createElement("div");
410
- wrap.style.cssText = "display:flex;flex-wrap:wrap;gap:7px;";
411
- var chips = [];
412
- items.forEach(function (label) {
413
- var chip = document.createElement("button");
414
- chip.textContent = label;
415
- chip.dataset.selected = "0";
416
- chip.style.cssText = [
417
- "padding:6px 13px",
418
- "border-radius:20px",
419
- "border:1.5px solid #3d3d5c",
420
- "background:#252536",
421
- "color:#888",
422
- "font-size:13px",
423
- "font-family:-apple-system,sans-serif",
424
- "cursor:pointer",
425
- "transition:all 0.15s",
426
- "white-space:nowrap",
427
- ].join(";");
428
-
429
- function applyStyle(sel) {
430
- if (sel) {
431
- chip.style.background = activeColor;
432
- chip.style.borderColor = activeColor;
433
- chip.style.color = "#fff";
434
- chip.style.fontWeight = "bold";
435
- } else {
436
- chip.style.background = "#252536";
437
- chip.style.borderColor = "#3d3d5c";
438
- chip.style.color = "#888";
439
- chip.style.fontWeight = "normal";
440
- }
441
- }
442
-
443
- chip.onmouseenter = function () {
444
- if (chip.dataset.selected !== "1") chip.style.borderColor = activeColor;
445
- };
446
- chip.onmouseleave = function () {
447
- if (chip.dataset.selected !== "1") chip.style.borderColor = "#3d3d5c";
448
- };
449
- chip.onclick = function () {
450
- var nowOn = chip.dataset.selected !== "1";
451
- // 取消同组其他
452
- chips.forEach(function (c) { c.dataset.selected = "0"; applyStyle.call({chip:c}, false); });
453
- // 重新用当前 chip 的 applyStyle(通过闭包拿到正确 chip)
454
- chips.forEach(function (c) {
455
- if (c !== chip) {
456
- c.style.background = "#252536";
457
- c.style.borderColor = "#3d3d5c";
458
- c.style.color = "#888";
459
- c.style.fontWeight = "normal";
460
- c.dataset.selected = "0";
461
- }
462
- });
463
- chip.dataset.selected = nowOn ? "1" : "0";
464
- applyStyle(nowOn);
465
- onChange(nowOn ? label : "");
466
- };
467
-
468
- chips.push(chip);
469
- wrap.appendChild(chip);
470
- });
471
- return wrap;
472
- }
473
-
474
- // ── 区域标题 ──
475
- function makeLabel(text) {
476
- var el = document.createElement("div");
477
- el.textContent = text;
478
- el.style.cssText = "font-size:13px;font-weight:bold;color:#cdd6f4;margin-bottom:8px;font-family:-apple-system,sans-serif;";
479
- return el;
480
- }
481
-
482
- // ══════════ DOM 组装 ══════════
483
-
484
- // 遮罩
485
- var overlay = document.createElement("div");
486
- overlay.id = "myclaw-prompt-modal";
487
- overlay.style.cssText = [
488
- "position:fixed", "top:0", "left:0",
489
- "width:100vw", "height:100vh",
490
- "background:rgba(0,0,0,0.45)",
491
- "z-index:99998",
492
- "display:flex", "align-items:center", "justify-content:center",
493
- "animation:myclaw-fade-in 0.15s ease",
494
- ].join(";");
495
-
496
- // 弹框
497
- var box = document.createElement("div");
498
- box.style.cssText = [
499
- "width:560px", "max-width:96vw", "max-height:92vh",
500
- "background:#1e1e2e",
501
- "border-radius:12px",
502
- "overflow:hidden",
503
- "display:flex", "flex-direction:column",
504
- "box-shadow:0 12px 48px rgba(0,0,0,0.6)",
505
- ].join(";");
506
-
507
- // 标题栏
508
- var header = document.createElement("div");
509
- header.style.cssText = [
510
- "display:flex", "align-items:center", "justify-content:space-between",
511
- "padding:13px 18px",
512
- "background:linear-gradient(135deg,#6366f1,#8b5cf6)",
513
- "color:#fff",
514
- "font-size:15px", "font-weight:bold",
515
- "font-family:-apple-system,sans-serif",
516
- "user-select:none", "flex-shrink:0",
517
- ].join(";");
518
- header.innerHTML = "<span>🤔 我卡住了,帮我继续改</span>";
519
- var closeX = document.createElement("span");
520
- closeX.textContent = "✕";
521
- closeX.style.cssText = "cursor:pointer;padding:2px 8px;border-radius:4px;font-size:15px;transition:background 0.15s;";
522
- closeX.onmouseenter = function () { closeX.style.background = "rgba(255,255,255,0.2)"; };
523
- closeX.onmouseleave = function () { closeX.style.background = "none"; };
524
- closeX.onclick = function () { closePromptModal(); };
525
- header.appendChild(closeX);
526
-
527
- // 滚动内容区
528
- var scroll = document.createElement("div");
529
- scroll.style.cssText = "flex:1;overflow-y:auto;padding:20px 20px 8px;display:flex;flex-direction:column;gap:20px;";
530
-
531
- // ── 第1区:问题 ──
532
- var sec1 = document.createElement("div");
533
- sec1.appendChild(makeLabel("现在最想改的问题是?"));
534
- sec1.appendChild(makeChipGroup(ITERATION_CONFIG.problems, "#f59e0b", function (val) {
535
- state.problem = val; refresh();
536
- }));
537
- scroll.appendChild(sec1);
538
-
539
- // ── 第2区:行动 ──
540
- var sec2 = document.createElement("div");
541
- sec2.appendChild(makeLabel("你下一步想先改什么?"));
542
- sec2.appendChild(makeChipGroup(ITERATION_CONFIG.actions, "#6366f1", function (val) {
543
- state.action = val; refresh();
544
- }));
545
- scroll.appendChild(sec2);
546
-
547
- // ── 第3区:补充(带语音输入) ──
548
- var sec3 = document.createElement("div");
549
- sec3.appendChild(makeLabel("可以补充一点点(可不填)"));
550
- var placeholders = ["比如:我想做成飞行射击", "比如:我想更像小猫主题", "比如:按钮点了以后想有变化"];
551
- var noteField = makeVoiceInputField(
552
- placeholders[Math.floor(Math.random() * placeholders.length)],
553
- 60,
554
- function (val) { state.note = val; refresh(); }
555
- );
556
- modalVoiceDestroy = noteField.destroy;
557
- sec3.appendChild(noteField.el);
558
- scroll.appendChild(sec3);
559
-
560
- // ── 预览区 ──
561
- var previewWrap = document.createElement("div");
562
- previewWrap.style.cssText = [
563
- "padding:12px 14px",
564
- "background:#252536",
565
- "border-radius:8px",
566
- "border-left:3px solid #6366f1",
567
- ].join(";");
568
- var previewLabel = document.createElement("div");
569
- previewLabel.textContent = "将要发给 AI 的内容:";
570
- previewLabel.style.cssText = "font-size:11px;color:#6366f1;font-weight:bold;margin-bottom:6px;font-family:monospace;letter-spacing:0.3px;";
571
- previewEl = document.createElement("div");
572
- previewEl.style.cssText = "font-size:13px;line-height:1.7;white-space:pre-wrap;font-family:-apple-system,sans-serif;word-break:break-all;";
573
- previewWrap.appendChild(previewLabel);
574
- previewWrap.appendChild(previewEl);
575
- scroll.appendChild(previewWrap);
576
-
577
- // ── 底部按钮区 ──
578
- var footer = document.createElement("div");
579
- footer.style.cssText = [
580
- "display:flex", "gap:10px",
581
- "padding:14px 20px",
582
- "border-top:1px solid #2d2d3f",
583
- "flex-shrink:0",
584
- "background:#1e1e2e",
585
- ].join(";");
586
-
587
- actionSendBtn = document.createElement("button");
588
- actionSendBtn.textContent = "➤ 直接发给 AI";
589
- actionSendBtn.disabled = true;
590
- actionSendBtn.style.cssText = [
591
- "flex:1", "padding:11px 0",
592
- "background:#10b981", "color:#fff",
593
- "border:none", "border-radius:8px",
594
- "font-size:14px", "font-weight:bold",
595
- "font-family:-apple-system,sans-serif",
596
- "cursor:not-allowed", "transition:all 0.15s", "opacity:0.4",
597
- ].join(";");
598
- actionSendBtn.onmouseenter = function () { if (!actionSendBtn.disabled) actionSendBtn.style.background = "#059669"; };
599
- actionSendBtn.onmouseleave = function () { if (!actionSendBtn.disabled) actionSendBtn.style.background = "#10b981"; };
600
- actionSendBtn.onclick = function () {
601
- if (actionSendBtn.disabled) return;
602
- var payload = buildPayload();
603
- console.log("[myclaw-prompt] onSendDirect", payload);
604
- insertPromptText(payload.generated_text);
605
- closePromptModal();
606
- setTimeout(function () { trySend(); }, 50);
607
- };
608
-
609
- actionInsertBtn = document.createElement("button");
610
- actionInsertBtn.textContent = "⬇ 先放到聊天框";
611
- actionInsertBtn.disabled = true;
612
- actionInsertBtn.style.cssText = [
613
- "flex:1", "padding:11px 0",
614
- "background:#6366f1", "color:#fff",
615
- "border:none", "border-radius:8px",
616
- "font-size:14px", "font-weight:bold",
617
- "font-family:-apple-system,sans-serif",
618
- "cursor:not-allowed", "transition:all 0.15s", "opacity:0.4",
619
- ].join(";");
620
- actionInsertBtn.onmouseenter = function () { if (!actionInsertBtn.disabled) actionInsertBtn.style.background = "#4f46e5"; };
621
- actionInsertBtn.onmouseleave = function () { if (!actionInsertBtn.disabled) actionInsertBtn.style.background = "#6366f1"; };
622
- actionInsertBtn.onclick = function () {
623
- if (actionInsertBtn.disabled) return;
624
- var payload = buildPayload();
625
- console.log("[myclaw-prompt] onInsertToInput", payload);
626
- insertPromptText(payload.generated_text);
627
- closePromptModal();
628
- };
629
-
630
- footer.appendChild(actionSendBtn);
631
- footer.appendChild(actionInsertBtn);
632
-
633
- // ── 拼装 ──
634
- box.appendChild(header);
635
- box.appendChild(scroll);
636
- box.appendChild(footer);
637
- overlay.appendChild(box);
638
- overlay.onclick = function (e) { if (e.target === overlay) closePromptModal(); };
639
- document.body.appendChild(overlay);
640
-
641
- refresh();
642
- }
643
-
644
- function closePromptModal() {
645
- if (modalVoiceDestroy) { modalVoiceDestroy(); modalVoiceDestroy = null; }
646
- var modal = document.querySelector("#myclaw-prompt-modal");
647
- if (modal) modal.remove();
648
- promptOpen = false;
649
-
650
- var btn = document.querySelector("#myclaw-prompt-btn");
651
- if (btn) { btn.style.color = ""; }
652
- }
653
-
654
- /**
655
- * 在 textarea 光标处插入 prompt 文字
656
- */
657
- function insertPromptText(text) {
658
- var ta = document.querySelector(".agent-chat__input textarea");
659
- if (!ta) return;
660
-
661
- var start = ta.selectionStart || 0;
662
- var end = ta.selectionEnd || 0;
663
- var current = ta.value;
664
- var newValue = current.substring(0, start) + text + current.substring(end);
665
-
666
- var setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value").set;
667
- setter.call(ta, newValue);
668
- ta.dispatchEvent(new Event("input", { bubbles: true }));
669
-
670
- var newCursor = start + text.length;
671
- try { ta.setSelectionRange(newCursor, newCursor); } catch (e) {}
672
- ta.focus();
673
- }
674
-
675
- // ═══ 3.6 带语音的输入框组件 ═══
676
-
677
- /**
678
- * makeVoiceInputField(placeholder, maxLength, onChange)
679
- * 返回 { el, getValue, setValue, destroy }
680
- *
681
- * 完全独立:有自己的 VoiceInput 实例和录音状态,
682
- * 不污染主 recording / voice 全局变量。
683
- */
684
- function makeVoiceInputField(placeholder, maxLength, onChange) {
685
- var lVoice = null; // 本组件的 VoiceInput 实例
686
- var lRecording = false; // 本组件的录音状态
687
- var lStopping = false; // 本组件的 stopping 窗口
688
-
689
- // ── 容器 ──
690
- var wrap = document.createElement("div");
691
- wrap.style.cssText = "display:flex;gap:8px;align-items:center;width:100%;box-sizing:border-box;";
692
-
693
- // ── 输入框 ──
694
- var input = document.createElement("input");
695
- input.type = "text";
696
- input.maxLength = maxLength || 60;
697
- input.placeholder = placeholder || "";
698
- input.style.cssText = [
699
- "flex:1", "min-width:0", "padding:9px 12px",
700
- "background:#252536",
701
- "border:1.5px solid #3d3d5c",
702
- "border-radius:8px",
703
- "color:#cdd6f4",
704
- "font-size:13px",
705
- "font-family:-apple-system,sans-serif",
706
- "outline:none",
707
- "transition:border-color 0.15s, border-left-width 0.1s",
708
- ].join(";");
709
- input.onfocus = function () { if (!lRecording) input.style.borderColor = "#6366f1"; };
710
- input.onblur = function () { if (!lRecording) input.style.borderColor = "#3d3d5c"; };
711
- input.oninput = function () { if (onChange) onChange(input.value); };
712
-
713
- // ── 麦克风按钮 ──
714
- var micBtn = document.createElement("button");
715
- micBtn.type = "button";
716
- micBtn.title = "语音输入";
717
- micBtn.style.cssText = [
718
- "width:34px", "height:34px", "flex-shrink:0",
719
- "border-radius:50%",
720
- "border:1.5px solid #3d3d5c",
721
- "background:#252536",
722
- "color:#888",
723
- "cursor:pointer",
724
- "display:flex", "align-items:center", "justify-content:center",
725
- "transition:all 0.15s",
726
- "position:relative",
727
- ].join(";");
728
- micBtn.innerHTML = [
729
- '<svg width="14" height="14" viewBox="0 0 24 24" fill="none"',
730
- ' stroke="currentColor" stroke-width="2"',
731
- ' stroke-linecap="round" stroke-linejoin="round">',
732
- ' <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/>',
733
- ' <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>',
734
- ' <line x1="12" x2="12" y1="19" y2="22"/>',
735
- '</svg>',
736
- ].join("");
737
-
738
- // ── UI 状态切换 ──
739
- function setUI(recording) {
740
- if (recording) {
741
- micBtn.style.background = "#ff4444";
742
- micBtn.style.borderColor = "#ff4444";
743
- micBtn.style.color = "#fff";
744
- micBtn.title = "停止录音";
745
- input.style.borderColor = "#ff4444";
746
- input.style.borderLeftWidth = "3px";
747
- } else {
748
- micBtn.style.background = "#252536";
749
- micBtn.style.borderColor = "#3d3d5c";
750
- micBtn.style.color = "#888";
751
- micBtn.title = "语音输入";
752
- input.style.borderColor = "#3d3d5c";
753
- input.style.borderLeftWidth = "1.5px";
754
- }
755
- }
756
-
757
- // ── 初始化本组件的 VoiceInput 实例 ──
758
- function initLVoice() {
759
- if (typeof window.VoiceInput === "undefined") return false;
760
- lVoice = new window.VoiceInput({
761
- onResult: function (text) {
762
- if (!lRecording && !lStopping) return;
763
- input.value = text;
764
- if (onChange) onChange(text);
765
- },
766
- onStatusChange: function (oldS, newS) {
767
- // 讯飞 60s 断开 → 自动重连
768
- if (newS === "idle" && lRecording && !lStopping) {
769
- setTimeout(function () {
770
- if (lRecording && lVoice) lVoice.start();
771
- }, 300);
772
- }
773
- },
774
- onError: function (err) {
775
- console.error("[voice-input-field] error:", err);
776
- lRecording = false; lStopping = false;
777
- setUI(false);
778
- },
779
- });
780
- return true;
781
- }
782
-
783
- function startLocal() {
784
- if (!lVoice && !initLVoice()) {
785
- console.warn("[voice-input-field] VoiceInput SDK 未加载");
786
- return;
787
- }
788
- lRecording = true;
789
- setUI(true);
790
- lVoice.start();
791
- }
792
-
793
- function stopLocal() {
794
- if (!lRecording) return;
795
- lStopping = true;
796
- lRecording = false;
797
- setUI(false);
798
- setTimeout(function () {
799
- lStopping = false;
800
- if (lVoice) lVoice.stop();
801
- }, 1500);
802
- }
803
-
804
- micBtn.onclick = function () {
805
- if (lRecording) { stopLocal(); } else { startLocal(); }
806
- };
807
-
808
- wrap.appendChild(input);
809
- wrap.appendChild(micBtn);
810
-
811
- return {
812
- el: wrap,
813
- getValue: function () { return input.value; },
814
- setValue: function (v) { input.value = v; if (onChange) onChange(v); },
815
- destroy: function () { if (lRecording) stopLocal(); },
816
- };
319
+ return window.myclawIteration.createButton();
817
320
  }
818
321
 
819
322
  // ═══ 3.5 语音按钮 ═══
@@ -1013,6 +516,10 @@ btn.addEventListener("click", function () {
1013
516
  var toolbar = document.querySelector(".agent-chat__toolbar-left");
1014
517
  if (!toolbar || document.querySelector("#myclaw-voice-btn")) return;
1015
518
 
519
+ if (window.myclawIteration) {
520
+ window.myclawIteration.init({ trySend: trySend });
521
+ }
522
+
1016
523
  var voiceBtn = createVoiceButton();
1017
524
  var promptBtn = createPromptButton();
1018
525
 
@@ -0,0 +1,652 @@
1
+ /**
2
+ * ============================================================================
3
+ * myclaw-iteration.js — 我卡住了,帮我继续改
4
+ * ============================================================================
5
+ *
6
+ * 功能:
7
+ * - 在聊天工具栏注入「🤔」按钮
8
+ * - 弹出两级选择面板(问题 → 行动),动态联动
9
+ * - 根据选择生成针对性提示词发给 AI
10
+ *
11
+ * 依赖:
12
+ * - window.VoiceInput(讯飞 SDK,可选,用于语音补充输入)
13
+ * - window.myclawIteration.init({ trySend }) 在 myclaw-inject.js 初始化时注入
14
+ * ============================================================================
15
+ */
16
+ (function () {
17
+ "use strict";
18
+
19
+ // ══════════ 配置 ══════════
20
+
21
+ var ITERATION_CONFIG = {
22
+ problems: ["样子不对", "逻辑不对", "不够好玩", "缺少创意玩法", "救命我卡住了"],
23
+ actionsByProblem: {
24
+ "样子不对": ["更好看", "更像我想的", "更清楚", "我还没想好"],
25
+ "逻辑不对": ["让它有反应", "让它运行正确", "先只改一个功能", "我还没想好"],
26
+ "不够好玩": ["加一个东西", "加一点互动", "加一个角色", "我还没想好"],
27
+ "缺少创意玩法": ["加一个新机制", "加一个有趣挑战", "加一个惊喜元素", "我还没想好"],
28
+ "救命我卡住了": ["先鼓励我一下", "帮我找卡点", "帮我缩成一步", "直接给我最简单版本"],
29
+ },
30
+ };
31
+
32
+ // ══════════ 提示词生成 ══════════
33
+
34
+ function buildIterationPrompt(problem, action, extraNote) {
35
+ var note = extraNote && extraNote.trim()
36
+ ? "\n补充说明:" + extraNote.trim()
37
+ : "";
38
+
39
+ if (!problem || !action) return "";
40
+
41
+ // 第二列选了"我还没想好":发散模式
42
+ if (action === "我还没想好") {
43
+ return "你是一个帮助学生继续推进项目的助手。\n"
44
+ + "我现在这个作品的问题是【" + problem + "】,\n"
45
+ + "但我还没想好具体要怎么改。\n\n"
46
+ + "请结合我当前正在做的内容,给我 3 个具体、容易执行的改进方向。\n"
47
+ + "要求:\n"
48
+ + "- 每条都要很短\n"
49
+ + "- 每条都要能马上动手\n"
50
+ + "- 不要太复杂\n"
51
+ + "- 最后帮我选 1 个最容易成功的方向\n\n"
52
+ + "输出格式:\n"
53
+ + "方向1:...\n"
54
+ + "方向2:...\n"
55
+ + "方向3:...\n"
56
+ + "建议我先做:..." + note;
57
+ }
58
+
59
+ // 逻辑类
60
+ if (problem === "逻辑不对") {
61
+ return "你是一个帮助学生继续推进项目的助手。\n"
62
+ + "我现在这个作品的问题是【" + problem + "】。\n"
63
+ + "我下一步想先【" + action + "】。\n\n"
64
+ + "请根据我当前正在做的内容,只帮我解决一个最小的问题。\n"
65
+ + "要求:\n"
66
+ + "- 只改一个点\n"
67
+ + "- 优先选择最容易成功的做法\n"
68
+ + "- 不要重构,不要大改\n"
69
+ + "- 如果你认为现在应该先缩小范围,也请直接告诉我最小版本应该怎么做\n\n"
70
+ + "输出格式:\n"
71
+ + "先改什么:...\n"
72
+ + "为什么先改这个:...\n"
73
+ + "我可以直接照着做的一句话:..." + note;
74
+ }
75
+
76
+ // 样子类
77
+ if (problem === "样子不对") {
78
+ return "你是一个帮助学生继续推进项目的助手。\n"
79
+ + "我现在这个作品的问题是【" + problem + "】。\n"
80
+ + "我下一步想先【" + action + "】。\n\n"
81
+ + "请根据我当前正在做的内容,只帮我做一个最小的视觉改进。\n"
82
+ + "要求:\n"
83
+ + "- 优先改样式、布局、文案、配色、画面感觉\n"
84
+ + "- 不要顺便改很多功能\n"
85
+ + "- 只给我一个最容易成功的下一步\n\n"
86
+ + "输出格式:\n"
87
+ + "先改什么:...\n"
88
+ + "为什么先改这个:...\n"
89
+ + "我可以直接发给 AI 的一句话:..." + note;
90
+ }
91
+
92
+ // 好玩类
93
+ if (problem === "不够好玩") {
94
+ return "你是一个帮助学生继续推进项目的助手。\n"
95
+ + "我现在这个作品的问题是【" + problem + "】。\n"
96
+ + "我下一步想先【" + action + "】。\n\n"
97
+ + "请根据我当前正在做的内容,只帮我增加一个最小的有趣点。\n"
98
+ + "要求:\n"
99
+ + "- 只加一个\n"
100
+ + "- 要容易实现\n"
101
+ + "- 不要把项目复杂度一下拉高\n"
102
+ + "- 优先选择能马上看到效果的改动\n\n"
103
+ + "输出格式:\n"
104
+ + "先加什么:...\n"
105
+ + "为什么先加这个:...\n"
106
+ + "我可以直接照着做的一句话:..." + note;
107
+ }
108
+
109
+ // 创意类
110
+ if (problem === "缺少创意玩法") {
111
+ return "你是一个帮助学生继续推进项目的助手。\n"
112
+ + "我现在这个作品的问题是【" + problem + "】。\n"
113
+ + "我下一步想先【" + action + "】。\n\n"
114
+ + "请根据我当前正在做的内容,帮我加一个最有创意、又最容易实现的新点子。\n"
115
+ + "要求:\n"
116
+ + "- 只加一个\n"
117
+ + "- 要让人眼前一亮\n"
118
+ + "- 不要做太复杂\n"
119
+ + "- 优先选择不需要大改就能看到效果的\n\n"
120
+ + "输出格式:\n"
121
+ + "先加什么:...\n"
122
+ + "为什么先加这个:...\n"
123
+ + "我可以直接照着做的一句话:..." + note;
124
+ }
125
+
126
+ // 卡住类
127
+ if (problem === "救命我卡住了") {
128
+ return "你是一个帮助学生继续推进项目的助手。\n"
129
+ + "学生现在卡住了,下一步想【" + action + "】。\n\n"
130
+ + "请根据当前项目内容,用最温和、最清楚的方式帮他继续推进。\n"
131
+ + "要求:\n"
132
+ + "- 不要给太多任务\n"
133
+ + "- 不要让他大改\n"
134
+ + "- 只给一个最小、最容易成功的下一步\n"
135
+ + "- 用孩子也能看懂的话说\n\n"
136
+ + "输出格式:\n"
137
+ + "我现在最该先做:...\n"
138
+ + "为什么:...\n"
139
+ + "我现在就可以开始做的一句话:..." + note;
140
+ }
141
+
142
+ // 兜底
143
+ return "你是一个帮助学生继续推进项目的助手。\n"
144
+ + "我现在这个作品的问题是【" + problem + "】。\n"
145
+ + "我下一步想先【" + action + "】。\n\n"
146
+ + "请根据我当前正在做的内容,帮我只改一个点,\n"
147
+ + "给我一个最简单、最容易成功的下一步方案。\n\n"
148
+ + "要求:\n"
149
+ + "- 只给一个方向\n"
150
+ + "- 不要太复杂\n"
151
+ + "- 不要大改\n"
152
+ + "- 要让我马上能动手\n\n"
153
+ + "输出格式:\n"
154
+ + "先改什么:...\n"
155
+ + "为什么先改这个:...\n"
156
+ + "我可以直接照着做的一句话:..." + note;
157
+ }
158
+
159
+ // ══════════ 状态 ══════════
160
+
161
+ var promptOpen = false;
162
+ var modalVoiceDestroy = null;
163
+ var _trySend = null; // 由 myclaw-inject.js 注入
164
+
165
+ // ══════════ 工具组件 ══════════
166
+
167
+ function makeLabel(text) {
168
+ var el = document.createElement("div");
169
+ el.textContent = text;
170
+ el.style.cssText = "font-size:13px;font-weight:bold;color:#cdd6f4;margin-bottom:8px;font-family:-apple-system,sans-serif;";
171
+ return el;
172
+ }
173
+
174
+ function makeChipGroup(items, activeColor, onChange) {
175
+ var wrap = document.createElement("div");
176
+ wrap.style.cssText = "display:flex;flex-wrap:wrap;gap:7px;";
177
+ var chips = [];
178
+ items.forEach(function (label) {
179
+ var chip = document.createElement("button");
180
+ chip.textContent = label;
181
+ chip.dataset.selected = "0";
182
+ chip.style.cssText = [
183
+ "padding:6px 13px",
184
+ "border-radius:20px",
185
+ "border:1.5px solid #3d3d5c",
186
+ "background:#252536",
187
+ "color:#888",
188
+ "font-size:13px",
189
+ "font-family:-apple-system,sans-serif",
190
+ "cursor:pointer",
191
+ "transition:all 0.15s",
192
+ "white-space:nowrap",
193
+ ].join(";");
194
+
195
+ function applyStyle(sel) {
196
+ if (sel) {
197
+ chip.style.background = activeColor;
198
+ chip.style.borderColor = activeColor;
199
+ chip.style.color = "#fff";
200
+ chip.style.fontWeight = "bold";
201
+ } else {
202
+ chip.style.background = "#252536";
203
+ chip.style.borderColor = "#3d3d5c";
204
+ chip.style.color = "#888";
205
+ chip.style.fontWeight = "normal";
206
+ }
207
+ }
208
+
209
+ chip.onmouseenter = function () {
210
+ if (chip.dataset.selected !== "1") chip.style.borderColor = activeColor;
211
+ };
212
+ chip.onmouseleave = function () {
213
+ if (chip.dataset.selected !== "1") chip.style.borderColor = "#3d3d5c";
214
+ };
215
+ chip.onclick = function () {
216
+ var nowOn = chip.dataset.selected !== "1";
217
+ chips.forEach(function (c) {
218
+ if (c !== chip) {
219
+ c.style.background = "#252536";
220
+ c.style.borderColor = "#3d3d5c";
221
+ c.style.color = "#888";
222
+ c.style.fontWeight = "normal";
223
+ c.dataset.selected = "0";
224
+ }
225
+ });
226
+ chip.dataset.selected = nowOn ? "1" : "0";
227
+ applyStyle(nowOn);
228
+ onChange(nowOn ? label : "");
229
+ };
230
+
231
+ chips.push(chip);
232
+ wrap.appendChild(chip);
233
+ });
234
+ return wrap;
235
+ }
236
+
237
+ function makeVoiceInputField(placeholder, maxLength, onChange) {
238
+ var lVoice = null;
239
+ var lRecording = false;
240
+ var lStopping = false;
241
+
242
+ var wrap = document.createElement("div");
243
+ wrap.style.cssText = "display:flex;gap:8px;align-items:center;width:100%;box-sizing:border-box;";
244
+
245
+ var input = document.createElement("input");
246
+ input.type = "text";
247
+ input.maxLength = maxLength || 60;
248
+ input.placeholder = placeholder || "";
249
+ input.style.cssText = [
250
+ "flex:1", "min-width:0", "padding:9px 12px",
251
+ "background:#252536",
252
+ "border:1.5px solid #3d3d5c",
253
+ "border-radius:8px",
254
+ "color:#cdd6f4",
255
+ "font-size:13px",
256
+ "font-family:-apple-system,sans-serif",
257
+ "outline:none",
258
+ "transition:border-color 0.15s, border-left-width 0.1s",
259
+ ].join(";");
260
+ input.onfocus = function () { if (!lRecording) input.style.borderColor = "#6366f1"; };
261
+ input.onblur = function () { if (!lRecording) input.style.borderColor = "#3d3d5c"; };
262
+ input.oninput = function () { if (onChange) onChange(input.value); };
263
+
264
+ var micBtn = document.createElement("button");
265
+ micBtn.type = "button";
266
+ micBtn.title = "语音输入";
267
+ micBtn.style.cssText = [
268
+ "width:34px", "height:34px", "flex-shrink:0",
269
+ "border-radius:50%",
270
+ "border:1.5px solid #3d3d5c",
271
+ "background:#252536",
272
+ "color:#888",
273
+ "cursor:pointer",
274
+ "display:flex", "align-items:center", "justify-content:center",
275
+ "transition:all 0.15s",
276
+ "position:relative",
277
+ ].join(";");
278
+ micBtn.innerHTML = [
279
+ '<svg width="14" height="14" viewBox="0 0 24 24" fill="none"',
280
+ ' stroke="currentColor" stroke-width="2"',
281
+ ' stroke-linecap="round" stroke-linejoin="round">',
282
+ ' <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/>',
283
+ ' <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>',
284
+ ' <line x1="12" x2="12" y1="19" y2="22"/>',
285
+ '</svg>',
286
+ ].join("");
287
+
288
+ function setUI(recording) {
289
+ if (recording) {
290
+ micBtn.style.background = "#ff4444";
291
+ micBtn.style.borderColor = "#ff4444";
292
+ micBtn.style.color = "#fff";
293
+ micBtn.title = "停止录音";
294
+ input.style.borderColor = "#ff4444";
295
+ input.style.borderLeftWidth = "3px";
296
+ } else {
297
+ micBtn.style.background = "#252536";
298
+ micBtn.style.borderColor = "#3d3d5c";
299
+ micBtn.style.color = "#888";
300
+ micBtn.title = "语音输入";
301
+ input.style.borderColor = "#3d3d5c";
302
+ input.style.borderLeftWidth = "1.5px";
303
+ }
304
+ }
305
+
306
+ function initLVoice() {
307
+ if (typeof window.VoiceInput === "undefined") return false;
308
+ lVoice = new window.VoiceInput({
309
+ onResult: function (text) {
310
+ if (!lRecording && !lStopping) return;
311
+ input.value = text;
312
+ if (onChange) onChange(text);
313
+ },
314
+ onStatusChange: function (oldS, newS) {
315
+ if (newS === "idle" && lRecording && !lStopping) {
316
+ setTimeout(function () {
317
+ if (lRecording && lVoice) lVoice.start();
318
+ }, 300);
319
+ }
320
+ },
321
+ onError: function (err) {
322
+ console.error("[myclaw-iteration] voice error:", err);
323
+ lRecording = false; lStopping = false;
324
+ setUI(false);
325
+ },
326
+ });
327
+ return true;
328
+ }
329
+
330
+ function startLocal() {
331
+ if (!lVoice && !initLVoice()) {
332
+ console.warn("[myclaw-iteration] VoiceInput SDK 未加载");
333
+ return;
334
+ }
335
+ lRecording = true;
336
+ setUI(true);
337
+ lVoice.start();
338
+ }
339
+
340
+ function stopLocal() {
341
+ if (!lRecording) return;
342
+ lStopping = true;
343
+ lRecording = false;
344
+ setUI(false);
345
+ setTimeout(function () {
346
+ lStopping = false;
347
+ if (lVoice) lVoice.stop();
348
+ }, 1500);
349
+ }
350
+
351
+ micBtn.onclick = function () {
352
+ if (lRecording) { stopLocal(); } else { startLocal(); }
353
+ };
354
+
355
+ wrap.appendChild(input);
356
+ wrap.appendChild(micBtn);
357
+
358
+ return {
359
+ el: wrap,
360
+ getValue: function () { return input.value; },
361
+ setValue: function (v) { input.value = v; if (onChange) onChange(v); },
362
+ destroy: function () { if (lRecording) stopLocal(); },
363
+ };
364
+ }
365
+
366
+ function insertPromptText(text) {
367
+ var ta = document.querySelector(".agent-chat__input textarea");
368
+ if (!ta) return;
369
+
370
+ var start = ta.selectionStart || 0;
371
+ var end = ta.selectionEnd || 0;
372
+ var current = ta.value;
373
+ var newValue = current.substring(0, start) + text + current.substring(end);
374
+
375
+ var setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value").set;
376
+ setter.call(ta, newValue);
377
+ ta.dispatchEvent(new Event("input", { bubbles: true }));
378
+
379
+ var newCursor = start + text.length;
380
+ try { ta.setSelectionRange(newCursor, newCursor); } catch (e) {}
381
+ ta.focus();
382
+ }
383
+
384
+ // ══════════ Modal ══════════
385
+
386
+ function openPromptModal() {
387
+ if (document.querySelector("#myclaw-prompt-modal")) return;
388
+ promptOpen = true;
389
+
390
+ var iconBtn = document.querySelector("#myclaw-prompt-btn");
391
+ if (iconBtn) iconBtn.style.color = "#6366f1";
392
+
393
+ var state = { problem: "", action: "", note: "" };
394
+
395
+ function buildText() {
396
+ return buildIterationPrompt(state.problem, state.action, state.note);
397
+ }
398
+
399
+ function buildPayload() {
400
+ return {
401
+ type: "iteration_helper",
402
+ problem: state.problem,
403
+ next_action: state.action,
404
+ extra_note: state.note,
405
+ generated_text: buildText(),
406
+ };
407
+ }
408
+
409
+ var previewEl, actionSendBtn, actionInsertBtn;
410
+ function refresh() {
411
+ var text = buildText();
412
+ var ready = !!(state.problem && state.action);
413
+ if (previewEl) {
414
+ previewEl.textContent = text || "先选上面两项,这里会自动生成要发给 AI 的内容 ✨";
415
+ previewEl.style.color = ready ? "#cdd6f4" : "#555";
416
+ }
417
+ if (actionSendBtn) {
418
+ actionSendBtn.disabled = !ready;
419
+ actionSendBtn.style.opacity = ready ? "1" : "0.4";
420
+ actionSendBtn.style.cursor = ready ? "pointer" : "not-allowed";
421
+ }
422
+ if (actionInsertBtn) {
423
+ actionInsertBtn.disabled = !ready;
424
+ actionInsertBtn.style.opacity = ready ? "1" : "0.4";
425
+ actionInsertBtn.style.cursor = ready ? "pointer" : "not-allowed";
426
+ }
427
+ }
428
+
429
+ // ── 遮罩 ──
430
+ var overlay = document.createElement("div");
431
+ overlay.id = "myclaw-prompt-modal";
432
+ overlay.style.cssText = [
433
+ "position:fixed", "top:0", "left:0",
434
+ "width:100vw", "height:100vh",
435
+ "background:rgba(0,0,0,0.45)",
436
+ "z-index:99998",
437
+ "display:flex", "align-items:center", "justify-content:center",
438
+ "animation:myclaw-fade-in 0.15s ease",
439
+ ].join(";");
440
+
441
+ // ── 弹框 ──
442
+ var box = document.createElement("div");
443
+ box.style.cssText = [
444
+ "width:560px", "max-width:96vw", "max-height:92vh",
445
+ "background:#1e1e2e",
446
+ "border-radius:12px",
447
+ "overflow:hidden",
448
+ "display:flex", "flex-direction:column",
449
+ "box-shadow:0 12px 48px rgba(0,0,0,0.6)",
450
+ ].join(";");
451
+
452
+ // ── 标题栏 ──
453
+ var header = document.createElement("div");
454
+ header.style.cssText = [
455
+ "display:flex", "align-items:center", "justify-content:space-between",
456
+ "padding:13px 18px",
457
+ "background:linear-gradient(135deg,#6366f1,#8b5cf6)",
458
+ "color:#fff",
459
+ "font-size:15px", "font-weight:bold",
460
+ "font-family:-apple-system,sans-serif",
461
+ "user-select:none", "flex-shrink:0",
462
+ ].join(";");
463
+ header.innerHTML = "<span>🤔 我卡住了,帮我继续改</span>";
464
+ var closeX = document.createElement("span");
465
+ closeX.textContent = "✕";
466
+ closeX.style.cssText = "cursor:pointer;padding:2px 8px;border-radius:4px;font-size:15px;transition:background 0.15s;";
467
+ closeX.onmouseenter = function () { closeX.style.background = "rgba(255,255,255,0.2)"; };
468
+ closeX.onmouseleave = function () { closeX.style.background = "none"; };
469
+ closeX.onclick = function () { closePromptModal(); };
470
+ header.appendChild(closeX);
471
+
472
+ // ── 内容区 ──
473
+ var scroll = document.createElement("div");
474
+ scroll.style.cssText = "flex:1;overflow-y:auto;padding:20px 20px 8px;display:flex;flex-direction:column;gap:20px;";
475
+
476
+ // 第1区:问题
477
+ var sec1 = document.createElement("div");
478
+ sec1.appendChild(makeLabel("只改一点选什么?"));
479
+ sec1.appendChild(makeChipGroup(ITERATION_CONFIG.problems, "#f59e0b", function (val) {
480
+ state.problem = val;
481
+ state.action = "";
482
+ renderActionChips();
483
+ refresh();
484
+ }));
485
+ scroll.appendChild(sec1);
486
+
487
+ // 第2区:行动(动态联动)
488
+ var sec2 = document.createElement("div");
489
+ sec2.appendChild(makeLabel("你想怎么改?"));
490
+ var actionChipsWrap = document.createElement("div");
491
+ sec2.appendChild(actionChipsWrap);
492
+ sec2.style.display = "none";
493
+ scroll.appendChild(sec2);
494
+
495
+ function renderActionChips() {
496
+ actionChipsWrap.innerHTML = "";
497
+ var actions = ITERATION_CONFIG.actionsByProblem[state.problem];
498
+ if (!actions) { sec2.style.display = "none"; return; }
499
+ sec2.style.display = "";
500
+ actionChipsWrap.appendChild(makeChipGroup(actions, "#6366f1", function (val) {
501
+ state.action = val; refresh();
502
+ }));
503
+ }
504
+
505
+ // 第3区:补充
506
+ var sec3 = document.createElement("div");
507
+ sec3.appendChild(makeLabel("你的补充很关键!"));
508
+ var placeholders = ["比如:我想做成飞行射击", "比如:我想更像小猫主题", "比如:按钮点了以后想有变化"];
509
+ var noteField = makeVoiceInputField(
510
+ placeholders[Math.floor(Math.random() * placeholders.length)],
511
+ 60,
512
+ function (val) { state.note = val; refresh(); }
513
+ );
514
+ modalVoiceDestroy = noteField.destroy;
515
+ sec3.appendChild(noteField.el);
516
+ scroll.appendChild(sec3);
517
+
518
+ // 预览区
519
+ var previewWrap = document.createElement("div");
520
+ previewWrap.style.cssText = [
521
+ "padding:12px 14px",
522
+ "background:#252536",
523
+ "border-radius:8px",
524
+ "border-left:3px solid #6366f1",
525
+ ].join(";");
526
+ var previewLabel = document.createElement("div");
527
+ previewLabel.textContent = "将要发给 AI 的内容:";
528
+ previewLabel.style.cssText = "font-size:11px;color:#6366f1;font-weight:bold;margin-bottom:6px;font-family:monospace;letter-spacing:0.3px;";
529
+ previewEl = document.createElement("div");
530
+ previewEl.style.cssText = "font-size:13px;line-height:1.7;white-space:pre-wrap;font-family:-apple-system,sans-serif;word-break:break-all;";
531
+ previewWrap.appendChild(previewLabel);
532
+ previewWrap.appendChild(previewEl);
533
+ scroll.appendChild(previewWrap);
534
+
535
+ // ── 底部按钮 ──
536
+ var footer = document.createElement("div");
537
+ footer.style.cssText = [
538
+ "display:flex", "gap:10px",
539
+ "padding:14px 20px",
540
+ "border-top:1px solid #2d2d3f",
541
+ "flex-shrink:0",
542
+ "background:#1e1e2e",
543
+ ].join(";");
544
+
545
+ actionSendBtn = document.createElement("button");
546
+ actionSendBtn.textContent = "➤ 直接发给 AI";
547
+ actionSendBtn.disabled = true;
548
+ actionSendBtn.style.cssText = [
549
+ "flex:1", "padding:11px 0",
550
+ "background:#10b981", "color:#fff",
551
+ "border:none", "border-radius:8px",
552
+ "font-size:14px", "font-weight:bold",
553
+ "font-family:-apple-system,sans-serif",
554
+ "cursor:not-allowed", "transition:all 0.15s", "opacity:0.4",
555
+ ].join(";");
556
+ actionSendBtn.onmouseenter = function () { if (!actionSendBtn.disabled) actionSendBtn.style.background = "#059669"; };
557
+ actionSendBtn.onmouseleave = function () { if (!actionSendBtn.disabled) actionSendBtn.style.background = "#10b981"; };
558
+ actionSendBtn.onclick = function () {
559
+ if (actionSendBtn.disabled) return;
560
+ var payload = buildPayload();
561
+ console.log("[myclaw-iteration] onSendDirect", payload);
562
+ insertPromptText(payload.generated_text);
563
+ closePromptModal();
564
+ setTimeout(function () { if (_trySend) _trySend(); }, 50);
565
+ };
566
+
567
+ actionInsertBtn = document.createElement("button");
568
+ actionInsertBtn.textContent = "⬇ 先放到聊天框";
569
+ actionInsertBtn.disabled = true;
570
+ actionInsertBtn.style.cssText = [
571
+ "flex:1", "padding:11px 0",
572
+ "background:#6366f1", "color:#fff",
573
+ "border:none", "border-radius:8px",
574
+ "font-size:14px", "font-weight:bold",
575
+ "font-family:-apple-system,sans-serif",
576
+ "cursor:not-allowed", "transition:all 0.15s", "opacity:0.4",
577
+ ].join(";");
578
+ actionInsertBtn.onmouseenter = function () { if (!actionInsertBtn.disabled) actionInsertBtn.style.background = "#4f46e5"; };
579
+ actionInsertBtn.onmouseleave = function () { if (!actionInsertBtn.disabled) actionInsertBtn.style.background = "#6366f1"; };
580
+ actionInsertBtn.onclick = function () {
581
+ if (actionInsertBtn.disabled) return;
582
+ var payload = buildPayload();
583
+ console.log("[myclaw-iteration] onInsertToInput", payload);
584
+ insertPromptText(payload.generated_text);
585
+ closePromptModal();
586
+ };
587
+
588
+ footer.appendChild(actionSendBtn);
589
+ footer.appendChild(actionInsertBtn);
590
+
591
+ box.appendChild(header);
592
+ box.appendChild(scroll);
593
+ box.appendChild(footer);
594
+ overlay.appendChild(box);
595
+ overlay.onclick = function (e) { if (e.target === overlay) closePromptModal(); };
596
+ document.body.appendChild(overlay);
597
+
598
+ refresh();
599
+ }
600
+
601
+ function closePromptModal() {
602
+ if (modalVoiceDestroy) { modalVoiceDestroy(); modalVoiceDestroy = null; }
603
+ var modal = document.querySelector("#myclaw-prompt-modal");
604
+ if (modal) modal.remove();
605
+ promptOpen = false;
606
+
607
+ var btn = document.querySelector("#myclaw-prompt-btn");
608
+ if (btn) btn.style.color = "";
609
+ }
610
+
611
+ // ══════════ 触发按钮 ══════════
612
+
613
+ function createPromptButton() {
614
+ var btn = document.createElement("button");
615
+ btn.id = "myclaw-prompt-btn";
616
+ btn.className = "agent-chat__input-btn";
617
+ btn.title = "我卡住了,帮我继续改";
618
+ btn.setAttribute("aria-label", "我卡住了,帮我继续改");
619
+ btn.innerHTML = [
620
+ '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"',
621
+ ' viewBox="0 0 24 24" fill="none" stroke="currentColor"',
622
+ ' stroke-width="2" stroke-linecap="round" stroke-linejoin="round">',
623
+ ' <circle cx="12" cy="12" r="10"/>',
624
+ ' <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>',
625
+ ' <line x1="12" y1="17" x2="12.01" y2="17"/>',
626
+ '</svg>',
627
+ ].join("");
628
+
629
+ btn.addEventListener("click", function (e) {
630
+ e.stopPropagation();
631
+ if (promptOpen) {
632
+ closePromptModal();
633
+ } else {
634
+ openPromptModal();
635
+ }
636
+ });
637
+
638
+ return btn;
639
+ }
640
+
641
+ // ══════════ 对外接口 ══════════
642
+
643
+ window.myclawIteration = {
644
+ init: function (deps) {
645
+ if (deps && deps.trySend) _trySend = deps.trySend;
646
+ },
647
+ createButton: createPromptButton,
648
+ open: openPromptModal,
649
+ close: closePromptModal,
650
+ isOpen: function () { return promptOpen; },
651
+ };
652
+ })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.1.76",
3
+ "version": "1.1.77",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
package/patches/patch.js CHANGED
@@ -27,6 +27,7 @@ const VOICE_SDK_FILENAME = 'voice-input.js';
27
27
  const VOICE_OUTPUT_SDK_FILENAME = 'voice-output.js';
28
28
  const TTS_INJECT_FILENAME = 'myclaw-tts.js';
29
29
  const ARTIFACTS_INJECT_FILENAME = 'myclaw-artifacts.js';
30
+ const ITERATION_INJECT_FILENAME = 'myclaw-iteration.js';
30
31
  const BACKUP_SUFFIX = '.myclaw-backup';
31
32
 
32
33
  /**
@@ -118,6 +119,8 @@ function patch() {
118
119
  const ttsInjectDest = path.join(uiDir, TTS_INJECT_FILENAME);
119
120
  const artifactsInjectSrc = path.join(__dirname, '..', 'assets', ARTIFACTS_INJECT_FILENAME);
120
121
  const artifactsInjectDest = path.join(uiDir, ARTIFACTS_INJECT_FILENAME);
122
+ const iterationInjectSrc = path.join(__dirname, '..', 'assets', ITERATION_INJECT_FILENAME);
123
+ const iterationInjectDest = path.join(uiDir, ITERATION_INJECT_FILENAME);
121
124
  const version = getMyclawVersion();
122
125
 
123
126
  console.log('[myclaw-patch] → control-ui: ' + uiDir);
@@ -177,6 +180,14 @@ function patch() {
177
180
  console.error('[myclaw-patch] ⚠ Artifacts 注入脚本复制失败 (非致命): ' + err.message);
178
181
  }
179
182
 
183
+ // 5.6 复制 Iteration 注入脚本
184
+ try {
185
+ fs.copyFileSync(iterationInjectSrc, iterationInjectDest);
186
+ console.log('[myclaw-patch] ✅ Iteration 注入脚本已复制');
187
+ } catch (err) {
188
+ console.error('[myclaw-patch] ⚠ Iteration 注入脚本复制失败 (非致命): ' + err.message);
189
+ }
190
+
180
191
  // 6. Patch index.html(幂等)
181
192
  try {
182
193
  let html = fs.readFileSync(indexPath, 'utf8');
@@ -195,6 +206,7 @@ function patch() {
195
206
  '<script src="./' + VOICE_SDK_FILENAME + '"></script>',
196
207
  '<script src="./' + TTS_INJECT_FILENAME + '"></script>',
197
208
  '<script src="./' + ARTIFACTS_INJECT_FILENAME + '"></script>',
209
+ '<script src="./' + ITERATION_INJECT_FILENAME + '"></script>',
198
210
  '<script src="./' + INJECT_FILENAME + '"></script>',
199
211
  '',
200
212
  ].join('\n');
@@ -319,6 +331,7 @@ function unpatch() {
319
331
  const voiceOutputSdkDest = path.join(uiDir, VOICE_OUTPUT_SDK_FILENAME);
320
332
  const ttsInjectDest = path.join(uiDir, TTS_INJECT_FILENAME);
321
333
  const artifactsInjectDest = path.join(uiDir, ARTIFACTS_INJECT_FILENAME);
334
+ const iterationInjectDest = path.join(uiDir, ITERATION_INJECT_FILENAME);
322
335
 
323
336
  // 恢复备份
324
337
  if (fs.existsSync(backupPath)) {
@@ -357,6 +370,12 @@ function unpatch() {
357
370
  console.log('[myclaw-patch] ✅ Artifacts 注入脚本已删除');
358
371
  }
359
372
 
373
+ // 删除 Iteration 注入脚本
374
+ if (fs.existsSync(iterationInjectDest)) {
375
+ fs.unlinkSync(iterationInjectDest);
376
+ console.log('[myclaw-patch] ✅ Iteration 注入脚本已删除');
377
+ }
378
+
360
379
  // 恢复 gateway-cli-*.js 的 Permissions-Policy
361
380
  try {
362
381
  const distParent = path.resolve(uiDir, '..');
@@ -1,5 +1,5 @@
1
1
  {
2
- "index_updated_at": "2026-04-22T17:22:47Z",
2
+ "index_updated_at": "2026-04-23T09:17:55Z",
3
3
  "templates": {
4
4
  "696de592": {
5
5
  "id": "696de592",
@@ -134,6 +134,59 @@
134
134
  ],
135
135
  "updated_at": "2026-04-22T17:22:44Z"
136
136
  },
137
+ "5d4e392a": {
138
+ "id": "5d4e392a",
139
+ "系列": "A",
140
+ "编号": "105",
141
+ "名称": "给喜欢的主题做一张 AI 画像",
142
+ "文件夹名": "a105_给喜欢的主题做一张_AI_画像",
143
+ "version": "v1",
144
+ "路径": "templates/a105_给喜欢的主题做一张_AI_画像/v1",
145
+ "入口文件": "templates/a105_给喜欢的主题做一张_AI_画像/v1/index.html",
146
+ "一句话说明": "先让 AI 生成一张画,再改一改,最后把它放到网页上。",
147
+ "主能力标签": "第二能力:表达构建",
148
+ "任务类型标签": "视听表达",
149
+ "关键词": [
150
+ "a105_给喜欢的主题做一张_AI_画像",
151
+ "先让 AI 生成一张画,再改一改,最后把它放到网页上。",
152
+ "刚接触 AI 生成的学生",
153
+ "不知道怎么把 AI 结果放进网页的学生",
154
+ "想练习「说清楚想要什么」这个能力的学生",
155
+ "练习用自然语言描述自己想看到的画面",
156
+ "学会提出一个具体的小修改点,而不是笼统地说「不好看」",
157
+ "初步理解 AI 生成和网页发布是两个不同的步骤",
158
+ "给喜欢的主题做一张 AI 画像",
159
+ "第二能力:表达构建",
160
+ "视听表达"
161
+ ],
162
+ "updated_at": "2026-04-23T08:36:43Z"
163
+ },
164
+ "ad24a76c": {
165
+ "id": "ad24a76c",
166
+ "系列": "A",
167
+ "编号": "106",
168
+ "名称": "火箭拦截:发现规则 & 提出改进",
169
+ "文件夹名": "a106_火箭拦截:发现规则_&_提出改进",
170
+ "version": "v1",
171
+ "路径": "templates/a106_火箭拦截:发现规则_&_提出改进/v1",
172
+ "入口文件": "templates/a106_火箭拦截:发现规则_&_提出改进/v1/index.html",
173
+ "一句话说明": "先玩10秒找到游戏规则,再提出一个改进点,最后验证改进有没有用。",
174
+ "主能力标签": "第三能力:问题判断",
175
+ "任务类型标签": "游戏机制",
176
+ "关键词": [
177
+ "a106_火箭拦截:发现规则_&_提出改进",
178
+ "先玩10秒找到游戏规则,再提出一个改进点,最后验证改进有没有用。",
179
+ "初次接触游戏设计的学生",
180
+ "会玩游戏但没分析过游戏规则的学生",
181
+ "通过10秒时间盒,训练学生快速聚焦核心规则",
182
+ "用「前10秒」作为判断标准,培养学生自我感受的游戏评价能力",
183
+ "三关结构:发现→提出→验证,形成完整的「判断-改进-验证」闭环",
184
+ "火箭拦截:发现规则 & 提出改进",
185
+ "第三能力:问题判断",
186
+ "游戏机制"
187
+ ],
188
+ "updated_at": "2026-04-23T09:17:54Z"
189
+ },
137
190
  "c360e192": {
138
191
  "id": "c360e192",
139
192
  "系列": "B",
@@ -47,6 +47,24 @@
47
47
  - 任务类型标签:游戏机制
48
48
  - 关键词:a104_做一个会出招的神奇按钮, 先试试按钮效果,再改一点,最后加一个你自己的新按钮。, 想先动手再思考的学生, 刚开始学网页互动的学生, 容易卡在空白页不知从哪开始的学生, 先运行现成示例,快速进入任务, 从已有功能里挑一个做小改动, 理解一个按钮对应一个功能, 把新增功能接入现有页面结构, 做一个会出招的神奇按钮, 迭代推进, 游戏机制
49
49
 
50
+ ## A105 给喜欢的主题做一张 AI 画像
51
+ - ID:5d4e392a
52
+ - 版本:v1
53
+ - 路径:templates/a105_给喜欢的主题做一张_AI_画像/v1
54
+ - 入口文件:templates/a105_给喜欢的主题做一张_AI_画像/v1/index.html
55
+ - 主能力标签:第二能力:表达构建
56
+ - 任务类型标签:视听表达
57
+ - 关键词:a105_给喜欢的主题做一张_AI_画像, 先让 AI 生成一张画,再改一改,最后把它放到网页上。, 刚接触 AI 生成的学生, 不知道怎么把 AI 结果放进网页的学生, 想练习「说清楚想要什么」这个能力的学生, 练习用自然语言描述自己想看到的画面, 学会提出一个具体的小修改点,而不是笼统地说「不好看」, 初步理解 AI 生成和网页发布是两个不同的步骤, 给喜欢的主题做一张 AI 画像, 第二能力:表达构建, 视听表达
58
+
59
+ ## A106 火箭拦截:发现规则 & 提出改进
60
+ - ID:ad24a76c
61
+ - 版本:v1
62
+ - 路径:templates/a106_火箭拦截:发现规则_&_提出改进/v1
63
+ - 入口文件:templates/a106_火箭拦截:发现规则_&_提出改进/v1/index.html
64
+ - 主能力标签:第三能力:问题判断
65
+ - 任务类型标签:游戏机制
66
+ - 关键词:a106_火箭拦截:发现规则_&_提出改进, 先玩10秒找到游戏规则,再提出一个改进点,最后验证改进有没有用。, 初次接触游戏设计的学生, 会玩游戏但没分析过游戏规则的学生, 通过10秒时间盒,训练学生快速聚焦核心规则, 用「前10秒」作为判断标准,培养学生自我感受的游戏评价能力, 三关结构:发现→提出→验证,形成完整的「判断-改进-验证」闭环, 火箭拦截:发现规则 & 提出改进, 第三能力:问题判断, 游戏机制
67
+
50
68
  ## B100 做一个按钮页面
51
69
  - ID:c360e192
52
70
  - 版本:v2