magic-agent 2.1.5 → 2.3.0
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/CLIENT-CONTRACT.md +103 -6
- package/client-ui/pc/app.css +3 -0
- package/client-ui/pc/index.html +5 -0
- package/client-ui/pc/js/05-task-list-render.js +28 -4
- package/client-ui/pc/js/07-chat-stream-render.js +19 -8
- package/client-ui/pc/js/93-rail.js +21 -1
- package/client-ui/shared/conv/03-chat-message.js +43 -0
- package/client-ui/shared/conv/06-ask-answer.js +30 -7
- package/client-ui/shared/conv/09-stream-apply.js +16 -0
- package/client-ui/shared/conv/19-attachments.js +75 -11
- package/client-ui/shared/conv/29-msg-variants.js +56 -3
- package/client-ui/shared/conv/README.md +6 -0
- package/client-ui/shared/conv/conv.css +16 -1
- package/client-ui/shared/conversation.css +42 -4
- package/client-ui/shared/conversation.js +655 -87
- package/client-ui/shared/transport-http.js +16 -0
- package/client-ui-mobile/index.html +401 -553
- package/client-ui-mobile/reqs.css +39 -0
- package/client-ui-mobile/reqs.js +507 -0
- package/cmd/magic-client/main.go +1 -1
- package/internal/cli/ask.go +6 -0
- package/internal/cli/root.go +5 -0
- package/internal/cli/session.go +16 -14
- package/internal/cli/suggest.go +98 -0
- package/internal/cli/suggest_test.go +54 -0
- package/internal/cli/team.go +111 -24
- package/internal/client/service.go +11 -1
- package/internal/client/suggest.go +131 -0
- package/internal/client/suggest_test.go +116 -0
- package/internal/session/delete.go +58 -11
- package/internal/session/delete_test.go +140 -0
- package/internal/session/session.go +25 -0
- package/internal/session/session_mark_test.go +62 -0
- package/internal/suggest/suggest.go +421 -0
- package/internal/suggest/suggest_test.go +218 -0
- package/internal/team/board.go +107 -15
- package/internal/team/execute.go +1 -1
- package/internal/team/team.go +28 -19
- package/internal/team/team_test.go +68 -15
- package/npm/apk/magic-agent-mobile-release.apk +0 -0
- package/npm/apk/version.json +3 -3
- package/npm/dist/darwin-arm64/magic-agent +0 -0
- package/npm/dist/darwin-arm64/magic-client +0 -0
- package/npm/dist/darwin-x64/magic-agent +0 -0
- package/npm/dist/darwin-x64/magic-client +0 -0
- package/npm/dist/linux-arm64/magic-agent +0 -0
- package/npm/dist/linux-arm64/magic-client +0 -0
- package/npm/dist/linux-x64/magic-agent +0 -0
- package/npm/dist/linux-x64/magic-client +0 -0
- package/npm/dist/win32-x64/magic-agent.exe +0 -0
- package/npm/dist/win32-x64/magic-client.exe +0 -0
- package/package.json +5 -2
|
@@ -650,6 +650,23 @@ function qaArtNode(it){
|
|
|
650
650
|
return card;
|
|
651
651
|
}
|
|
652
652
|
|
|
653
|
+
/** 产物路径的**归一化**(判「是不是同一个文件」的唯一口径)。
|
|
654
|
+
*
|
|
655
|
+
* 同一份文件在事件里有三种写法 —— 绝对路径、workspace 相对路径、以及 `t2/cover.png`
|
|
656
|
+
* 这类不带 `./` 的写法;只按字符串比对就会把它们当成三个文件画三张卡
|
|
657
|
+
* (2026-10-22 用户:「子任务产物同一个文件不要重复显示」)。
|
|
658
|
+
* 相对路径按会话 workspace 拼成绝对路径再比对(拼不出来就保留原串),顺手吃掉
|
|
659
|
+
* `./`、重复斜杠与末尾斜杠。 */
|
|
660
|
+
function qaArtCanon(p){
|
|
661
|
+
let s = String(p == null ? "" : p).trim().replace(/\\/g, "/");
|
|
662
|
+
if(!s) return "";
|
|
663
|
+
if(!/^\//.test(s) && !/^[A-Za-z]:\//.test(s)){
|
|
664
|
+
const base = String(qaArtBase() || "").replace(/\/+$/, "");
|
|
665
|
+
if(base) s = base + "/" + s.replace(/^\.\//, "");
|
|
666
|
+
}
|
|
667
|
+
return s.replace(/\/{2,}/g, "/").replace(/\/\.\//g, "/").replace(/\/+$/, "");
|
|
668
|
+
}
|
|
669
|
+
|
|
653
670
|
/** 把挑出的产物路径画成一排卡(**只画真的落在盘上的**)。
|
|
654
671
|
* ⚠️ 存在性问主进程(`desk:path-probe`,一次批量)——渲染层拿不到 fs。
|
|
655
672
|
* 正文里提到的 `app.js`、远端 URL、示例路径都不会被误画成卡。
|
|
@@ -657,15 +674,41 @@ function qaArtNode(it){
|
|
|
657
674
|
* `opts.label`(2026-10-15)= 在这一排卡上面先落一行小标题(「本轮产物」用,见 qaTurnArtsMount);
|
|
658
675
|
* `opts.detached` = 允许画进**不在文档树上**的宿主:后台那一轮的画布被整份摘进 holder,
|
|
659
676
|
* 节点还在、只是不在树上(`isConnected` 为假)—— 收尾时往它末尾补产物正是这种情形。
|
|
677
|
+
* `opts.skipShown:false` = 不做「已经画过的跳过」(默认做,见 qaTaskArtsShown)。
|
|
660
678
|
* 返回画出来的张数(Promise):收尾那条链要**等它画完**再落 HTML 快照(见 qaTurnArtsMount)。 */
|
|
661
679
|
function qaArtMount(host, paths, opts){
|
|
662
680
|
const o = opts || {};
|
|
663
681
|
const api = qaDeskFn("pathProbe");
|
|
664
682
|
if(!host || !api || !Array.isArray(paths) || !paths.length) return Promise.resolve(0);
|
|
665
|
-
const
|
|
683
|
+
const dup = new Set();
|
|
684
|
+
const list = [];
|
|
685
|
+
paths.forEach((p)=>{
|
|
686
|
+
const s = qaArtCanon(p);
|
|
687
|
+
if(!s || dup.has(s)) return;
|
|
688
|
+
dup.add(s);
|
|
689
|
+
list.push(String(p).trim());
|
|
690
|
+
});
|
|
691
|
+
if(!list.length) return Promise.resolve(0);
|
|
666
692
|
return Promise.resolve(api({ base: qaArtBase(), paths: list })).then((r)=>{
|
|
667
693
|
const items = (r && r.ok && Array.isArray(r.items)) ? r.items : [];
|
|
668
|
-
|
|
694
|
+
/* 探针回的是**解析后的绝对路径**(相对路径已按 workspace 展开),它才是最终落盘位置 ——
|
|
695
|
+
归一化去重也以它为准:上面那轮只挡得住「同一批里的重复」,跨两次挂载
|
|
696
|
+
(子任务产物区 vs 正文 / 本轮产物)的重复要靠这里。 */
|
|
697
|
+
const shown = new Set();
|
|
698
|
+
let hit = items.filter((x)=>{
|
|
699
|
+
if(!x || !x.exists || !x.abs) return false;
|
|
700
|
+
const k = qaArtCanon(x.abs);
|
|
701
|
+
if(shown.has(k)) return false;
|
|
702
|
+
shown.add(k);
|
|
703
|
+
return true;
|
|
704
|
+
});
|
|
705
|
+
/* 已经在本条消息的任务列表下方展示过的文件,别在正文 / 本轮产物里再画一遍
|
|
706
|
+
(2026-10-22 用户:「子任务产物同一个文件不要重复显示」)——
|
|
707
|
+
汇总正文末尾常有一份「产物」清单,子任务产物区里也是同一批文件。 */
|
|
708
|
+
const skip = o.skipShown === false ? null : qaTaskArtsShown(host);
|
|
709
|
+
if(skip && skip.size){
|
|
710
|
+
hit = hit.filter((x)=> !skip.has(qaArtCanon(x.abs)));
|
|
711
|
+
}
|
|
669
712
|
if(!hit.length) return 0; // 一个都不存在 → 不画
|
|
670
713
|
if(!o.detached && !host.isConnected) return 0; // 这条消息已经换掉了 → 不画
|
|
671
714
|
if(o.label){
|
|
@@ -695,6 +738,31 @@ function qaArtMount(host, paths, opts){
|
|
|
695
738
|
}).catch(()=> 0); // 探针失败 = 当作「都不是产物」,正文照旧
|
|
696
739
|
}
|
|
697
740
|
|
|
741
|
+
/** 这条应答消息里**已经画出来**的产物(规范化绝对路径集合)。
|
|
742
|
+
*
|
|
743
|
+
* 为什么要一问一答式地与 qaArtMount 合流(2026-10-22 用户:「子任务产物同一个文件不要
|
|
744
|
+
* 重复显示」):同一轮里产物有三条来路 ——
|
|
745
|
+
* ① 子任务产物区(任务列表卡下方,core 的 `arts`,最先画出来);
|
|
746
|
+
* ② 工具的「输出」段(结果正文里挑出来的路径);
|
|
747
|
+
* ③ 收尾的「本轮产物」区 + 正文里的产物卡。
|
|
748
|
+
* 同一份 cover.png 三条来路都会挑到。先画出来的那条留下,后面的跳过 —— 判据取 DOM 上
|
|
749
|
+
* 真实的 `data-path`(快照恢复后照样成立),不另记内存状态。
|
|
750
|
+
* 宿主容器不在树上时(后台那一轮被摘进 holder)退回 `host.closest` 拿不到消息 →
|
|
751
|
+
* 只看 host 自己,宁可少挡一次也不误挡。 */
|
|
752
|
+
function qaTaskArtsShown(host){
|
|
753
|
+
const out = new Set();
|
|
754
|
+
if(!host || !host.closest) return out;
|
|
755
|
+
const msg = (host.classList && host.classList.contains("qa-bd")) ? host : host.closest(".qa-bd");
|
|
756
|
+
const scope = msg || host;
|
|
757
|
+
try{
|
|
758
|
+
scope.querySelectorAll(".qa-task-arts .qa-art[data-path], .qa-arts .qa-art[data-path]").forEach((n)=>{
|
|
759
|
+
const p = String(n.dataset.path || "").trim();
|
|
760
|
+
if(p) out.add(qaArtCanon(p));
|
|
761
|
+
});
|
|
762
|
+
}catch(e){ /* 取不到就当没画过 —— 顶多多画一张,不会漏 */ }
|
|
763
|
+
return out;
|
|
764
|
+
}
|
|
765
|
+
|
|
698
766
|
/* ── 破图收尾(2026-10-20 用户:「是图片不存在吗 不存在的不显示」)────────────
|
|
699
767
|
探针只说「这会儿在盘上」,图却仍可能取不回来:手机连的是另一台/另一个实例、
|
|
700
768
|
快照里存的是换过的旧令牌、文件在渲染后被删。别留一张 alt + 碎图标的空卡:
|
|
@@ -821,15 +889,11 @@ function qaTurnArtsMount(run, h){
|
|
|
821
889
|
const el = h && h.el;
|
|
822
890
|
const host = (el && el.querySelector) ? el.querySelector(".qa-bd") : null;
|
|
823
891
|
if(!host || !paths.length) return Promise.resolve(0);
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
}catch(e){ /* 取不到就当没画过 —— 顶多多画一张,不会漏 */ }
|
|
830
|
-
const want = paths.filter((p)=>{ const b = String(p).split("/").pop(); return !seen.has(p) && !seen.has(b); });
|
|
831
|
-
if(!want.length) return Promise.resolve(0);
|
|
832
|
-
return qaArtMount(host, want, { label: "本轮产物", detached: true });
|
|
892
|
+
/* 「正文里已经画过的跳过」不再在这里判:qaArtMount 统一按规范化绝对路径比对
|
|
893
|
+
DOM 上真实的 `data-path`(含子任务产物区),相对 / 绝对 / 带不带 `./` 三种写法
|
|
894
|
+
都能对上(2026-10-22 用户:「子任务产物同一个文件不要重复显示」)。
|
|
895
|
+
这里只把「本轮产物」这个标题带下去 —— 一张都画不出来时它自己不会出现。 */
|
|
896
|
+
return qaArtMount(host, paths, { label: "本轮产物", detached: true });
|
|
833
897
|
}
|
|
834
898
|
/** 回放 / 续传专用(2026-10-20 方案 A):按**轮**收产物,封口时补挂到这轮消息末尾。
|
|
835
899
|
*
|
|
@@ -349,6 +349,19 @@ function qaClip(s, n){
|
|
|
349
349
|
const max = n || 72;
|
|
350
350
|
return t.length > max ? t.slice(0, max) + "…" : t;
|
|
351
351
|
}
|
|
352
|
+
/** 毫秒 → 任务列表行尾的耗时文案(0 / 非数 → "",不写 "0s" 这种噪音)。
|
|
353
|
+
* 口径与收尾行「总耗时」一致:<1 分钟按秒、<1 小时按分、再往上按小时。 */
|
|
354
|
+
function qaDur(ms){
|
|
355
|
+
const v = Number(ms);
|
|
356
|
+
if(!Number.isFinite(v) || v <= 0) return "";
|
|
357
|
+
if(v < 60000) return Math.round(v / 1000) + "s";
|
|
358
|
+
if(v < 3600000){
|
|
359
|
+
const m = Math.floor(v / 60000), s = Math.round((v % 60000) / 1000);
|
|
360
|
+
return s ? m + "m" + s + "s" : m + "m";
|
|
361
|
+
}
|
|
362
|
+
const h = Math.floor(v / 3600000), m = Math.round((v % 3600000) / 60000);
|
|
363
|
+
return m ? h + "h" + m + "m" : h + "h";
|
|
364
|
+
}
|
|
352
365
|
function qaBaseName(p){
|
|
353
366
|
const t = String(p || "").replace(/\\/g, "/").replace(/\/+$/, "");
|
|
354
367
|
const i = t.lastIndexOf("/");
|
|
@@ -485,8 +498,10 @@ function qaTodoFill(host, rows, opts){
|
|
|
485
498
|
}
|
|
486
499
|
const sum = document.createElement("span");
|
|
487
500
|
sum.className = "qa-todo-sum";
|
|
488
|
-
|
|
489
|
-
|
|
501
|
+
/* 标题栏只报总账(「待办 1/2」),**不再**塞一行「已 41 秒无新输出」:
|
|
502
|
+
2026-10-22 用户截图 —— 标题栏和任务行同时写着同一句秒数,看着像两件事。
|
|
503
|
+
每一行各自的进度就在那一行里(见下方 .qa-todo-st)。 */
|
|
504
|
+
sum.textContent = "待办 " + done + "/" + arr.length;
|
|
490
505
|
hd.appendChild(sum);
|
|
491
506
|
if(o.title){
|
|
492
507
|
/* ⚠️ 开合态只落**类**(.qa-todo-panel 上的 open),不写 data-* / 不写行内样式:
|
|
@@ -515,9 +530,39 @@ function qaTodoFill(host, rows, opts){
|
|
|
515
530
|
else if(t.status === "failed") ico.innerHTML = QA_V_ICONS.alert;
|
|
516
531
|
const txt = document.createElement("span");
|
|
517
532
|
txt.className = "qa-todo-t";
|
|
518
|
-
|
|
533
|
+
/* 标题**永远是标题**(2026-10-22 用户截图:整行只剩「已 41 秒无新输出」,
|
|
534
|
+
任务叫什么完全看不见了 —— 此前这里是 `in_progress && active` 就把 text 换掉)。 */
|
|
535
|
+
txt.textContent = t.text;
|
|
519
536
|
r.appendChild(ico);
|
|
520
537
|
r.appendChild(txt);
|
|
538
|
+
/* 行尾:状态 · 耗时 ·(可选)此刻在干什么 —— 全在一行里,右对齐。
|
|
539
|
+
2026-10-22 用户:「状态重新显示 应该显示子任务标题和状态耗时」。
|
|
540
|
+
团队的每一行(running / 已完成 / 失败 / 跳过)都带状态与耗时;待办清单
|
|
541
|
+
(引擎的 TodoWrite)不带 st,走下面那两档旧形态,形态不变。 */
|
|
542
|
+
const dur = qaDur(t.ms);
|
|
543
|
+
if(t.st){
|
|
544
|
+
const st = document.createElement("span");
|
|
545
|
+
st.className = "qa-todo-st is-" + t.status;
|
|
546
|
+
st.textContent = String(t.st);
|
|
547
|
+
r.appendChild(st);
|
|
548
|
+
if(dur){
|
|
549
|
+
const ms = document.createElement("span");
|
|
550
|
+
ms.className = "qa-todo-dur";
|
|
551
|
+
ms.textContent = dur;
|
|
552
|
+
r.appendChild(ms);
|
|
553
|
+
}
|
|
554
|
+
/* 状态说明(等授权 / 长时间无输出)接在「状态 耗时」后面 —— 它是**次要**信息,
|
|
555
|
+
所以排在最后、字更小。只有真正要用户注意的时候 core 才会给 active
|
|
556
|
+
(等授权 / 静默超 30 秒),平时这一行就只有「标题 … 运行中 41s」。 */
|
|
557
|
+
if(t.status === "in_progress" && t.active){
|
|
558
|
+
const ac = document.createElement("span");
|
|
559
|
+
ac.className = "qa-todo-active";
|
|
560
|
+
ac.textContent = String(t.active);
|
|
561
|
+
r.appendChild(ac);
|
|
562
|
+
}
|
|
563
|
+
list.appendChild(r);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
521
566
|
/* 行尾标注(可选,`引擎 · 角色`):只有团队任务列表给这一档,待办清单没有 */
|
|
522
567
|
if(t.meta){
|
|
523
568
|
const mt = document.createElement("span");
|
|
@@ -525,6 +570,14 @@ function qaTodoFill(host, rows, opts){
|
|
|
525
570
|
mt.textContent = String(t.meta);
|
|
526
571
|
r.appendChild(mt);
|
|
527
572
|
}
|
|
573
|
+
if(t.status === "in_progress" && t.active){
|
|
574
|
+
const ac = document.createElement("span");
|
|
575
|
+
ac.className = "qa-todo-active";
|
|
576
|
+
ac.textContent = String(t.active);
|
|
577
|
+
const mt = r.querySelector(".qa-todo-meta");
|
|
578
|
+
if(mt) mt.remove();
|
|
579
|
+
r.appendChild(ac);
|
|
580
|
+
}
|
|
528
581
|
list.appendChild(r);
|
|
529
582
|
});
|
|
530
583
|
host.appendChild(list);
|
|
@@ -145,6 +145,12 @@ node tests/check-conv-shards.mjs --write # 按代
|
|
|
145
145
|
② **移动端宿主页有了装配冒烟**(`check:conv-comp` 第二段,按打包后的 `shared/` 关系把
|
|
146
146
|
`client-ui-mobile/index.html` 原样跑一遍):组件挂得上、画布在 `#conv` 里、共用皮肤与
|
|
147
147
|
两个基线令牌都在、零脚本报错。
|
|
148
|
+
**2026-10-22 补了行为级的那一层**:`npm run check:conv-mobile`
|
|
149
|
+
(`tests/check-conv-mobile-host.mjs`,21 条)按 `magic-client` 的 `/m/` 路由镜像起服务,
|
|
150
|
+
真跑 `client-ui-mobile/index.html`(390×844 手机视口),验团队一轮的
|
|
151
|
+
任务行(标题 + 状态 + 耗时)、`.qa-task-arts` 子任务产物区(缩略图走 `/desk/media`、
|
|
152
|
+
同一文件去重、重发不叠加),以及**移动端宿主的 `pathProbe` / `mediaUrl` 桥真的装上了**
|
|
153
|
+
—— 之前只有装配冒烟,桥断掉时内核只是静默不画,闸门全绿。
|
|
148
154
|
③ [[conv.css]] 里 magic-test 独有的结构件规则**刻意保留**(本壳没有那些节点 → 匹配不到,
|
|
149
155
|
无害;内核里有运行时拼出来的类名,静态删会误判)—— 理由写在文件头。
|
|
150
156
|
- **2026-10-17 修的两个「看得见吗 / 画了吗」问题**(移动端报障:「看不到会话历史」):
|
|
@@ -516,6 +516,10 @@
|
|
|
516
516
|
|
|
517
517
|
.qa-arts-cap + .qa-arts{margin-top:4px}
|
|
518
518
|
|
|
519
|
+
/* 子任务产物区(任务列表卡正下方,见 03-chat-message::mountTaskArts):
|
|
520
|
+
与列表卡之间留一点缝,标题跟着卡一起对齐;卡本身沿用 .qa-arts / .qa-art 那一套。 */
|
|
521
|
+
.qa-task-arts{margin:0 0 8px}
|
|
522
|
+
|
|
519
523
|
.qa-art{border:1px solid var(--line);border-radius:10px;background:#FCFBF9;
|
|
520
524
|
overflow:hidden;cursor:pointer;transition:border-color .15s,box-shadow .15s}
|
|
521
525
|
|
|
@@ -1645,9 +1649,20 @@
|
|
|
1645
1649
|
.qa-todo-row.is-skipped{color:var(--paper-mute)}
|
|
1646
1650
|
.qa-todo-row.is-skipped .qa-todo-ico{border-style:dashed}
|
|
1647
1651
|
.qa-todo-row.is-skipped .qa-todo-t{text-decoration:line-through}
|
|
1648
|
-
.qa-todo-t{min-width:0}
|
|
1652
|
+
.qa-todo-t{min-width:0;flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1653
|
+
/* 行尾「状态 · 耗时」:团队的每一行都有(2026-10-22 用户:「应该显示子任务标题和状态耗时」)。
|
|
1654
|
+
标题占满剩余宽度并省略,状态与耗时靠右不换行 —— 一行永远是一行。 */
|
|
1655
|
+
.qa-todo-st{flex:none;font-family:var(--mono);font-size:10px;color:var(--paper-mute);white-space:nowrap}
|
|
1656
|
+
.qa-todo-st.is-in_progress{color:var(--amber)}
|
|
1657
|
+
.qa-todo-st.is-failed{color:var(--cinnabar-hi)}
|
|
1658
|
+
.qa-todo-dur{flex:none;font-family:var(--mono);font-size:10px;color:var(--paper-mute);
|
|
1659
|
+
white-space:nowrap;font-variant-numeric:tabular-nums}
|
|
1649
1660
|
.qa-todo-meta{margin-left:auto;flex:none;max-width:46%;font-family:var(--mono);font-size:10px;
|
|
1650
1661
|
color:var(--paper-mute);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
1662
|
+
/* 行内进度(团队心跳:「等待授权」「已 40 秒无新输出」):接在「状态 耗时」之后,
|
|
1663
|
+
是这一行里**最次要**的一档(字最小、允许收缩),标题永远优先占宽度。 */
|
|
1664
|
+
.qa-todo-active{flex:0 1 auto;min-width:0;max-width:40%;font-size:10px;
|
|
1665
|
+
color:var(--amber);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
1651
1666
|
.qa-todo-panel{min-width:0;margin:0 0 8px;border:1px solid var(--line);
|
|
1652
1667
|
border-radius:var(--r-md);overflow:hidden;background:var(--panel,#FFF)}
|
|
1653
1668
|
.qa-todo-panel .qa-todo-hd{gap:7px}
|
|
@@ -161,6 +161,37 @@
|
|
|
161
161
|
|
|
162
162
|
.composer{flex:0 0 auto;padding:0 clamp(12px,4vw,20px) 16px}
|
|
163
163
|
|
|
164
|
+
/* 下一轮建议(2026-10-23):一轮收尾后出现在**输入框正上方**,点一下直接发送。
|
|
165
|
+
形制照用户给的参考图:一枚枚圆角描边的短语,贴在输入框外沿、左对齐。
|
|
166
|
+
· 只占一行,横向滚动(不换行)—— 三条建议在手机上也不会把输入框顶下去;
|
|
167
|
+
· 宽/居中与 .composer__box、.composer__tools 逐值对齐,左边缘不参差;
|
|
168
|
+
· 默认 hidden 且**不预留高度**:没有建议时输入框位置一个像素都不动;
|
|
169
|
+
· ⚠️ display:flex 会盖掉 [hidden] 的 UA 规则,所以显式补一条。 */
|
|
170
|
+
.sugbar{display:flex;align-items:center;gap:8px;max-width:860px;margin:0 auto 8px;
|
|
171
|
+
overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;
|
|
172
|
+
-webkit-overflow-scrolling:touch;padding:1px 1px 2px}
|
|
173
|
+
.sugbar[hidden]{display:none}
|
|
174
|
+
.sugbar::-webkit-scrollbar{display:none}
|
|
175
|
+
.sugchip{flex:0 0 auto;max-width:min(78vw,320px);height:34px;padding:0 14px;
|
|
176
|
+
border:1px solid var(--border);border-radius:var(--r-full);background:var(--raised);
|
|
177
|
+
color:var(--ink);font:13.5px/32px var(--font);white-space:nowrap;overflow:hidden;
|
|
178
|
+
text-overflow:ellipsis;-webkit-tap-highlight-color:transparent}
|
|
179
|
+
.sugchip:active{background:var(--subtle);border-color:var(--accent)}
|
|
180
|
+
/* 托管中:紧挨着建议行的一枚状态标记(点它 = 暂停托管)。它与第一枚「待发」建议
|
|
181
|
+
同时出现,用户看到的是「要自动发这句」而不是「又多了几个按钮」。 */
|
|
182
|
+
.sugtag{flex:0 0 auto;height:34px;padding:0 11px;border:1px solid var(--accent);
|
|
183
|
+
border-radius:var(--r-full);background:transparent;color:var(--accent);
|
|
184
|
+
font:12px/32px var(--font);white-space:nowrap;letter-spacing:.02em}
|
|
185
|
+
.sugtag::before{content:"";display:inline-block;width:6px;height:6px;margin-right:6px;
|
|
186
|
+
border-radius:50%;background:currentColor;vertical-align:1px}
|
|
187
|
+
.sugtag:active{background:var(--subtle)}
|
|
188
|
+
/* 提问卡头的托管标记:卡头只有 9px 高,按建议条那枚 34px 会撑破;缩到 20px,仍保留圆点。 */
|
|
189
|
+
.qa-ask-hd .sugtag{height:20px;padding:0 8px;font:10px/18px var(--font);border-radius:var(--r-full)}
|
|
190
|
+
.qa-ask-hd .sugtag::before{width:5px;height:5px;margin-right:5px;vertical-align:1px}
|
|
191
|
+
/* 第一条建议 = 托管马上要发的那句:给一圈同色描边,别让它和另外两枚看起来一样重。 */
|
|
192
|
+
.sugchip--next{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
|
|
193
|
+
@media (prefers-reduced-motion:no-preference){.sugchip{transition:background .12s,border-color .12s}}
|
|
194
|
+
|
|
164
195
|
.composer__box{max-width:860px;margin:0 auto;border:1px solid var(--border);border-radius:var(--r-card);
|
|
165
196
|
background:var(--raised);display:flex;flex-direction:column;gap:2px;padding:10px 12px 8px}
|
|
166
197
|
|
|
@@ -212,13 +243,20 @@
|
|
|
212
243
|
|
|
213
244
|
/* 排队追问(2026-10-21:会话运行中允许先提交追问,类似 magic-test PC 端):
|
|
214
245
|
运行中提交的那几条先停在这里,等当前轮收尾后自动发出 —— 不打断、也不画成
|
|
215
|
-
用户气泡(还没跑,画上去会让人以为已经发出去了)。
|
|
216
|
-
|
|
246
|
+
用户气泡(还没跑,画上去会让人以为已经发出去了)。
|
|
247
|
+
⚠️ 2026-10-22 用户:「排队的任务列表是挂在对话框外面不是里面」—— 这条**挂在输入框
|
|
248
|
+
外面**(.composer__box 的上沿之上,DOM 里是它的前一个兄弟),不共享框的底色与描边。
|
|
249
|
+
所以在框外要自己站得住:宽度/居中与 .composer__box 逐值对齐;胶囊用 --raised 底 +
|
|
250
|
+
描边(页面底色上的 --subtle 几乎看不见)。出现在框外 → 顶上去的是参数行,框不被撑高。 */
|
|
251
|
+
.queuebar{display:flex;flex-direction:column;gap:6px;min-width:0;max-width:860px;
|
|
252
|
+
margin:0 auto 6px;padding:0 2px;max-height:132px;overflow-y:auto}
|
|
253
|
+
.queuebar[hidden]{display:none}
|
|
217
254
|
|
|
218
|
-
.queuebar__cap{font-size:11.5px;line-height:15px;color:var(--muted);padding-left:
|
|
255
|
+
.queuebar__cap{font-size:11.5px;line-height:15px;color:var(--muted);padding-left:4px}
|
|
219
256
|
|
|
220
257
|
.queuechip{display:flex;align-items:center;gap:6px;min-width:0;padding:5px 6px 5px 10px;
|
|
221
|
-
border-radius:var(--r-full);background:var(--
|
|
258
|
+
border-radius:var(--r-full);background:var(--raised);border:1px solid var(--border);
|
|
259
|
+
color:var(--ink);font-size:12px}
|
|
222
260
|
|
|
223
261
|
.queuechip__ico{flex:0 0 auto;color:var(--muted);display:flex}
|
|
224
262
|
|