magic-agent 1.0.3 → 1.0.49
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 +254 -8
- package/README.md +48 -5
- package/client-ui/index.html +32 -0
- package/client-ui/pc/app.css +89 -1
- package/client-ui/pc/index.html +18 -8
- package/client-ui/pc/js/00-base.js +8 -30
- package/client-ui/pc/js/05-task-list-render.js +4 -1
- package/client-ui/pc/js/07-chat-stream-render.js +2 -85
- package/client-ui/pc/js/10-markdown.js +3 -171
- package/client-ui/pc/js/18-askbox.js +2 -176
- package/client-ui/pc/js/90-glue.js +1 -1
- package/client-ui/pc/js/91-workbench-stubs.js +4 -10
- package/client-ui/pc/js/93-rail.js +10 -3
- package/client-ui/shared/conv/01-dom.js +52 -0
- package/client-ui/shared/conv/02-media-kinds.js +34 -0
- package/client-ui/shared/conv/03-chat-message.js +1561 -0
- package/client-ui/shared/conv/06-ask-answer.js +377 -0
- package/client-ui/shared/conv/07-stream-delegation.js +158 -0
- package/client-ui/{pc/js → shared/conv}/09-stream-apply.js +121 -6
- package/client-ui/shared/conv/10-markdown-render.js +252 -0
- package/client-ui/shared/conv/18-askbox-markup.js +185 -0
- package/client-ui/{pc/js → shared/conv}/19-attachments.js +365 -21
- package/client-ui/shared/conv/27-conv-ledger.js +381 -0
- package/client-ui/{pc/js → shared/conv}/29-msg-variants.js +112 -3
- package/client-ui/shared/conv/README.md +257 -0
- package/client-ui/shared/conv/conv.css +1638 -0
- package/client-ui/shared/conv/dom-map.md +170 -0
- package/client-ui/shared/conv/manifest.json +44 -0
- package/client-ui/shared/conversation.css +368 -190
- package/client-ui/shared/conversation.js +1722 -788
- package/client-ui/shared/host-glue.js +143 -0
- package/client-ui/shared/transport-http.js +130 -6
- package/client-ui-mobile/index.html +2417 -103
- package/cmd/magic-client/clientctl.go +273 -0
- package/cmd/magic-client/main.go +71 -5
- package/go.mod +6 -3
- package/go.sum +4 -1
- package/internal/agent/arkclaw.go +3 -2
- package/internal/agent/arkclaw_stream.go +126 -8
- package/internal/agent/arkclaw_stream_test.go +97 -15
- package/internal/agent/arkclaw_test.go +3 -3
- package/internal/agent/attachments.go +18 -5
- package/internal/agent/attachments_test.go +7 -1
- package/internal/agent/capability_test.go +11 -1
- package/internal/agent/claude.go +96 -158
- package/internal/agent/codebuddy.go +108 -133
- package/internal/agent/codebuddy_gateway.go +8 -4
- package/internal/agent/codebuddy_gateway_stream.go +82 -2
- package/internal/agent/codebuddy_gateway_test.go +236 -1
- package/internal/agent/codebuddy_stream_test.go +262 -0
- package/internal/agent/codex.go +50 -9
- package/internal/agent/codex_stream_test.go +30 -0
- package/internal/agent/codex_test.go +24 -0
- package/internal/agent/custom_models.go +109 -3
- package/internal/agent/custom_models_test.go +74 -0
- package/internal/agent/dsh.go +464 -130
- package/internal/agent/dsh_resume.go +258 -0
- package/internal/agent/dsh_sdk.go +68 -17
- package/internal/agent/dsh_sdk_test.go +102 -2
- package/internal/agent/dsh_test.go +409 -36
- package/internal/agent/engine.go +26 -6
- package/internal/agent/engine_base.go +46 -2
- package/internal/agent/engine_base_test.go +29 -0
- package/internal/agent/engine_test.go +101 -0
- package/internal/agent/env.go +69 -1
- package/internal/agent/itemtracker.go +271 -0
- package/internal/agent/launch.go +192 -0
- package/internal/agent/launch_test.go +206 -0
- package/internal/agent/launch_unix.go +15 -0
- package/internal/agent/launch_windows.go +24 -0
- package/internal/agent/limitnotice.go +103 -0
- package/internal/agent/limitnotice_test.go +211 -0
- package/internal/agent/llmengine.go +2 -2
- package/internal/agent/models.go +3 -2
- package/internal/agent/openclaw.go +4 -3
- package/internal/agent/openclaw_acp.go +7 -0
- package/internal/agent/openclaw_test.go +2 -2
- package/internal/agent/permission.go +100 -11
- package/internal/agent/permission_test.go +100 -36
- package/internal/agent/runcmd.go +13 -3
- package/internal/agent/runner.go +7 -1
- package/internal/agent/runner_test.go +1 -1
- package/internal/agent/stream.go +777 -43
- package/internal/agent/stream_test.go +3 -2
- package/internal/agent/stream_timeline_test.go +905 -0
- package/internal/agent/streamjson.go +36 -2
- package/internal/agent/toolkind.go +20 -1
- package/internal/agent/toolkind_test.go +7 -1
- package/internal/agent/tools_suffix_test.go +10 -3
- package/internal/agent/trae.go +5 -3
- package/internal/agent/workspace_test.go +52 -0
- package/internal/cli/ask.go +260 -33
- package/internal/cli/ask_test.go +15 -11
- package/internal/cli/attach_test.go +32 -20
- package/internal/cli/clientcmd.go +156 -0
- package/internal/cli/events.go +53 -8
- package/internal/cli/events_test.go +230 -0
- package/internal/cli/keepalive_test.go +101 -6
- package/internal/cli/permission_test.go +5 -1
- package/internal/cli/root.go +174 -32
- package/internal/cli/session.go +175 -28
- package/internal/cli/session_test.go +144 -5
- package/internal/cli/sessionlog.go +385 -38
- package/internal/cli/sessionlog_test.go +708 -0
- package/internal/cli/sessionlog_user_test.go +142 -1
- package/internal/cli/sessiontitle.go +141 -0
- package/internal/client/browse.go +456 -0
- package/internal/client/browse_test.go +199 -0
- package/internal/client/cors_test.go +35 -0
- package/internal/client/daemon.go +525 -0
- package/internal/client/daemon_test.go +212 -0
- package/internal/client/detach_unix.go +19 -0
- package/internal/client/detach_windows.go +27 -0
- package/internal/client/pair.go +3 -3
- package/internal/client/plugins.go +376 -0
- package/internal/client/plugins_test.go +223 -0
- package/internal/client/proxy.go +374 -0
- package/internal/client/proxy_test.go +212 -0
- package/internal/client/service.go +1464 -48
- package/internal/client/service_test.go +649 -206
- package/internal/client/session_events_test.go +130 -0
- package/internal/client/wsproxy.go +113 -0
- package/internal/client/wsproxy_test.go +102 -0
- package/internal/session/delete.go +151 -0
- package/internal/session/delete_test.go +103 -0
- package/internal/session/session.go +155 -20
- package/internal/session/session_append.go +79 -21
- package/internal/session/session_test.go +124 -10
- package/internal/session/sessionlog.go +572 -6
- package/internal/session/sessionlog_read_test.go +410 -0
- package/internal/session/title.go +486 -0
- package/internal/session/title_test.go +298 -0
- package/npm/bin/magic-agent.js +101 -10
- package/npm/bin/magic-client.js +118 -0
- 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/npm/install.js +36 -13
- package/npm/lib/platform.js +27 -5
- package/package.json +8 -3
- package/client-ui/pc/js/03-chat-message.js +0 -880
- package/client-ui/pc/js/06-ask-answer.js +0 -204
- /package/client-ui/{pc/js → shared/conv}/08-ask-event-map.js +0 -0
- /package/client-ui/{pc → shared}/vendor/highlight.min.js +0 -0
- /package/client-ui/{pc → shared}/vendor/marked.min.js +0 -0
- /package/client-ui/{pc → shared}/vendor/purify.min.js +0 -0
|
@@ -1,256 +1,434 @@
|
|
|
1
|
-
/* 对话 UI
|
|
1
|
+
/* 对话 UI 组件(PC 与移动端共用这一份)的样式 —— 2026-10-16(块③)起只剩**外壳**。
|
|
2
2
|
*
|
|
3
|
-
* ──
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* ── 这份文件现在管什么 ──
|
|
4
|
+
* · 挂载壳:`.conv` / `.conv__thread`(= 内核的画布 `#qa-stream`,滚动容器)/ 空态
|
|
5
|
+
* · 输入区:`.composer*` / `.attachbar` / `.attachchip` / `.attachthumb` / `.attachgrid`
|
|
6
|
+
* · 选择器:`.casc`(引擎 · 模型)/ `.permchip`(授权档)/ `.pick*`(底部选择面板)
|
|
7
|
+
* · 本壳自己的「运行中」指示 `.working*` 与错误提示 `.notice*`
|
|
8
|
+
*
|
|
9
|
+
* ── 不再管的(已交给共用皮肤)──
|
|
10
|
+
* 消息流本体(气泡 / 思考块 / 工具组与工具卡 / 标记行 / 分隔条 / 收尾行 / 提问卡 /
|
|
11
|
+
* 产物卡 / markdown 正文)由**对话内核**画,皮肤在 `shared/conv/conv.css` 一份 ——
|
|
12
|
+
* 三端同一份 DOM、同一份皮肤,差别只在下面这组颜色令牌。旧的那套 `.ubub` / `.msg` /
|
|
13
|
+
* `.rsn` / `.tool` / `.conv-turn` / `.md` 规则随实现一起删掉了(别再加回来)。
|
|
8
14
|
*
|
|
9
15
|
* ── 颜色契约(宿主必须定义,本文件只用不定义)──
|
|
10
16
|
* 基础:--canvas --ink --ink-soft --muted --faint --border --subtle --raised
|
|
11
17
|
* --primary-action --on-primary-action --app-shadow
|
|
12
18
|
* 对话:--bubble --bubble-text --code --status-neutral --status-neutral-text --activity
|
|
13
|
-
*
|
|
14
|
-
*
|
|
19
|
+
* 正文:--md-body --md-muted --md-border --md-code-bg --link --prompt --shimmer
|
|
20
|
+
* 面板:--panel --panel-border
|
|
15
21
|
* 语义:--accent --warning --success --error-surface --error-border --error-text --error-icon
|
|
16
22
|
* --running --transitioning --active
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
23
|
+
* 尺寸:--r-card --r --r-full --font --mono
|
|
24
|
+
*
|
|
25
|
+
* ── 可选令牌(不给就用下面的兜底值)──
|
|
26
|
+
* `--q-bottle*` 是掌天瓶绿液的配色(瓶身描边 / 液面亮色 / 液色 / 深液 / 瓶塞 / 光晕)。
|
|
27
|
+
* 兜底那套绿是**自带的**,宿主不定义也能画;想跟品牌色对齐的宿主(PC 的 --accent 是墨绿系)
|
|
28
|
+
* 在自己页里覆盖这 6 个名字即可。
|
|
20
29
|
*/
|
|
30
|
+
/* ⚠️ min-width:0 两处都不能省(2026-10-17):本壳是 flex 链上的中间层,
|
|
31
|
+
默认的 `min-width:auto` 会让「内容里最宽的那一小块」决定整条链的宽度 ——
|
|
32
|
+
一行长代码就能把会话区从 414 撑到 1157(用户在手机上看到的就是「文字被右缘切掉」)。
|
|
33
|
+
这么写之后宽度只由外层的视口定,宽内容(代码块/表格)在**自己的盒子里**横向滚。 */
|
|
34
|
+
.conv{display:flex;flex-direction:column;min-height:0;min-width:0;flex:1}
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
MessageList LazyColumn:padding horizontal 20dp · spacedBy 16dp。
|
|
24
|
-
宽屏时用左右对称的 padding 把内容收在 860px 内 —— 刻意不加内层 wrapper:
|
|
25
|
-
组件里那个 thread 元素同时是**滚动容器**和**条目容器**,多套一层会让
|
|
26
|
-
scrollTop 与 appendChild 落到两个不同元素上(历史与实时共用一条渲染路径,
|
|
27
|
-
这类错会表现成「滚动不跟随 / 事件画不出来」)。 */
|
|
28
|
-
.conv{display:flex;flex-direction:column;min-height:0;flex:1}
|
|
29
|
-
.conv__thread{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
|
|
36
|
+
.conv__thread{flex:1;min-height:0;min-width:0;overflow-y:auto;overscroll-behavior:contain;
|
|
30
37
|
-webkit-overflow-scrolling:touch;display:flex;flex-direction:column;gap:16px;
|
|
31
38
|
padding:20px max(12px, calc((100% - 860px) / 2)) 28px}
|
|
32
|
-
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
/* ── 用户气泡 UserBubble ──
|
|
36
|
-
SessionMessages.kt:1341 RoundedCornerShape(22.dp) · sessionMessageBubble
|
|
37
|
-
:1343 padding(17,13) · :1349 16.5sp/24sp · :1296 maxBubbleWidth = 78% · :1352 maxLines 8 */
|
|
38
|
-
.urow{display:flex;justify-content:flex-end;align-items:flex-end;gap:6px;max-width:100%}
|
|
39
|
-
.urow__col{display:flex;flex-direction:column;align-items:flex-end;gap:7px;min-width:0;max-width:78%}
|
|
40
|
-
.ubub{position:relative;max-width:100%;border-radius:22px;background:var(--bubble);
|
|
41
|
-
padding:13px 17px;display:flex;flex-direction:column;gap:7px}
|
|
42
|
-
.ubub__t{color:var(--bubble-text);font-size:16.5px;line-height:24px;white-space:pre-wrap;
|
|
43
|
-
word-break:break-word;overflow:hidden}
|
|
44
|
-
.ubub:not(.is-open) .ubub__t{display:-webkit-box;-webkit-box-orient:vertical;
|
|
45
|
-
-webkit-line-clamp:8;overflow:hidden}
|
|
46
|
-
.ubub__more{align-self:flex-start;color:var(--warning);font-size:12px;font-weight:700;line-height:1.2}
|
|
47
|
-
.ubub__meta{align-self:flex-end;font-size:11px;font-weight:600;color:var(--error-icon)}
|
|
48
|
-
/* MessageCopyButton:30dp 圆 · muted */
|
|
49
|
-
.copybtn{width:30px;height:30px;border-radius:var(--r-full);display:flex;align-items:center;
|
|
50
|
-
justify-content:center;color:var(--muted);flex:0 0 auto}
|
|
51
|
-
.copybtn:hover,.copybtn:active{background:var(--activity)}
|
|
52
|
-
.copybtn--sm{width:23px;height:23px}
|
|
53
|
-
|
|
54
|
-
/* ── Agent 文本 AgentMarkdownText ──
|
|
55
|
-
AgentMarkdownText.kt:139-143 padding horizontal 4dp · spacedBy 8dp */
|
|
56
|
-
.md{padding:0 4px;display:flex;flex-direction:column;gap:8px;color:var(--md-body);
|
|
57
|
-
font-size:16px;line-height:26px;word-break:break-word}
|
|
58
|
-
.md>*{margin:0}
|
|
59
|
-
.md h1{font-size:22px;line-height:30px;font-weight:700}
|
|
60
|
-
.md h2{font-size:20px;line-height:28px;font-weight:700}
|
|
61
|
-
.md h3,.md h4,.md h5,.md h6{font-size:18px;line-height:26px;font-weight:700}
|
|
62
|
-
.md strong{font-weight:700}
|
|
63
|
-
.md em{font-style:italic}
|
|
64
|
-
.md del{text-decoration:line-through}
|
|
65
|
-
.md a{color:var(--link);text-decoration:underline;word-break:break-all}
|
|
66
|
-
.md hr{border:0;height:1px;background:var(--md-border)}
|
|
67
|
-
/* MarkdownList(:219-250) */
|
|
68
|
-
.md ul,.md ol{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px}
|
|
69
|
-
.md li{display:flex;gap:8px}
|
|
70
|
-
.md li>.mk{flex:0 0 auto;color:var(--md-muted);font-size:15px;line-height:24px}
|
|
71
|
-
.md li>.mk--mono{font-family:var(--mono);font-size:13px;line-height:24px}
|
|
72
|
-
.md li>.li-b{flex:1;min-width:0;white-space:pre-wrap;word-break:break-word}
|
|
73
|
-
/* MarkdownQuote(:254-278) 2dp 竖线 */
|
|
74
|
-
.md blockquote{display:flex;gap:10px;align-items:stretch}
|
|
75
|
-
.md blockquote>.bar{flex:0 0 auto;width:2px;background:var(--md-border);border-radius:1px}
|
|
76
|
-
.md blockquote>.q-b{flex:1;min-width:0;color:var(--md-muted)}
|
|
77
|
-
/* MarkdownTable(:281-320) 8dp 圆角 · 1dp 描边 */
|
|
78
|
-
.md-table-wrap{align-self:flex-start;max-width:100%;overflow-x:auto;
|
|
79
|
-
border:1px solid var(--md-border);border-radius:8px}
|
|
80
|
-
.md table{border-collapse:collapse;font-size:14px;line-height:22px}
|
|
81
|
-
.md th,.md td{min-width:100px;max-width:240px;padding:8px 10px;
|
|
82
|
-
border:.5px solid var(--md-border);text-align:left;vertical-align:top}
|
|
83
|
-
.md th{background:var(--md-code-bg);font-weight:700}
|
|
84
|
-
/* 行内码 codeInlineContent(:445-480) 5dp 圆角 · 14sp mono */
|
|
85
|
-
.md code{font-family:var(--mono);font-size:13.5px;line-height:20px;background:var(--md-code-bg);
|
|
86
|
-
border:1px solid var(--md-border);border-radius:5px;padding:1px 6px;word-break:break-all}
|
|
87
|
-
.md .md-code--file{color:var(--link);text-decoration:underline}
|
|
88
|
-
.md .md-code--wrap{display:inline-block;padding:3px 7px}
|
|
89
|
-
|
|
90
|
-
/* ── 代码面板 MarkdownCodePanel(:602-670) 20dp 圆角 · padding(18,17,18,12) ── */
|
|
91
|
-
.codepanel{background:var(--panel);border:1px solid var(--panel-border);border-radius:20px;
|
|
92
|
-
box-shadow:0 12px 28px -8px var(--app-shadow);padding:17px 18px 12px;display:flex;flex-direction:column}
|
|
93
|
-
.codepanel__head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
|
|
94
|
-
.codepanel__label{color:var(--ink);font-size:14px;line-height:18px;font-weight:700}
|
|
95
|
-
.codepanel__code{margin-top:14px;overflow-x:auto}
|
|
96
|
-
.codepanel__pre{margin:0;font:15px/22px var(--mono);color:var(--ink);white-space:pre;letter-spacing:0}
|
|
97
|
-
/* BashCommandLine(:739-770) 首个 token 用 prompt 色 */
|
|
98
|
-
.cmdline{display:flex;gap:7px;align-items:baseline;font:500 15px/22px var(--mono);white-space:nowrap}
|
|
99
|
-
.cmdline+.cmdline{margin-top:4px}
|
|
100
|
-
.cmdline__p{color:var(--prompt)}
|
|
101
|
-
.cmdline__r{color:var(--ink)}
|
|
102
|
-
|
|
103
|
-
/* ── 命令行 CommandLineBar(:970-2019) 14dp 圆角 ── */
|
|
104
|
-
.cmdbar{display:flex;flex-direction:column;gap:6px}
|
|
105
|
-
.cmdbar__head{display:flex;align-items:center;justify-content:space-between;gap:8px}
|
|
106
|
-
.cmdbar__label{color:var(--muted);font-size:11px;font-weight:700}
|
|
107
|
-
.cmdbar__body{display:flex;gap:8px;align-items:flex-start;border-radius:14px;background:var(--code);
|
|
108
|
-
border:1px solid var(--border);padding:9px 10px}
|
|
109
|
-
.cmdbar__p{flex:0 0 auto;color:var(--muted);font:800 13px/18px var(--mono)}
|
|
110
|
-
.cmdbar__t{flex:1;min-width:0;color:var(--ink-soft);font:600 13px/18px var(--mono);
|
|
111
|
-
overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-break:break-all}
|
|
112
|
-
|
|
113
|
-
/* ── 段落预览 CommandPreviewSection(:2021-2052) 12dp 圆角 ── */
|
|
114
|
-
.sect{display:flex;flex-direction:column;gap:6px}
|
|
115
|
-
.sect__head{display:flex;align-items:center;justify-content:space-between;gap:8px}
|
|
116
|
-
.sect__label{color:var(--muted);font-size:11px;font-weight:700}
|
|
117
|
-
.sect__code{border-radius:12px;border:1px solid var(--md-border);background:var(--code);
|
|
118
|
-
padding:10px 12px;overflow:auto;max-height:360px}
|
|
119
|
-
.sect__pre{margin:0;font:13px/18px var(--mono);color:var(--md-body);white-space:pre-wrap;word-break:break-word}
|
|
120
|
-
.sect__pre--err{color:var(--error-text)}
|
|
121
|
-
|
|
122
|
-
/* ── 思考块 ReasoningSection(:1530-1608) 行 min 34dp · 展开体缩进 30dp ── */
|
|
123
|
-
.rsn{display:flex;flex-direction:column;gap:8px;padding:0 4px}
|
|
124
|
-
.rsn__head{display:flex;align-items:center;gap:8px;min-height:34px;padding:6px;border-radius:8px;width:100%;text-align:left}
|
|
125
|
-
.rsn__title{flex:1;min-width:0;color:var(--ink);font:400 13px/1.3 var(--mono);
|
|
126
|
-
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
127
|
-
.rsn__body{padding:0 6px 0 30px}
|
|
128
|
-
.rsn__body .md{font-size:14px;line-height:21px;font-weight:300;gap:6px}
|
|
129
|
-
.rsn__body .md h1{font-size:17px;line-height:24px;font-weight:600}
|
|
130
|
-
.rsn__body .md h2{font-size:16px;line-height:23px;font-weight:600}
|
|
131
|
-
.rsn__body .md h3,.rsn__body .md h4,.rsn__body .md h5,.rsn__body .md h6{font-size:15px;line-height:22px;font-weight:600}
|
|
132
|
-
.rsn__body .md table{font-size:13px;line-height:19px}
|
|
133
|
-
|
|
134
|
-
/* ── 工具卡 ToolActivityCard(:1625-1748) 折叠行 min 34dp · 8dp 圆角 ── */
|
|
135
|
-
.tool{display:flex;flex-direction:column;gap:8px;padding:0 4px}
|
|
136
|
-
.tool__head{display:flex;align-items:center;gap:8px;min-height:34px;padding:6px;border-radius:8px;
|
|
137
|
-
background:var(--activity);text-align:left;width:100%}
|
|
138
|
-
.tool__ico{flex:0 0 auto;color:var(--muted);display:flex}
|
|
139
|
-
.tool--err .tool__ico{color:var(--error-icon)}
|
|
140
|
-
.tool__t{flex:1;min-width:0;color:var(--ink);font:400 13px/1.3 var(--mono);
|
|
141
|
-
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
142
|
-
.tool__cv{flex:0 0 auto;color:var(--faint);transition:transform .18s}
|
|
143
|
-
.tool[aria-expanded="false"] .tool__cv{transform:rotate(-90deg)}
|
|
144
|
-
.tool__body{border-radius:14px;background:var(--subtle);border:1px solid var(--border);
|
|
145
|
-
padding:14px 16px;display:flex;flex-direction:column;gap:10px}
|
|
146
|
-
/* 扫光:ToolActivityCard 折叠行在“正在跑”时的 shimmer 动画 */
|
|
39
|
+
|
|
40
|
+
.conv__thread{overflow-x:hidden}
|
|
41
|
+
|
|
147
42
|
@keyframes tool-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
background-size:200% 100%;animation:tool-shimmer 1.4s ease-in-out infinite}
|
|
153
|
-
|
|
154
|
-
/* ── 状态胶囊 CompactStatusPill / 工具占位 ToolPlaceholder ── */
|
|
155
|
-
.pill{display:inline-flex;align-items:center;gap:6px;height:24px;padding:0 10px;border-radius:var(--r-full);
|
|
156
|
-
background:var(--status-neutral);color:var(--status-neutral-text);font-size:12px;font-weight:600;flex:0 0 auto}
|
|
157
|
-
.pill--err{background:var(--error-surface);color:var(--error-text);border:1px solid var(--error-border)}
|
|
158
|
-
.ph{display:flex;align-items:center;gap:10px;padding:6px 4px;color:var(--muted);font-size:14px}
|
|
159
|
-
.ph--err{color:var(--error-text)}
|
|
160
|
-
/* WorkingIndicator:16dp conic 环 */
|
|
161
|
-
.work{display:flex;align-items:center;gap:10px;padding:6px 4px;color:var(--muted);font-size:14px}
|
|
162
|
-
.work__ring{width:16px;height:16px;border-radius:var(--r-full);flex:0 0 auto;
|
|
43
|
+
|
|
44
|
+
.working{display:flex;align-items:center;gap:9px;padding:6px 4px;color:var(--muted);font-size:13.5px;line-height:20px}
|
|
45
|
+
|
|
46
|
+
.working__sp{width:16px;height:16px;border-radius:var(--r-full);flex:0 0 auto;
|
|
163
47
|
background:conic-gradient(from 0deg,transparent 0% 25%,var(--accent) 75% 100%);
|
|
164
48
|
animation:work-spin .9s linear infinite}
|
|
49
|
+
|
|
50
|
+
.working__act{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
51
|
+
|
|
52
|
+
.working__d{flex:0 0 auto;font-variant-numeric:tabular-nums;color:var(--faint)}
|
|
53
|
+
|
|
165
54
|
@keyframes work-spin{to{transform:rotate(360deg)}}
|
|
166
|
-
/* 时间线分隔条 */
|
|
167
|
-
.sep{display:flex;align-items:center;gap:10px;color:var(--muted);font-size:12px;
|
|
168
|
-
font-variant-numeric:tabular-nums;padding:2px 4px}
|
|
169
|
-
.sep::after{content:"";flex:1;height:1px;background:var(--border)}
|
|
170
55
|
|
|
171
|
-
/*
|
|
56
|
+
/* 「运行中」并到 bot 头像那一行(2026-10-20 用户:「agent 运行中这一段和下面的bot图标
|
|
57
|
+
合并到一行 运行中的图标动画也改到bot上显示」)。
|
|
58
|
+
这一块与 `.qa-msg` / `.qa-ico` 有关(那是内核画的 DOM),但**规则写在宿主这份**:
|
|
59
|
+
`.qa-runlive` 这个类由 conversation.js 的 mountWorking 盖上去,内核并不知情
|
|
60
|
+
—— 见 shared/conv/README.md 的边界表:谁的状态谁盖类,皮肤跟着走。
|
|
61
|
+
|
|
62
|
+
形制:`[头像] [引擎名] [运行中 · 秒数]` 同一行,正文(.qa-bd)仍整行换到下面。
|
|
63
|
+
⚠️ 为什么不是给 `.working` 一个 `left` 绝对定位:引擎名(内核的 `.qa-who`)是**绝对定位**
|
|
64
|
+
贴在头像右边、宽度随引擎/模型名变化,用固定的 `left` 避开它必然要么压住名字、要么留一段空白。
|
|
65
|
+
这里改成把 `.qa-who` 从绝对定位放回流内(`.qa-ico` 转 flex、宽度自适应),
|
|
66
|
+
三块自然按顺序排 —— 名字多长都不会撞。
|
|
67
|
+
⚠️ 选择器带 `:not(.qa-mine):not(.qa-sim)`:要与内核那条「非本人侧竖排」的规则
|
|
68
|
+
(conv.css 的 `.qa-msg:not(.qa-mine):not(.qa-sim){flex-direction:column}`)同等特异度且
|
|
69
|
+
排在它之后才压得住,同时保证用户/数字分身那两档不受影响。 */
|
|
70
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim){
|
|
71
|
+
flex-flow:row wrap;align-items:center;gap:0 10px}
|
|
72
|
+
|
|
73
|
+
/* 头像那一格从「固定 22px 的方块」改成「脸 + 名字」横排、宽度自适应:
|
|
74
|
+
这是引擎名能进流内的前提(`.qa-who` 原本绝对定位在 `.qa-ico` 右边,父级一自适应就跟着排)。
|
|
75
|
+
⚠️ `min-width:0` 别省:flex 子项默认 `min-width:auto`,小标那格会成为**整条链的下限**
|
|
76
|
+
—— 模型名一长,这一格就把秒数那一格顶出去换行(用户报的「现在换行了」)。 */
|
|
77
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-ico{
|
|
78
|
+
width:auto;height:auto;min-width:0;display:flex;align-items:center;gap:8px}
|
|
79
|
+
|
|
80
|
+
/* 脸固定 22px:`.qa-av` 原本是 `width:100%;height:100%`(填满 `.qa-ico` 那一格),
|
|
81
|
+
`.qa-ico` 宽度自适应之后必须显式给回尺寸,否则脸会被压成 0。
|
|
82
|
+
⚠️ 只给 `.qa-av`、**不给 `svg`**:svg 是脸**里面**那枚图标(15px,另有 `.qa-av-bot svg`
|
|
83
|
+
与 `.qa-ico svg` 两条规则管),写成 `> svg` 命中不到、且一旦命中就会把它从 15px 撑成 22px。 */
|
|
84
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-ico > .qa-av{
|
|
85
|
+
width:var(--qa-av);height:var(--qa-av);flex:0 0 auto}
|
|
86
|
+
|
|
87
|
+
/* 小标从绝对定位放回流内(见上面)。
|
|
88
|
+
⚠️ 加 `min-width:0` + 省略号:内核那条给的是 `white-space:nowrap`,模型名再短也有
|
|
89
|
+
撑爆的可能(一行放不下时 `flex-flow:row wrap` 会把秒数挤到换行 —— 用户报的正是这个)。
|
|
90
|
+
有了 `min-width:0` + `overflow:hidden`,超长时它自己截断,秒数那一格永远留在行内。 */
|
|
91
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-ico .qa-who{
|
|
92
|
+
position:static;transform:none;
|
|
93
|
+
min-width:0;overflow:hidden;text-overflow:ellipsis}
|
|
94
|
+
|
|
95
|
+
/* ⚠️ `.qa-ico::before` 是那圈 `inset:0` 的光晕(内核 conv.css 的 `.qa-ico::before`)。
|
|
96
|
+
`.qa-ico` 为了让引擎名进流内而改成宽度自适应,那圈光晕就会顺着一起变宽、把引擎名
|
|
97
|
+
也罩进去(`z-index:-1` + `blur(3px)` → 名字底下糊一层颜色)。运行态按脸的大小收回来。 */
|
|
98
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-ico::before{
|
|
99
|
+
width:var(--qa-av);height:var(--qa-av);inset:0 auto 0 0}
|
|
100
|
+
|
|
101
|
+
/* 正文仍整行换到下面一行(`flex-flow:row wrap` 下靠它撑满整行来换行) */
|
|
102
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-bd{flex:0 0 100%;min-width:0}
|
|
103
|
+
|
|
104
|
+
/* 操作行(复制 / 重试)同理整行。它平时是 `hidden`(流式期间半句正文不该能复制),
|
|
105
|
+
由内核的 finish 显形 —— 而 finish 早于本壳的 clearWorking 摘类,所以有个一两帧的窗口:
|
|
106
|
+
不给它整行声明,那个窗口里它会被当成头像那一行的第三块。 */
|
|
107
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-acts{flex:0 0 100%}
|
|
108
|
+
|
|
109
|
+
/* ⚠️ 这段皮肤只在**并进消息之后**起作用:`setWorking` 造出来的节点不再单独落画布
|
|
110
|
+
(用户:「不需要这个效果」—— 那条「等待处理… + 蓝圈 + 秒数」的假 bot 行),
|
|
111
|
+
要等这一轮的消息建出来才被 `mountWorking` 插进来。
|
|
112
|
+
文字节点在 `setWorking` 里**根本不建**(用户:「agent运行中还在」)——
|
|
113
|
+
以前靠这里 `display:none` 隐藏,一旦「并进消息」那一步没生效,那串字就照旧露出来。 */
|
|
114
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .working{
|
|
115
|
+
flex:1 1 auto;min-width:0;padding:0;gap:7px;justify-content:flex-end}
|
|
116
|
+
|
|
117
|
+
/* 耗时计时(`.working__d`)保留,它落在这一行的右端,是「没卡死」的佐证;
|
|
118
|
+
状态由左边头像的呼吸表达,不再复述一遍。
|
|
119
|
+
转圈那一枚同样交给头像:并进同一行之后它就多余了;空占位 `.working__act` 一并摘掉。 */
|
|
120
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .working > .working__sp,
|
|
121
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .working > .working__act{display:none}
|
|
122
|
+
|
|
123
|
+
/* 运行中的动画 = 左边 bot 头像**呼吸**(2026-10-20 用户两次追认:「运行中动画设计bot上的
|
|
124
|
+
呼吸动画即可」「呼吸动画要显示在左边bot头像上」)。
|
|
125
|
+
⚠️ 挂在 **svg** 上、不挂 `.qa-av`:`.qa-av` 是那格 22px 的圆角实底方块(`background:var(--subtle)`
|
|
126
|
+
+ `overflow:hidden`),缩放它 = 整个色块在胀缩,看起来像按钮在闪;而里面那张脸
|
|
127
|
+
(中性机器人头,`currentColor` 描线)才是「bot」本身 —— 动它才是表情在动。
|
|
128
|
+
顺带不用再放开 `.qa-av` 的裁剪:脸在方块**里面**放大,裁剪框原样留着即可。 */
|
|
129
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-ico .qa-av > svg{
|
|
130
|
+
animation:qaRunBreath 1.5s ease-in-out infinite;
|
|
131
|
+
transform-origin:50% 50%}
|
|
132
|
+
|
|
133
|
+
/* ⚠️ 宿主若给了 `QA_BOT_AVATAR_SRC`(图片头像),那格是 `<img>` 而不是脸 ——
|
|
134
|
+
同样给它一份呼吸,免得换成图片头像时运行中就完全没动效了。 */
|
|
135
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-ico .qa-av > img{
|
|
136
|
+
animation:qaRunBreath 1.5s ease-in-out infinite;
|
|
137
|
+
transform-origin:50% 50%}
|
|
138
|
+
|
|
139
|
+
@keyframes qaRunBreath{
|
|
140
|
+
0%,100%{transform:scale(1);opacity:1}
|
|
141
|
+
50%{transform:scale(1.08);opacity:.6}}
|
|
142
|
+
|
|
172
143
|
.notice{display:flex;align-items:center;gap:10px;border-radius:14px;background:var(--error-surface);
|
|
173
144
|
border:1.2px solid var(--error-border);padding:14px}
|
|
145
|
+
|
|
174
146
|
.notice__ico{flex:0 0 auto;color:var(--error-icon);display:flex}
|
|
147
|
+
|
|
175
148
|
.notice__t{flex:1;min-width:0;color:var(--error-text);font-size:13.5px;line-height:17px;font-weight:500}
|
|
176
149
|
|
|
177
|
-
/* ── 审批 / 提问卡 ──
|
|
178
|
-
ask 是 headless 下**唯一**能喂答案的通道(core 的 --control answer),
|
|
179
|
-
所以它必须能作答,不能只显示一行提示。 */
|
|
180
|
-
.askcard{background:var(--bubble);border-radius:var(--r-card);padding:14px 15px;
|
|
181
|
-
border-left:3px solid var(--active);display:flex;flex-direction:column;gap:10px;align-items:flex-start}
|
|
182
|
-
.askcard__head{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:600;color:var(--ink)}
|
|
183
|
-
.askcard__dot{width:8px;height:8px;border-radius:var(--r-full);background:var(--active);flex:0 0 auto}
|
|
184
|
-
.askcard__dot--ok{background:var(--accent)}
|
|
185
|
-
.askcard__q{margin:0;font-size:14.5px;line-height:1.55;color:var(--bubble-text)}
|
|
186
|
-
.askcard__opts{display:flex;flex-wrap:wrap;gap:7px}
|
|
187
|
-
.askcard__btn{height:34px;padding:0 14px;border-radius:var(--r-full);border:1px solid var(--border);
|
|
188
|
-
background:var(--raised);color:var(--ink);font-size:13.5px}
|
|
189
|
-
.askcard__btn--primary{background:var(--primary-action);color:var(--on-primary-action);
|
|
190
|
-
border-color:var(--primary-action);font-weight:600}
|
|
191
|
-
.askcard__opt{display:flex;flex-direction:column;gap:2px;padding:9px 13px;border-radius:var(--r);
|
|
192
|
-
border:1px solid var(--border);background:var(--raised);text-align:left;max-width:100%}
|
|
193
|
-
.askcard__opt b{font-size:13.5px;color:var(--ink)}
|
|
194
|
-
.askcard__opt span{font-size:12px;color:var(--muted);line-height:1.45}
|
|
195
|
-
|
|
196
|
-
/* ── 输入区 Composer ── */
|
|
197
150
|
.composer{flex:0 0 auto;padding:0 clamp(12px,4vw,20px) 16px}
|
|
151
|
+
|
|
198
152
|
.composer__box{max-width:860px;margin:0 auto;border:1px solid var(--border);border-radius:var(--r-card);
|
|
199
153
|
background:var(--raised);display:flex;flex-direction:column;gap:2px;padding:10px 12px 8px}
|
|
154
|
+
|
|
200
155
|
.composer__box:focus-within{border-color:var(--accent)}
|
|
156
|
+
|
|
201
157
|
.composer textarea{display:block;width:100%;border:0;outline:0;resize:none;background:none;
|
|
202
158
|
color:var(--ink);font:16px/24px var(--font);min-height:24px;max-height:200px;padding:2px}
|
|
159
|
+
|
|
203
160
|
.composer__row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
161
|
+
|
|
204
162
|
.composer__left{display:flex;align-items:center;gap:9px;min-width:0;flex:1}
|
|
205
163
|
|
|
206
|
-
/*
|
|
207
|
-
|
|
164
|
+
/* 参数行:引擎 · 模型 + 授权档位,**浮在圆角输入框整个上方**(2026-10-20 用户定稿 B 方案)。
|
|
165
|
+
原来它们与 + / 发送挤在同一行,`.casc` 只能分到 280px 里的一半,引擎名与模型名各被截一半。
|
|
166
|
+
移出框外后:① 整行宽度都是它们的;② 框内只剩输入与动作,不再有换行。
|
|
167
|
+
宽度/居中与 .composer__box 逐值对齐 —— 同在 .composer 里,不对齐就会左边缘错开。 */
|
|
168
|
+
.composer__tools{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0;
|
|
169
|
+
max-width:860px;margin:0 auto 8px}
|
|
170
|
+
|
|
171
|
+
.iconbtn{flex:0 0 auto;width:32px;height:32px;border-radius:var(--r-full);display:flex;align-items:center;justify-content:center;background:var(--subtle);color:var(--ink)}
|
|
172
|
+
|
|
173
|
+
.iconbtn:active{background:var(--activity)}
|
|
174
|
+
|
|
175
|
+
.attachbar{display:flex;flex-wrap:wrap;gap:6px;padding:2px 2px 8px;min-width:0}
|
|
176
|
+
|
|
177
|
+
.attachchip{display:inline-flex;align-items:center;gap:6px;max-width:100%;padding:4px 6px 4px 10px;border-radius:var(--r-full);background:var(--subtle);color:var(--ink);font-size:12px}
|
|
178
|
+
|
|
179
|
+
.attachchip__n{max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
180
|
+
|
|
181
|
+
.attachchip__x{color:var(--muted);font-size:12px;padding:0 2px}
|
|
182
|
+
|
|
183
|
+
.attachthumb{position:relative;flex:0 0 auto;width:56px;height:56px;border-radius:var(--r);overflow:hidden;background:var(--subtle)}
|
|
184
|
+
|
|
185
|
+
.attachthumb__img{display:block;width:100%;height:100%;object-fit:cover}
|
|
186
|
+
|
|
187
|
+
.attachthumb__mask{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.45);color:#fff;font-size:11px}
|
|
188
|
+
|
|
189
|
+
.attachthumb__mask--err{background:var(--error-surface);color:var(--error-text)}
|
|
190
|
+
|
|
191
|
+
.attachthumb__x{position:absolute;top:3px;right:3px;width:18px;height:18px;padding:0;border-radius:50%;background:rgba(0,0,0,.55);color:#fff;font-size:11px;line-height:1;display:flex;align-items:center;justify-content:center}
|
|
192
|
+
|
|
193
|
+
.attachgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;padding:2px 2px 10px}
|
|
194
|
+
|
|
195
|
+
.tile{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:16px 8px;border-radius:var(--r);background:var(--subtle);color:var(--ink);font-size:13px}
|
|
196
|
+
|
|
197
|
+
.tile:active{background:var(--activity)}
|
|
198
|
+
|
|
199
|
+
.sheetrow{display:flex;align-items:center;gap:12px;width:100%;padding:14px 6px;font-size:14px;color:var(--ink);text-align:left}
|
|
200
|
+
|
|
201
|
+
.sheetrow__l{flex:1;min-width:0}
|
|
202
|
+
|
|
203
|
+
.sheetrow:active{background:var(--subtle)}
|
|
204
|
+
|
|
208
205
|
.permchip{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 11px;
|
|
209
206
|
border-radius:var(--r-full);border:1px solid var(--border);background:var(--subtle);
|
|
210
207
|
color:var(--ink-soft);font-size:12.5px;max-width:190px;white-space:nowrap;overflow:hidden}
|
|
208
|
+
|
|
211
209
|
.permchip:disabled{opacity:.45}
|
|
210
|
+
|
|
212
211
|
.permchip[data-tier="full"]{background:var(--error-surface);border-color:var(--error-border);
|
|
213
212
|
color:var(--error-text);font-weight:600}
|
|
214
213
|
|
|
215
|
-
/* 引擎 ·
|
|
214
|
+
/* 引擎 · 模型胶囊:独占 .composer__tools 一行后**不再有 280px 硬上限**,吃满可用宽度
|
|
215
|
+
(上限只留作兜底,防止超长模型名把整行撑破)。引擎名不参与收缩 —— 短且必须看得见;
|
|
216
|
+
需要让位时只截模型名,语义损失最小。 */
|
|
216
217
|
.casc{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 11px;
|
|
217
218
|
border-radius:var(--r-full);border:1px solid var(--border);background:var(--subtle);
|
|
218
|
-
font-size:12.5px;max-width:
|
|
219
|
+
font-size:12.5px;max-width:100%;min-width:0}
|
|
220
|
+
|
|
219
221
|
.casc__p,.casc__s{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
222
|
+
|
|
223
|
+
.casc__p{flex:none}
|
|
224
|
+
|
|
220
225
|
.casc__sep{color:var(--muted)}
|
|
221
|
-
|
|
226
|
+
|
|
227
|
+
/* ── 「生成中」提示行:合并后那枚胶囊(.casc)**下面**的一行 ──────────────
|
|
228
|
+
2026-10-20 用户:「两个胶囊合并成1个 在下面显示生成中效果即可」——
|
|
229
|
+
画布底部内核那枚「引擎 · 模型 · 生成中」胶囊(.qa-run)与输入区上方那枚
|
|
230
|
+
「引擎 · 模型」胶囊(.casc)显示的是**同一串字**、上下挨着,像同一件事说了两遍。
|
|
231
|
+
本壳的做法:画布那枚不画(见下面 .conv__thread .qa-run),「这一轮在跑」
|
|
232
|
+
改由 .casc 下面这一行小字表达(转圈 + 「生成中」)。
|
|
233
|
+
⚠️ 宽度/居中与 .composer__tools 逐值对齐 —— 左边缘与那枚胶囊同一条线。 */
|
|
234
|
+
.composer__gen{display:none;align-items:center;gap:6px;max-width:860px;margin:0 auto 8px;
|
|
235
|
+
font-size:11.5px;line-height:16px;color:var(--muted)}
|
|
236
|
+
.composer__gen.on{display:flex}
|
|
237
|
+
.composer__gen__sp{width:11px;height:11px;flex:none;border-radius:50%;
|
|
238
|
+
border:1.5px solid var(--faint);border-top-color:var(--muted);
|
|
239
|
+
animation:work-spin .8s linear infinite}
|
|
240
|
+
|
|
241
|
+
/* 内核那枚「引擎 · 模型 · 生成中」胶囊:组件这条线上**不画**。
|
|
242
|
+
理由与 .composer__gen 那一段相同(同一串字说了两遍)—— 它在**画布**里,
|
|
243
|
+
而画布里另有本壳自己的运行态表达(并进 bot 头像那一行的呼吸 + 计时)。
|
|
244
|
+
⚠️ 引擎 · 模型的真值仍在 .casc 上(降级换模型时 'start' 事件改写它),
|
|
245
|
+
所以不丢信息;PC 工作台那条线用的是自己的皮肤(client-ui/pc/app.css),
|
|
246
|
+
不引本文件 → 那边的胶囊照旧(那边画布里没有 .casc,两处不重复)。 */
|
|
247
|
+
.conv__thread .qa-run{display:none}
|
|
222
248
|
|
|
223
249
|
.btn{height:32px;padding:0 13px;border-radius:var(--r-full);border:1px solid var(--border);
|
|
224
250
|
background:var(--raised);color:var(--ink);font-size:13px;display:inline-flex;align-items:center;gap:6px}
|
|
251
|
+
|
|
225
252
|
.btn:disabled{opacity:.45;cursor:not-allowed}
|
|
253
|
+
|
|
226
254
|
.sendbtn{width:36px;height:36px;border-radius:var(--r-full);background:var(--primary-action);
|
|
227
255
|
color:var(--on-primary-action);display:flex;align-items:center;justify-content:center;flex:0 0 auto}
|
|
256
|
+
|
|
228
257
|
.sendbtn:active{transform:scale(.94)}
|
|
258
|
+
|
|
229
259
|
.sendbtn--stop{background:var(--error-surface);color:var(--error-text);
|
|
230
260
|
border:1px solid var(--error-border)}
|
|
231
261
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
.
|
|
262
|
+
.pick-scrim{position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,.42);animation:pick-fade .16s ease}
|
|
263
|
+
|
|
264
|
+
.pick{position:fixed;left:0;right:0;bottom:0;z-index:1001;display:none;flex-direction:column;
|
|
265
|
+
max-height:min(72vh,72dvh,560px);border:1px solid var(--border);border-bottom:0;
|
|
266
|
+
border-radius:20px 20px 0 0;background:var(--raised);
|
|
267
|
+
box-shadow:0 -14px 40px -12px var(--app-shadow);animation:pick-up .2s ease}
|
|
268
|
+
|
|
269
|
+
.pick--on{display:flex}
|
|
270
|
+
|
|
271
|
+
.pick__grab{flex:0 0 auto;width:36px;height:4px;border-radius:999px;background:var(--border);margin:6px auto 2px}
|
|
272
|
+
|
|
273
|
+
.pick__head{flex:0 0 auto;display:flex;align-items:center;gap:8px;padding:4px 6px 4px 14px}
|
|
274
|
+
|
|
275
|
+
.pick__title{flex:1;min-width:0;font-size:13px;font-weight:700;color:var(--ink);
|
|
276
|
+
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
277
|
+
|
|
278
|
+
.pick__x{flex:0 0 auto;width:32px;height:32px;border-radius:var(--r-full);color:var(--muted);
|
|
279
|
+
display:flex;align-items:center;justify-content:center}
|
|
280
|
+
|
|
281
|
+
.pick__x:hover{background:var(--subtle)}
|
|
282
|
+
|
|
283
|
+
.pick__body{flex:1;min-height:0;overflow-y:auto;padding:2px 10px calc(16px + env(safe-area-inset-bottom))}
|
|
284
|
+
|
|
285
|
+
.pick__cap{padding:10px 9px 4px;font-size:11px;font-weight:700;letter-spacing:.04em;color:var(--muted)}
|
|
286
|
+
|
|
287
|
+
.pick__chev{flex:0 0 auto;color:var(--faint);font-size:16px;line-height:1}
|
|
288
|
+
|
|
289
|
+
.pick__cols{display:flex;gap:0;height:min(56vh,56dvh,440px);border:1px solid var(--border);
|
|
290
|
+
border-radius:var(--r);overflow:hidden}
|
|
291
|
+
|
|
292
|
+
.pick__col{flex:1;min-width:0;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:4px}
|
|
293
|
+
|
|
294
|
+
.pick__col--r{flex:1.5;border-left:1px solid var(--border)}
|
|
295
|
+
|
|
296
|
+
.pick__citem{display:flex;flex-direction:column;gap:1px;width:100%;padding:9px 10px;
|
|
297
|
+
border-radius:var(--r);text-align:left}
|
|
298
|
+
|
|
299
|
+
.pick__citem.is-on{background:var(--activity)}
|
|
300
|
+
|
|
301
|
+
.pick__cl{font-size:14px;line-height:20px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
302
|
+
|
|
303
|
+
.pick__citem.is-on .pick__cl{font-weight:600}
|
|
304
|
+
|
|
305
|
+
.pick__cd{font-size:11px;line-height:15px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
306
|
+
|
|
307
|
+
@keyframes pick-fade{from{opacity:0}to{opacity:1}}
|
|
308
|
+
|
|
309
|
+
@keyframes pick-up{from{transform:translateY(12px);opacity:.6}to{transform:none;opacity:1}}
|
|
310
|
+
|
|
311
|
+
.pick__back{display:flex;align-items:center;gap:8px;width:100%;padding:8px 9px;margin-bottom:2px;
|
|
312
|
+
border-radius:var(--r);text-align:left;font-size:12px;font-weight:700;color:var(--ink)}
|
|
313
|
+
|
|
314
|
+
.pick__back:hover{background:var(--subtle)}
|
|
315
|
+
|
|
316
|
+
.pick__back__a{color:var(--muted)}
|
|
317
|
+
|
|
240
318
|
.pick__row{width:100%;display:flex;align-items:center;gap:9px;padding:8px 9px;
|
|
241
319
|
border-radius:var(--r);text-align:left;font-size:13.5px;color:var(--ink)}
|
|
320
|
+
|
|
242
321
|
.pick__row:hover{background:var(--subtle)}
|
|
322
|
+
|
|
243
323
|
.pick__row[aria-selected="true"]{font-weight:600}
|
|
324
|
+
|
|
244
325
|
.pick__row:disabled{opacity:.5;cursor:not-allowed}
|
|
326
|
+
|
|
245
327
|
.pick__ck{width:14px;flex:0 0 auto;color:var(--accent)}
|
|
328
|
+
|
|
246
329
|
.pick__l{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
330
|
+
|
|
247
331
|
.pick__d{font-size:11.5px;color:var(--muted);line-height:1.4}
|
|
248
|
-
.pick__danger{color:var(--error-text)}
|
|
249
|
-
.pick__danger:hover{background:var(--error-surface)}
|
|
250
332
|
|
|
251
|
-
/* ── 空态 ── */
|
|
252
333
|
.conv__empty{margin:auto;padding:28px 20px;text-align:center;color:var(--muted);font-size:14px;line-height:1.7}
|
|
253
334
|
|
|
335
|
+
/* 画布顶部「加载更早」入口:画布第一个孩子(见 conversation.js 的 moreBtn)。
|
|
336
|
+
画布有下边距,这里只留一点空隙,别把第一条消息挤下去。 */
|
|
337
|
+
.conv__more{flex:0 0 auto;margin:2px auto 0;padding:6px 14px;border:0;border-radius:999px;background:var(--subtle);color:var(--muted);font:inherit;font-size:12.5px;line-height:1.4;cursor:pointer}
|
|
338
|
+
.conv__more:hover{color:var(--ink)}
|
|
339
|
+
.conv__more[hidden]{display:none}
|
|
340
|
+
.conv__more--wait{cursor:default;opacity:.7}
|
|
341
|
+
|
|
342
|
+
/* 「正在加载更早的消息…」那一拍也转一枚小环:翻页同样是等网络,
|
|
343
|
+
只有一个静态句子会被读成「按钮坏了」。 */
|
|
344
|
+
.conv__more--wait::before{content:"";display:inline-block;width:9px;height:9px;margin-right:6px;
|
|
345
|
+
vertical-align:-1px;border-radius:50%;border:1.5px solid currentColor;border-top-color:transparent;
|
|
346
|
+
animation:convHistSpin .8s linear infinite}
|
|
347
|
+
|
|
348
|
+
@keyframes convHistSpin{to{transform:rotate(360deg)}}
|
|
349
|
+
|
|
350
|
+
/* ── 读历史时的页面 loading(2026-10-20 用户:「要页面 loading 的水平垂直居中的那种效果」→
|
|
351
|
+
「要撑满宽高」→「要更有创意 使用掌天瓶填充绿液作为加载效果」)──
|
|
352
|
+
从点开会话到第一屏画出来是纯等网络(反向分页要往返 1~3 次),此前这段画布是空的,
|
|
353
|
+
用户分不清「正在读」和「这条会话没内容」。
|
|
354
|
+
形态:**整块画布正中一枚掌天瓶,绿液缓缓涨落**(瓶身见 conversation.js 的 bottleSVG)。
|
|
355
|
+
绿液是**不确定进度**(不知道还差几页),所以无限涨落、不假装百分比 —— 假装成进度条
|
|
356
|
+
反而会让「读得慢」看起来像「卡在 90%」。
|
|
357
|
+
⚠️ 只动 transform / opacity:回放超大日志会把主线程占死,动 layout 的属性会让这一屏
|
|
358
|
+
跟着冻住 —— 那正是「看起来卡在读取」的来源(同 magic-test 那份 qa-hist-loading 的教训)。
|
|
359
|
+
⚠️ 节点由 conversation.js 的 histLoadingNode 建,类名与这里成对,改名要两边一起改。 */
|
|
360
|
+
.conv__loading{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;
|
|
361
|
+
align-items:center;justify-content:center;gap:16px;
|
|
362
|
+
/* 「撑满宽高」:画布有 20/12/28/12 的内边距,光靠 `flex:1` 只能填满**内边距以内**那块
|
|
363
|
+
(实测 366×582,画布 390×630)。用负边距把这层内边距吃回来,盒子才真正贴到画布的
|
|
364
|
+
四边 —— 四边数值必须与 `.conv__thread` 的 padding 逐位对齐(上 20 / 右 12 / 下 28 / 左 12),
|
|
365
|
+
那边改这里要跟着改,否则会漏出或溢出。 */
|
|
366
|
+
margin:-20px -12px -28px;padding:20px 12px 28px}
|
|
367
|
+
/* 留 `flex:1` 而不是 `position:absolute`:读历史时画布尾部可能还挂着「加载更早」入口
|
|
368
|
+
(首屏还没拿到 `more` 时它是隐的,但用户二次进入可能是亮的)—— 用 flex 参与布局时
|
|
369
|
+
两者不重叠;绝对定位则会把那块入口盖在 loading 底下。 */
|
|
370
|
+
|
|
371
|
+
/* 瓶的光晕:一团绿雾垫在瓶子后面,让「绿」在这一屏有存在感(只动 opacity) */
|
|
372
|
+
.conv__loading__bottle{position:relative;flex:none;width:132px;height:165px}
|
|
373
|
+
|
|
374
|
+
.conv__loading__bottle::before{content:"";position:absolute;left:50%;top:56%;width:180px;height:180px;
|
|
375
|
+
margin:-90px 0 0 -90px;border-radius:50%;pointer-events:none;
|
|
376
|
+
background:radial-gradient(circle,var(--q-bottle-glow, rgba(52,210,123,.30)) 0%,transparent 68%);
|
|
377
|
+
animation:qbBottleGlow 2.8s ease-in-out infinite}
|
|
378
|
+
|
|
379
|
+
.conv__loading__bottle .qbottle{display:block;width:132px;height:165px;
|
|
380
|
+
color:var(--q-bottle-line, var(--muted))}
|
|
381
|
+
|
|
382
|
+
.conv__loading__label{font-size:13.5px;line-height:1.5;color:var(--muted);text-align:center}
|
|
383
|
+
|
|
384
|
+
@keyframes qbBottleGlow{0%,100%{opacity:.45;transform:scale(.94)}50%{opacity:.9;transform:scale(1.04)}}
|
|
385
|
+
|
|
386
|
+
/* ══════════ 掌天瓶:绿液本体(conversation.js 的 bottleSVG 产出的 DOM)══════════
|
|
387
|
+
液面位置已经**烘进 path 的 d**(脚本按 `fill` 算好顶边 y),这里只叠加动作:
|
|
388
|
+
涨液做 `translateY`、波浪做 `translateX`、气泡做位移 —— 全是 transform,
|
|
389
|
+
主线程被回放占住时也不会触发重排。
|
|
390
|
+
⚠️ 波浪的横移距离必须**等于一个波长**(bottleSVG 里 `q24 …48` = 48),否则循环处会跳一下。 */
|
|
391
|
+
.qbottle__wave{animation:qbWaveX 3.2s linear infinite}
|
|
392
|
+
|
|
393
|
+
/* 气泡:在腹部深处升起、升到液面之下就淡出(循环)。三个错开相位 = 一直在冒泡。
|
|
394
|
+
位移上限 16px 是配合 bottleSVG 里气泡的坐标(y 94→78):最浅也还在液面(y≈88)以下,
|
|
395
|
+
不会出现「气泡跑到空气里」。 */
|
|
396
|
+
.qbottle__bubble{fill:#EAFFF2;opacity:0;animation:qbBubble 3s ease-in infinite}
|
|
397
|
+
.qbottle__bubble--b{animation-duration:3.7s;animation-delay:.9s}
|
|
398
|
+
.qbottle__bubble--c{animation-duration:4.3s;animation-delay:1.8s}
|
|
399
|
+
|
|
400
|
+
/* 涨落:整块液体上下推 ±8px。看着是「液面在呼吸」,而不是「瓶子在倒来倒去」。 */
|
|
401
|
+
.qbottle--hist .qbottle__liquid{animation:qbRise 3.6s ease-in-out infinite}
|
|
402
|
+
|
|
403
|
+
@keyframes qbWaveX{0%{transform:translateX(0)}100%{transform:translateX(-48px)}}
|
|
404
|
+
@keyframes qbBubble{
|
|
405
|
+
0%{transform:translateY(0) scale(.7);opacity:0}
|
|
406
|
+
20%{opacity:.8}
|
|
407
|
+
70%{opacity:.55}
|
|
408
|
+
100%{transform:translateY(-16px) scale(1);opacity:0}}
|
|
409
|
+
@keyframes qbRise{
|
|
410
|
+
0%,100%{transform:translateY(8px)}
|
|
411
|
+
50%{transform:translateY(-8px)}}
|
|
412
|
+
|
|
413
|
+
/* 一次性灌满(App 启动闪屏用):从空瓶灌到满,停住不回退 —— 启动是**有终点**的,
|
|
414
|
+
反复涨落会让人以为「还在启动」。`forwards` 让它停在满瓶那一刻。
|
|
415
|
+
⚠️ 曲线必须**前快后缓**(ease-out):试过 ease-in-out,前 300ms 几乎不动,
|
|
416
|
+
用户看到的是一枚空瓶子在发光 —— 第一眼没有「绿」就白做了这一屏。 */
|
|
417
|
+
.qbottle--rise .qbottle__liquid{animation:qbFill .95s cubic-bezier(.16,.72,.28,1) forwards}
|
|
418
|
+
|
|
419
|
+
@keyframes qbFill{
|
|
420
|
+
0%{transform:translateY(84px)}
|
|
421
|
+
100%{transform:translateY(0)}}
|
|
422
|
+
|
|
254
423
|
@media (prefers-reduced-motion:reduce){
|
|
255
424
|
*,*::before,*::after{animation:none!important;transition:none!important}
|
|
425
|
+
/* ⚠️ 运行中的呼吸要留一份(2026-10-20 用户:「动画效果没有」):文字已删,那张脸
|
|
426
|
+
就是**唯一**的状态提示 —— 上面那条通用规则会把它整条关掉,系统开了「减弱动态效果」
|
|
427
|
+
的设备(iOS / macOS 常见)上运行中就是一张完全静止的脸。
|
|
428
|
+
这里退一步只改**透明度**、不做缩放(前庭刺激主要来自位移与缩放),慢一点也够读。 */
|
|
429
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-ico .qa-av > svg,
|
|
430
|
+
.qa-msg.qa-runlive:not(.qa-mine):not(.qa-sim) > .qa-ico .qa-av > img{
|
|
431
|
+
animation:qaRunBreathSoft 1.6s ease-in-out infinite!important}
|
|
256
432
|
}
|
|
433
|
+
|
|
434
|
+
@keyframes qaRunBreathSoft{0%,100%{opacity:1}50%{opacity:.45}}
|