wezard 1.4.1 → 2.0.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/CLAUDE.md +12 -7
- package/README.md +227 -112
- package/cli/wezard.sh +6 -3
- package/dist/daemon/approval.js +9 -3
- package/dist/daemon/approval.js.map +1 -1
- package/dist/daemon/cc-bridge.js.map +1 -1
- package/dist/daemon/chat-name.js +8 -14
- package/dist/daemon/chat-name.js.map +1 -1
- package/dist/daemon/detail.js +94 -9
- package/dist/daemon/detail.js.map +1 -1
- package/dist/daemon/graph.js +5 -7
- package/dist/daemon/graph.js.map +1 -1
- package/dist/daemon/inbound.js +135 -158
- package/dist/daemon/inbound.js.map +1 -1
- package/dist/daemon/index.js +452 -156
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/jobs.js +2 -2
- package/dist/daemon/mirror-bridge.js +284 -140
- package/dist/daemon/mirror-bridge.js.map +1 -1
- package/dist/daemon/mirror-store.js.map +1 -1
- package/dist/daemon/model-select.js +74 -0
- package/dist/daemon/model-select.js.map +1 -0
- package/dist/daemon/peers.js +10 -16
- package/dist/daemon/peers.js.map +1 -1
- package/dist/daemon/spawn-tmux.js +27 -4
- package/dist/daemon/spawn-tmux.js.map +1 -1
- package/dist/daemon/task-gate.js +97 -0
- package/dist/daemon/task-gate.js.map +1 -0
- package/dist/daemon/task-registry.js +132 -0
- package/dist/daemon/task-registry.js.map +1 -0
- package/dist/daemon/tasks.js +126 -54
- package/dist/daemon/tasks.js.map +1 -1
- package/dist/daemon/wizard-memory.js +57 -0
- package/dist/daemon/wizard-memory.js.map +1 -0
- package/dist/daemon/wizard.js +84 -36
- package/dist/daemon/wizard.js.map +1 -1
- package/dist/mcp/server.js +138 -76
- package/dist/mcp/server.js.map +1 -1
- package/dist/shared/chat-http.js +193 -96
- package/dist/shared/chat-http.js.map +1 -1
- package/dist/shared/chat-render.js +31 -24
- package/dist/shared/chat-render.js.map +1 -1
- package/dist/shared/chat-view.js +14 -2
- package/dist/shared/chat-view.js.map +1 -1
- package/dist/shared/config.js +23 -4
- package/dist/shared/config.js.map +1 -1
- package/dist/shared/detail-render.js +14 -7
- package/dist/shared/detail-render.js.map +1 -1
- package/dist/shared/detail-store.js +12 -5
- package/dist/shared/detail-store.js.map +1 -1
- package/dist/shared/role-render.js +52 -0
- package/dist/shared/role-render.js.map +1 -0
- package/dist/shared/role-view.js +210 -0
- package/dist/shared/role-view.js.map +1 -0
- package/dist/shared/schedule-spec.js +92 -116
- package/dist/shared/schedule-spec.js.map +1 -1
- package/dist/shared/session-label.js +103 -72
- package/dist/shared/session-label.js.map +1 -1
- package/dist/shared/std.js +20 -0
- package/dist/shared/std.js.map +1 -1
- package/dist/shared/task-file.js +120 -0
- package/dist/shared/task-file.js.map +1 -0
- package/dist/shared/trigger.js +232 -0
- package/dist/shared/trigger.js.map +1 -0
- package/dist/shared/world.js +193 -0
- package/dist/shared/world.js.map +1 -0
- package/dist/svr/index.js +68 -3
- package/dist/svr/index.js.map +1 -1
- package/package.json +1 -1
- package/web/chat.css +609 -147
- package/web/chat.js +1274 -215
package/web/chat.css
CHANGED
|
@@ -1,140 +1,293 @@
|
|
|
1
|
-
/*
|
|
2
|
-
由 /chat/app.css 拼在本文件之前 ——
|
|
1
|
+
/* Rolepage 外壳。消息正文 (turn 卡片) 的样式来自 detail-render 的 SHARED_CSS/TURN_CSS,
|
|
2
|
+
由 /chat/app.css 拼在本文件之前 —— 这里管骨架、会话列表、左右气泡与页脚总账。
|
|
3
|
+
视觉只押一处: 「视角」。我说的靠右、染成视角色; 别人说的靠左、带头像; 气泡对面
|
|
4
|
+
那片空白是一道门 —— 点下去就换成对方的视角。其余一律安静。 */
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
--paper: #eef1f5; /* 对话底 —— 冷灰, 让白色与视角色的两种气泡都站得住 */
|
|
8
|
+
--card: #fff;
|
|
9
|
+
--ink: #1c2430;
|
|
10
|
+
--mute: #6a7483;
|
|
11
|
+
--faint: #9aa3af;
|
|
12
|
+
--line: #d9dee6;
|
|
13
|
+
--mine: #eef3fd; /* 当前视角说的话 */
|
|
14
|
+
--mine-soft: #e2eafa; /* 它内部还要再沉一档的块 (代码/结果/折叠头) */
|
|
15
|
+
--mine-line: #c9d7f3;
|
|
16
|
+
--accent: #2f55c9; /* 视角色: 名字的点、选中态、自己的气泡描边 */
|
|
17
|
+
--priv: #9a5b10; /* 私聊 */
|
|
18
|
+
--pub: #237a55; /* 公开 */
|
|
19
|
+
}
|
|
3
20
|
|
|
4
21
|
html, body { height: 100% }
|
|
5
22
|
/* overscroll-behavior:none 阻止整页橡皮筋回弹 —— webview 里它会和内层
|
|
6
23
|
.thread 抢滚动手势, 表现为"滚一下卡住/整页在弹"。 */
|
|
7
|
-
body { overflow: hidden; overscroll-behavior: none }
|
|
24
|
+
body { overflow: hidden; overscroll-behavior: none; color: var(--ink) }
|
|
25
|
+
button { font: inherit; color: inherit }
|
|
26
|
+
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px }
|
|
8
27
|
|
|
9
28
|
.app { display: flex; height: 100vh; height: 100dvh }
|
|
29
|
+
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }
|
|
30
|
+
|
|
31
|
+
/* 名字 = 地址。点是地址的一部分, 也是这一页唯一的颜色记号。 */
|
|
32
|
+
.nm { font-weight: 600 }
|
|
33
|
+
.nm::before { content: "."; color: var(--accent); font-weight: 700 }
|
|
34
|
+
.nm.human::before, .nm.task::before, .nm.chat::before { content: none }
|
|
10
35
|
|
|
11
|
-
/* ── 侧栏:
|
|
36
|
+
/* ── 侧栏: 我 + 会话 ─────────────────────────────────────────────── */
|
|
12
37
|
.side {
|
|
13
|
-
width:
|
|
38
|
+
width: 312px; flex: none; background: var(--card); border-right: 1px solid var(--line);
|
|
14
39
|
display: flex; flex-direction: column; min-height: 0;
|
|
15
40
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
font-variant-numeric: tabular-nums;
|
|
41
|
+
|
|
42
|
+
/* ── 左栏顶: 当前 role 的名片 (身份 + 关系/日程入口) ──── */
|
|
43
|
+
.rolebar {
|
|
44
|
+
position: relative; flex: none; padding: 16px 16px 14px; border-bottom: 1px solid var(--line);
|
|
45
|
+
display: flex; flex-direction: column; gap: 8px; background: var(--card);
|
|
22
46
|
}
|
|
23
|
-
.
|
|
24
|
-
|
|
25
|
-
|
|
47
|
+
.rolebar .conn { position: absolute; top: 12px; right: 14px }
|
|
48
|
+
.rolebar .id { display: flex; gap: 10px; align-items: center; min-width: 0; padding-right: 14px }
|
|
49
|
+
.rolebar .id .av { font-size: 26px; line-height: 1; flex: none }
|
|
50
|
+
.rolebar .id .l { min-width: 0; display: flex; flex-direction: column; gap: 2px }
|
|
51
|
+
.rolebar .id .nm { font-size: 18px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
52
|
+
.rolebar .id .k { font-size: 11.5px; color: var(--mute) }
|
|
53
|
+
.rolebar .job { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--ink); opacity: .8 }
|
|
54
|
+
.rolebar .facts { display: flex; gap: 6px 12px; flex-wrap: wrap; font-size: 11.5px; color: var(--mute) }
|
|
55
|
+
.rolebar .facts span { white-space: nowrap }
|
|
56
|
+
.rolebar .facts .go { cursor: pointer; color: var(--accent) }
|
|
57
|
+
.rolebar .facts .go:hover { text-decoration: underline }
|
|
58
|
+
.rolebar .acts { display: flex; gap: 6px; flex-wrap: wrap; align-items: center }
|
|
59
|
+
.rolebar .acts:empty { display: none }
|
|
60
|
+
/* session 切换: 按钮 + 浮层列表, 取代原生 <select>。 */
|
|
61
|
+
.sp { position: relative; flex: 1 1 100%; min-width: 0 }
|
|
62
|
+
.sp-btn {
|
|
63
|
+
width: 100%; display: flex; align-items: center; gap: 8px; cursor: pointer; text-align: left;
|
|
64
|
+
font-size: 12px; background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: 5px 10px;
|
|
65
|
+
transition: border-color .12s, box-shadow .12s;
|
|
26
66
|
}
|
|
67
|
+
.sp-btn:hover, .sp.open .sp-btn { border-color: var(--accent) }
|
|
68
|
+
.sp.open .sp-btn { box-shadow: 0 0 0 3px #2f55c91f }
|
|
69
|
+
.sp-btn .lb { font-size: 10px; color: var(--faint); text-transform: uppercase; letter-spacing: .4px; flex: none }
|
|
70
|
+
.sp-btn .v { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums }
|
|
71
|
+
.sp-btn .car {
|
|
72
|
+
flex: none; width: 6px; height: 6px; margin: -3px 2px 0 0; border: solid var(--mute); border-width: 0 1.5px 1.5px 0;
|
|
73
|
+
transform: rotate(45deg); transition: transform .15s;
|
|
74
|
+
}
|
|
75
|
+
.sp.open .sp-btn .car { transform: rotate(-135deg); margin-top: 3px }
|
|
76
|
+
.sp-list {
|
|
77
|
+
position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% + 4px);
|
|
78
|
+
max-height: 280px; overflow-y: auto; overscroll-behavior: contain;
|
|
79
|
+
background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 4px;
|
|
80
|
+
box-shadow: 0 8px 24px #1c24301f, 0 2px 6px #1c24300f;
|
|
81
|
+
opacity: 0; transform: translateY(-4px); pointer-events: none; transition: opacity .12s, transform .12s;
|
|
82
|
+
}
|
|
83
|
+
.sp.open .sp-list { opacity: 1; transform: none; pointer-events: auto }
|
|
84
|
+
.sp-it {
|
|
85
|
+
width: 100%; display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: baseline;
|
|
86
|
+
background: none; border: 0; border-radius: 6px; padding: 6px 8px; cursor: pointer; text-align: left; font-size: 12px;
|
|
87
|
+
}
|
|
88
|
+
.sp-it:hover { background: var(--paper) }
|
|
89
|
+
.sp-it.on { background: var(--mine); color: var(--accent) }
|
|
90
|
+
.sp-it .id { font-family: ui-monospace, Menlo, monospace; font-weight: 600 }
|
|
91
|
+
.sp-it .ago { color: var(--mute) }
|
|
92
|
+
.sp-it .n { color: var(--faint); font-variant-numeric: tabular-nums; grid-column: 3 }
|
|
93
|
+
.sp-it.all { border-bottom: 1px solid var(--line); border-radius: 6px 6px 0 0; margin-bottom: 3px }
|
|
94
|
+
.sp-it.all .id { font-family: inherit }
|
|
27
95
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
padding: 8px 10px 8px 11px; border-radius: 8px; cursor: pointer;
|
|
33
|
-
border: 1px solid transparent; transition: background .12s;
|
|
96
|
+
/* 左栏底: session 切换 + 用量, 都是轮次的事 —— 与顶上的身份分开, 钉在会话列表下面。 */
|
|
97
|
+
.sidefoot {
|
|
98
|
+
flex: none; border-top: 1px solid var(--line); padding: 10px 12px 12px;
|
|
99
|
+
display: flex; flex-direction: column; gap: 8px; background: var(--paper);
|
|
34
100
|
}
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.
|
|
39
|
-
|
|
40
|
-
|
|
101
|
+
.sidefoot[hidden] { display: none }
|
|
102
|
+
#rb-sess:empty, #rb-prof:empty { display: none }
|
|
103
|
+
/* 在底部, 浮层往上开。 */
|
|
104
|
+
.sidefoot .sp-list { top: auto; bottom: calc(100% + 4px); transform: translateY(4px) }
|
|
105
|
+
.sidefoot .sp.open .sp-list { transform: none }
|
|
106
|
+
|
|
107
|
+
/* profile: 这个 role 自己跑过的轮次的总账 —— 平铺在左栏底, 不套卡片。 */
|
|
108
|
+
.prof { font-family: ui-monospace, Menlo, monospace }
|
|
109
|
+
.prof .hd { display: flex; align-items: center; gap: 8px; font-size: 11.5px; margin-bottom: 6px }
|
|
110
|
+
.prof .hd .lb { font-size: 10px; color: var(--faint); text-transform: uppercase; letter-spacing: .4px; font-family: system-ui, sans-serif }
|
|
111
|
+
.prof .model {
|
|
112
|
+
margin-left: auto; color: var(--mute);
|
|
113
|
+
max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
41
114
|
}
|
|
115
|
+
.prof .grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px 4px }
|
|
116
|
+
.prof .kv { display: flex; flex-direction: column; align-items: flex-start; min-width: 0 }
|
|
117
|
+
.prof .kv .v { font-size: 13px; font-weight: 700; color: #1f2328; font-variant-numeric: tabular-nums; white-space: nowrap }
|
|
118
|
+
.prof .kv .k { font-size: 9.5px; color: #8c959f; text-transform: uppercase; letter-spacing: .4px }
|
|
119
|
+
.prof .io { margin-top: 8px; display: flex; flex-direction: column; gap: 6px }
|
|
120
|
+
.prof .bar { display: flex; height: 6px; border-radius: 3px; overflow: hidden; background: #eaeef2 }
|
|
121
|
+
.prof .bar .seg { height: 100% }
|
|
122
|
+
.prof .leg { display: flex; gap: 4px 9px; flex-wrap: wrap }
|
|
123
|
+
.prof .lg { display: flex; align-items: center; gap: 4px; font-size: 10.5px; color: #8c959f }
|
|
124
|
+
.prof .lg i { width: 8px; height: 8px; border-radius: 2px; flex: none }
|
|
125
|
+
.prof .lg b { color: #1f2328; font-weight: 700; font-variant-numeric: tabular-nums }
|
|
126
|
+
.conn { display: flex; align-items: center; gap: 5px }
|
|
127
|
+
.conn .d { width: 7px; height: 7px; border-radius: 50%; background: #1a7f37 }
|
|
128
|
+
.conn.off .d { background: #cf222e }
|
|
42
129
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
display:
|
|
46
|
-
|
|
130
|
+
/* 群聊头像 = 参与者头像拼图: 1 格居中, 2 格左右, 3/4 格田字。 */
|
|
131
|
+
.mosaic {
|
|
132
|
+
display: grid !important; width: 34px; height: 34px; border-radius: 10px; overflow: hidden; flex: none;
|
|
133
|
+
background: #eef1f6; border: 1px solid var(--line); gap: 1px; padding: 1px;
|
|
134
|
+
font-size: 13px !important; line-height: 1 !important;
|
|
47
135
|
}
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
136
|
+
.mosaic i { font-style: normal; display: grid; place-items: center; background: #fff; min-width: 0; min-height: 0 }
|
|
137
|
+
.mosaic.n1 { grid-template: 1fr / 1fr; font-size: 20px !important }
|
|
138
|
+
.mosaic.n2 { grid-template: 1fr / 1fr 1fr; font-size: 14px !important }
|
|
139
|
+
.mosaic.n3, .mosaic.n4 { grid-template: 1fr 1fr / 1fr 1fr }
|
|
140
|
+
.mosaic.n3 i:first-child { grid-row: span 2 }
|
|
141
|
+
|
|
142
|
+
.convs { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 8px 16px }
|
|
143
|
+
.convs h2 {
|
|
144
|
+
margin: 14px 8px 4px; font-size: 12px; font-weight: 600; color: var(--mute);
|
|
145
|
+
display: flex; align-items: baseline; gap: 6px;
|
|
52
146
|
}
|
|
53
|
-
|
|
147
|
+
.convs h2 span { font-weight: 400; color: var(--faint); font-variant-numeric: tabular-nums }
|
|
148
|
+
.convs .none { padding: 6px 10px; font-size: 12px; color: var(--faint) }
|
|
54
149
|
|
|
55
|
-
.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden;
|
|
59
|
-
text-overflow: ellipsis; font-family: ui-monospace, Menlo, monospace;
|
|
150
|
+
.ci {
|
|
151
|
+
display: flex; gap: 10px; align-items: flex-start; width: 100%; text-align: left; overflow: hidden;
|
|
152
|
+
padding: 8px 10px; border: 0; border-radius: 9px; background: transparent; cursor: pointer;
|
|
60
153
|
}
|
|
61
|
-
.
|
|
62
|
-
.
|
|
63
|
-
|
|
64
|
-
|
|
154
|
+
.ci:hover { background: #f4f6f9 }
|
|
155
|
+
.ci.on { background: #eaf0fc }
|
|
156
|
+
.ci { align-items: center }
|
|
157
|
+
.ci > .av { font-size: 22px; line-height: 1; flex: none; width: 34px; text-align: center }
|
|
158
|
+
.ci .b { min-width: 0; flex: 1; overflow: hidden }
|
|
159
|
+
.ci .pv, .ci .l1 { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
160
|
+
.ci .l1 { display: flex }
|
|
161
|
+
.ci .l1 { display: flex; align-items: baseline; gap: 6px }
|
|
162
|
+
.ci .t { font-size: 13.5px; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
163
|
+
.ci .ts { margin-left: auto; flex: none; font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums }
|
|
164
|
+
.ci .pv { font-size: 12px; color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
165
|
+
.ci .pv:empty { display: none }
|
|
166
|
+
.ci .tag { font-size: 10.5px; border-radius: 4px; padding: 0 4px; flex: none }
|
|
167
|
+
.ci .tag.priv { color: var(--priv); background: #9a5b1014 }
|
|
168
|
+
|
|
169
|
+
/* 群里的其他 role —— 缩进挂在群下, 点开是「我与它在这个群里」的往来。 */
|
|
170
|
+
.subs { margin: 0 0 6px 34px; border-left: 1px solid var(--line); padding-left: 6px }
|
|
171
|
+
.si {
|
|
172
|
+
display: flex; align-items: center; gap: 7px; width: 100%; text-align: left;
|
|
173
|
+
padding: 4px 8px; border: 0; border-radius: 7px; background: transparent; cursor: pointer;
|
|
174
|
+
font-size: 12.5px; color: var(--ink);
|
|
65
175
|
}
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
176
|
+
.si:hover { background: #f4f6f9 }
|
|
177
|
+
.si.on { background: #eaf0fc; color: var(--accent) }
|
|
178
|
+
.si .n { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
179
|
+
.si .c { margin-left: auto; font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums }
|
|
180
|
+
.si.quiet { color: var(--faint) }
|
|
181
|
+
|
|
182
|
+
/* ── 右栏头: 这个群聊 / 私聊是什么 ───────────────────────────────── */
|
|
183
|
+
.main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; background: var(--paper) }
|
|
184
|
+
.chathead {
|
|
185
|
+
flex: none; display: flex; align-items: center; gap: 12px; padding: 10px 20px; min-height: 58px;
|
|
186
|
+
background: var(--card); border-bottom: 1px solid var(--line);
|
|
70
187
|
}
|
|
71
|
-
|
|
72
|
-
.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
.
|
|
78
|
-
.
|
|
79
|
-
.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/* min-height:0 给纵向 flex (mobile) 用: 缺了它 .main 的 min-height:auto 会被
|
|
83
|
-
内容撑破 100dvh, .thread 不产生内部滚动, 底部被 body overflow:hidden 裁掉。
|
|
84
|
-
横向 (桌面) 下无副作用 —— 高度本来就由 stretch 定死。 */
|
|
85
|
-
.main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column }
|
|
86
|
-
.topbar {
|
|
87
|
-
flex: none; display: flex; align-items: center; gap: 10px; padding: 11px 20px;
|
|
88
|
-
background: #fff; border-bottom: 1px solid #d0d7de;
|
|
89
|
-
}
|
|
90
|
-
.topbar .em { font-size: 18px }
|
|
91
|
-
.topbar .h { font-size: 15px; font-weight: 600; font-family: ui-monospace, Menlo, monospace }
|
|
92
|
-
.topbar .cwd {
|
|
93
|
-
flex: none; font-size: 11px; color: #57606a; font-family: ui-monospace, Menlo, monospace;
|
|
94
|
-
background: #f6f8fa; border: 1px solid #eaeef2; border-radius: 5px; padding: 1px 6px;
|
|
95
|
-
max-width: 42%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
96
|
-
}
|
|
97
|
-
.topbar .sub {
|
|
98
|
-
font-size: 11px; color: #8c959f; font-family: ui-monospace, Menlo, monospace;
|
|
99
|
-
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
188
|
+
.chathead .back { display: none; border: 0; background: none; font-size: 22px; color: var(--mute); cursor: pointer; padding: 0 4px }
|
|
189
|
+
.ch-who { display: flex; gap: 11px; align-items: center; min-width: 0; flex: 1 }
|
|
190
|
+
.ch-who > .av:not(.mosaic) { font-size: 26px; line-height: 1; flex: none }
|
|
191
|
+
.ch-who .l { min-width: 0; display: flex; flex-direction: column; gap: 1px }
|
|
192
|
+
.ch-who .t { font-size: 16px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
193
|
+
.ch-who > .t { flex: none }
|
|
194
|
+
.ch-who .sub { font-size: 11.5px; color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
195
|
+
.ch-acts { display: flex; gap: 8px; align-items: center; flex: none; font-size: 12px; color: var(--mute) }
|
|
196
|
+
.vb {
|
|
197
|
+
font-size: 12.5px; background: var(--card); border: 1px solid var(--line); border-radius: 7px;
|
|
198
|
+
padding: 4px 11px; cursor: pointer; white-space: nowrap;
|
|
100
199
|
}
|
|
101
|
-
.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
一条线的流水线不值得画成节点图 —— steps 结构上不可能分叉, 画出来的永远是
|
|
105
|
-
一条直线。这里只把它压成一行: 步骤序列 + 当前停在哪一步 + 轮次进度。 */
|
|
106
|
-
.gbar {
|
|
107
|
-
flex: none; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
|
108
|
-
padding: 7px 20px; background: #fbf9ff; border-bottom: 1px solid #8250df33;
|
|
109
|
-
font-size: 11.5px; color: #6639ba; font-family: ui-monospace, Menlo, monospace;
|
|
110
|
-
}
|
|
111
|
-
.gbar .gid { background: #8250df14; border-radius: 5px; padding: 1px 6px }
|
|
112
|
-
.gbar .pipe { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; min-width: 0 }
|
|
113
|
-
.gbar .nd {
|
|
114
|
-
display: flex; align-items: center; gap: 3px; padding: 1px 7px; border-radius: 10px;
|
|
115
|
-
background: #fff; border: 1px solid #d8cdf0; color: #57606a; cursor: pointer;
|
|
116
|
-
}
|
|
117
|
-
.gbar .nd:hover { border-color: #8250df }
|
|
118
|
-
.gbar .nd.on { background: #8250df; border-color: #8250df; color: #fff; font-weight: 700 }
|
|
119
|
-
.gbar .nd.on.live { animation: pulse 1.4s infinite }
|
|
120
|
-
.gbar .arw { color: #b3a3d6 }
|
|
121
|
-
.gbar .prog { margin-left: auto; font-variant-numeric: tabular-nums }
|
|
122
|
-
.gbar .done { color: #8c959f }
|
|
123
|
-
/* 侧栏里标出"这个会话是 graph 在驱动的" —— 否则它看着就是某个 tag 在自说自话。 */
|
|
124
|
-
.tag-meta .gtag { color: #6639ba }
|
|
200
|
+
.vb:hover { border-color: var(--accent); color: var(--accent) }
|
|
201
|
+
.vb.on { background: var(--accent); border-color: var(--accent); color: #fff }
|
|
202
|
+
.vb b { font-weight: 600; margin-left: 3px; font-variant-numeric: tabular-nums }
|
|
125
203
|
|
|
204
|
+
/* ── 消息 ─────────────────────────────────────────────────────────── */
|
|
126
205
|
.thread {
|
|
127
206
|
flex: 1; min-height: 0; overflow-y: auto; overflow-anchor: none;
|
|
128
|
-
padding: 16px 20px
|
|
129
|
-
overscroll-behavior: contain;
|
|
207
|
+
padding: 16px 20px 28px; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
|
|
130
208
|
}
|
|
131
|
-
.thread-in { max-width:
|
|
132
|
-
.empty { color:
|
|
209
|
+
.thread-in { max-width: 960px; margin: 0 auto; display: flex; flex-direction: column; gap: 14px }
|
|
210
|
+
.empty { color: var(--faint); text-align: center; padding: 60px 0; font-size: 13px }
|
|
133
211
|
.more-btn {
|
|
134
|
-
align-self: center; background:
|
|
135
|
-
padding: 5px 16px; font-size: 12px; color:
|
|
212
|
+
align-self: center; background: var(--card); border: 1px solid var(--line); border-radius: 16px;
|
|
213
|
+
padding: 5px 16px; font-size: 12px; color: var(--mute); cursor: pointer;
|
|
214
|
+
}
|
|
215
|
+
.more-btn:hover { color: var(--accent); border-color: var(--accent) }
|
|
216
|
+
|
|
217
|
+
.mrow { display: flex; align-items: flex-start; gap: 10px }
|
|
218
|
+
.mrow .av {
|
|
219
|
+
flex: none; width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--line);
|
|
220
|
+
background: var(--card); font-size: 19px; line-height: 32px; text-align: center; cursor: default; padding: 0;
|
|
221
|
+
}
|
|
222
|
+
.mcol { min-width: 0; max-width: min(78%, 760px); display: flex; flex-direction: column; gap: 4px }
|
|
223
|
+
.mrow.mine { flex-direction: row-reverse }
|
|
224
|
+
.mrow.mine .mcol { align-items: flex-end }
|
|
225
|
+
.mwho { font-size: 11.5px; color: var(--mute); display: flex; gap: 6px; align-items: baseline; flex-wrap: wrap }
|
|
226
|
+
.mwho .to { color: var(--faint) }
|
|
227
|
+
.mwho .ch { font-size: 10.5px; border-radius: 4px; padding: 0 4px }
|
|
228
|
+
.mwho .ch.priv { color: var(--priv); background: #9a5b1014 }
|
|
229
|
+
/* 时刻只此一处 —— 气泡外, 跟着名字走。等宽数字让一列时间对得齐。 */
|
|
230
|
+
.mwho .mt { color: var(--faint); font-family: ui-monospace, Menlo, monospace; font-variant-numeric: tabular-nums }
|
|
231
|
+
.mb { min-width: 0; max-width: 100% }
|
|
232
|
+
/* 卡片里那份时刻是详情页要的, 这里已经写在 .mwho 上了 —— 同一条消息不报两次时间。 */
|
|
233
|
+
.mb .tg-time { display: none }
|
|
234
|
+
|
|
235
|
+
/* 入消息 (问句) 是一颗气泡; 出消息 (回复) 是一张带工具细节的卡片 —— 两者都按视角着色。 */
|
|
236
|
+
.mb > .bubble.mq { border-radius: 14px; border-color: var(--line) }
|
|
237
|
+
.mb .mq .mmeta { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; padding: 8px 14px 0; font-size: 11px; color: var(--faint) }
|
|
238
|
+
.mb .mq .md-body { padding: 10px 14px }
|
|
239
|
+
.mb .mq .mmeta + .md-body { padding-top: 4px }
|
|
240
|
+
.mchip { border-radius: 4px; padding: 0 5px; background: #f1f3f6; color: var(--mute) }
|
|
241
|
+
.mchip.priv { color: var(--priv); background: #9a5b1014 }
|
|
242
|
+
.mchip.pub { color: var(--pub); background: #237a5514 }
|
|
243
|
+
.mchip.graph { color: #6639ba; background: #8250df12 }
|
|
244
|
+
|
|
245
|
+
/* 自己发出的那条: 整块就是视角色。TURN_CSS 里写死的白卡 / 灰底 (嵌套气泡、
|
|
246
|
+
代码块、折叠头) 会在蓝底上显成一张张贴纸 —— 这里把它们收回到气泡自己的
|
|
247
|
+
底色上, 一条消息读起来是一块颜色, 需要再沉一档的才用 --mine-soft。 */
|
|
248
|
+
.mrow.mine .mb > .bubble.mq, .mrow.mine .mb > .turn-group { background: var(--mine); border-color: var(--mine-line) }
|
|
249
|
+
.mrow.mine .mb .bubble, .mrow.mine .mb .typing, .mrow.mine .mb .stats { background: transparent; border-color: var(--mine-line) }
|
|
250
|
+
.mrow.mine .mb .bubble.user, .mrow.mine .mb .bubble.approval,
|
|
251
|
+
.mrow.mine .mb .md-body code, .mrow.mine .mb .md-body pre,
|
|
252
|
+
.mrow.mine .mb .tile, .mrow.mine .mb details > summary,
|
|
253
|
+
.mrow.mine .mb .tool-call > summary:hover { background: var(--mine-soft); border-color: var(--mine-line) }
|
|
254
|
+
.mrow.mine .mb .md-body pre code { background: transparent }
|
|
255
|
+
.mrow.mine .mb .bubble details, .mrow.mine .mb .tg-head { border-color: var(--mine-line) }
|
|
256
|
+
.mrow.mine .mb .tool-body { border-left-color: var(--mine-line) }
|
|
257
|
+
.mrow.mine .mb .io-bar { background: var(--mine-soft) }
|
|
258
|
+
|
|
259
|
+
/* 气泡对面那片空白 = 换视角的门。平时不可见, 悬停才露出要去谁那里。 */
|
|
260
|
+
.flip {
|
|
261
|
+
flex: 1; align-self: stretch; min-width: 40px; border: 0; background: transparent; cursor: pointer;
|
|
262
|
+
display: flex; align-items: center; padding: 0 12px; border-radius: 10px; color: transparent;
|
|
263
|
+
font-size: 12px; transition: background .15s, color .15s;
|
|
264
|
+
}
|
|
265
|
+
.mrow.them .flip { justify-content: flex-start }
|
|
266
|
+
.mrow.mine .flip { justify-content: flex-end }
|
|
267
|
+
/* 气泡可以比视窗还高, 居中的箭头就跑出屏幕了 —— 让它在按钮内 sticky, 上下各留半屏,
|
|
268
|
+
于是只要这一行还在视窗里, 箭头就钉在视窗中线附近。 */
|
|
269
|
+
.flip .fi {
|
|
270
|
+
position: sticky; top: calc(50% - 10px); bottom: calc(50% - 10px);
|
|
271
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
272
|
+
}
|
|
273
|
+
.mrow.mine .flip .fi { flex-direction: row-reverse }
|
|
274
|
+
.mrow.mine .flip .fc { transform: scaleX(-1) }
|
|
275
|
+
.flip .fc { flex: none }
|
|
276
|
+
.flip:hover, .flip:focus-visible { background: #2f55c90d; color: var(--accent) }
|
|
277
|
+
.flip[disabled] { cursor: default; background: transparent !important; color: transparent !important }
|
|
278
|
+
.mrow .av.go { cursor: pointer }
|
|
279
|
+
.mrow .av.go:hover { border-color: var(--accent) }
|
|
280
|
+
|
|
281
|
+
.mrow.mark { justify-content: center }
|
|
282
|
+
.mrow.mark > * { flex: 1; max-width: 520px }
|
|
283
|
+
|
|
284
|
+
/* 视角切换的那一下: 整列消息淡出再按新视角排回来 —— 这一页唯一的编排动效。 */
|
|
285
|
+
.thread-in.flipping { animation: flip .26s ease }
|
|
286
|
+
@keyframes flip { 0% { opacity: 1 } 45% { opacity: 0; transform: scaleX(.985) } 100% { opacity: 1 } }
|
|
287
|
+
@media (prefers-reduced-motion: reduce) {
|
|
288
|
+
.thread-in.flipping { animation: none }
|
|
289
|
+
.flip { transition: none }
|
|
136
290
|
}
|
|
137
|
-
.more-btn:hover { background: #f6f8fa; color: #0969da; border-color: #0969da55 }
|
|
138
291
|
|
|
139
292
|
/* ── turn 分组 ──────────────────────────────────────────────────── */
|
|
140
293
|
/* section 的默认样式给了白底+边框, 但没有内边距 —— 气泡/工具行会直接贴着卡片
|
|
@@ -167,49 +320,358 @@ body { overflow: hidden; overscroll-behavior: none }
|
|
|
167
320
|
/* graph 派的一轮: 卡片本身也着色, 滚动时不必读归因条就知道这不是人发的。 */
|
|
168
321
|
.turn-group.graph { border-color: #8250df44 }
|
|
169
322
|
|
|
170
|
-
/* ──
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
font-
|
|
177
|
-
}
|
|
178
|
-
.
|
|
179
|
-
.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
.
|
|
185
|
-
.
|
|
186
|
-
.
|
|
187
|
-
.
|
|
188
|
-
.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.
|
|
195
|
-
.sb-io > .k { color: #8c959f; text-transform: uppercase; letter-spacing: .4px; font-size: 10px }
|
|
196
|
-
.sb-bar { display: flex; height: 6px; width: 120px; border-radius: 3px; overflow: hidden; background: #eaeef2 }
|
|
197
|
-
.sb-bar .seg { height: 100% }
|
|
198
|
-
.sb-leg { display: flex; align-items: center; gap: 9px; flex-wrap: wrap }
|
|
199
|
-
.sb-leg .lg { display: flex; align-items: center; gap: 4px; font-size: 10.5px; color: #8c959f }
|
|
200
|
-
.sb-leg .lg i { width: 8px; height: 8px; border-radius: 2px; flex: none }
|
|
201
|
-
.sb-leg .lg b { color: #1f2328; font-weight: 700; font-variant-numeric: tabular-nums }
|
|
202
|
-
.conn { margin-left: auto; display: flex; align-items: center; gap: 5px }
|
|
203
|
-
.conn .d { width: 7px; height: 7px; border-radius: 50%; background: #1a7f37 }
|
|
204
|
-
.conn.off .d { background: #cf222e }
|
|
323
|
+
/* ── 🏓 keepalive 折叠 ──────────────────────────────────────────────
|
|
324
|
+
保温 ping 不是对话, 但它是真花销 —— 所以留在时间轴上, 只是收成一行虚线。 */
|
|
325
|
+
.ping-fold { border: 1px dashed #d8dee4; border-radius: 10px; background: #fbfcfd }
|
|
326
|
+
.ping-fold > .ping-sum {
|
|
327
|
+
display: flex; align-items: center; gap: 8px; padding: 6px 12px;
|
|
328
|
+
cursor: pointer; list-style: none; font-size: 11.5px; color: #8c959f;
|
|
329
|
+
font-family: ui-monospace, Menlo, monospace; font-variant-numeric: tabular-nums;
|
|
330
|
+
}
|
|
331
|
+
.ping-fold > .ping-sum::-webkit-details-marker { display: none }
|
|
332
|
+
.ping-fold > .ping-sum::after {
|
|
333
|
+
content: "›"; margin-left: auto; font-size: 14px; color: #b1bac4;
|
|
334
|
+
transition: transform .12s;
|
|
335
|
+
}
|
|
336
|
+
.ping-fold[open] > .ping-sum::after { transform: rotate(90deg) }
|
|
337
|
+
.ping-fold > .ping-sum:hover { color: #57606a }
|
|
338
|
+
.ping-fold .pb { color: #57606a; font-weight: 700; flex: none }
|
|
339
|
+
.ping-fold .ps { overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
340
|
+
.ping-fold .pt { flex: none; opacity: .8 }
|
|
341
|
+
.ping-fold .ping-body {
|
|
342
|
+
display: flex; flex-direction: column; gap: 10px; padding: 2px 10px 10px;
|
|
343
|
+
}
|
|
344
|
+
/* 展开后的 ping 轮次压一档: 它们是背景噪声, 不该和真对话卡片一样醒目。 */
|
|
345
|
+
.ping-fold .turn-group { box-shadow: none }
|
|
346
|
+
.ping-fold .mcol { max-width: 100% }
|
|
347
|
+
.ping-fold .flip { display: none }
|
|
205
348
|
|
|
206
349
|
@media (max-width: 760px) {
|
|
207
350
|
.app { flex-direction: column }
|
|
208
|
-
.side { width: auto;
|
|
351
|
+
.side { width: auto; flex: 1; border-right: 0 }
|
|
352
|
+
.main { display: none }
|
|
209
353
|
.app.reading .side { display: none }
|
|
210
|
-
.app.reading .
|
|
354
|
+
.app.reading .main { display: flex }
|
|
355
|
+
.chathead { padding: 8px 12px; gap: 8px }
|
|
356
|
+
.chathead .back { display: block }
|
|
211
357
|
.thread { padding: 12px }
|
|
358
|
+
.mcol { max-width: 86% }
|
|
359
|
+
.flip { min-width: 16px; padding: 0 4px; font-size: 0 }
|
|
212
360
|
.turn-group { padding: 12px }
|
|
213
|
-
.
|
|
214
|
-
.sb-io, .statusbar .model { display: none }
|
|
361
|
+
.thread { padding-bottom: calc(12px + env(safe-area-inset-bottom)) }
|
|
215
362
|
}
|
|
363
|
+
|
|
364
|
+
/* UA 的 [hidden] 规则和 `.gbar{display:flex}` 这类类选择器同权重, 而作者样式
|
|
365
|
+
胜出 —— 于是 el.hidden=true 的元素照旧显示 (空的 gbar 会剩一条带边框的窄带)。
|
|
366
|
+
一条 !important 把 hidden 的语义钉死, 三块视图容器的显隐都靠它。 */
|
|
367
|
+
[hidden] { display: none !important }
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
/* ══ 关系图 ═════════════════════════════════════════════════════════ */
|
|
371
|
+
.wtools {
|
|
372
|
+
flex: none; display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
|
|
373
|
+
padding: 8px 20px; background: #fff; border-bottom: 1px solid #d0d7de;
|
|
374
|
+
font-size: 11.5px; color: #57606a;
|
|
375
|
+
}
|
|
376
|
+
.wlegend { display: flex; gap: 6px; flex-wrap: wrap }
|
|
377
|
+
/* 图例同时是开关 —— 边一多就得能关掉几种; 把"图例"和"过滤器"做成两样东西
|
|
378
|
+
只会让人在两处之间对照颜色。 */
|
|
379
|
+
.wtools .chip {
|
|
380
|
+
display: flex; align-items: center; gap: 5px; font: inherit; cursor: pointer;
|
|
381
|
+
background: #fff; border: 1px solid #d0d7de; border-radius: 13px; padding: 2px 10px;
|
|
382
|
+
color: #8c959f;
|
|
383
|
+
}
|
|
384
|
+
.wtools .chip i { width: 14px; height: 0; border-top: 2px solid var(--c); opacity: .35 }
|
|
385
|
+
.wtools .chip b { font-variant-numeric: tabular-nums; font-weight: 700 }
|
|
386
|
+
.wtools .chip.on { color: #1f2328; border-color: var(--c) }
|
|
387
|
+
.wtools .chip.on i { opacity: 1 }
|
|
388
|
+
.wtools .chip.clear, .wtools .chip.only { border-color: #d0d7de; color: #57606a }
|
|
389
|
+
.wtools .chip.only.on { border-color: #0969da; color: #0550ae; background: #ddf4ff }
|
|
390
|
+
.wtools .wstat { margin-left: auto; color: #8c959f; font-variant-numeric: tabular-nums }
|
|
391
|
+
.wtools .wstat b { color: #0969da }
|
|
392
|
+
.wtools .warn { color: #9a6700 }
|
|
393
|
+
.wtools .hint { margin-left: 10px; color: #b1bac4 }
|
|
394
|
+
|
|
395
|
+
/* position 不是装饰: 力导向的画布是 `position:absolute; inset:0`, 没有这一行它
|
|
396
|
+
就贴到更外层的定位祖先上去 —— 坐标原点跑到顶栏, 节点飘到工具条上面, 而
|
|
397
|
+
overflow:hidden 也拦不住一个容器块在自己之外的绝对定位子元素。 */
|
|
398
|
+
.wscroll { position: relative; flex: 1; min-height: 0; overflow: auto; -webkit-overflow-scrolling: touch }
|
|
399
|
+
.wmap { position: relative; padding: 18px 20px 28px; min-width: min-content }
|
|
400
|
+
/* 有关系的节点带一道左侧色条 —— 76 个会话里哪几个真的在协作, 不点开也看得见。 */
|
|
401
|
+
.wnode.rel::before {
|
|
402
|
+
content: ""; position: absolute; left: 1px; top: 8px; bottom: 8px;
|
|
403
|
+
width: 2px; border-radius: 1px; background: #0969da66;
|
|
404
|
+
}
|
|
405
|
+
/* 连线层铺满整张图并且不吃事件 —— 节点的点击/双击必须照常穿过去。
|
|
406
|
+
z-index 把它抬到卡片之上: 卡片是不透明的, 压在下面时一条跨卡片的边会被中途
|
|
407
|
+
吃掉半截, 读出来像是断的。.wgrid 不设 z-index (保持 auto) 才不会自成一个层叠
|
|
408
|
+
上下文 —— 那样悬停/聚焦的节点就再也抬不过这条线了。 */
|
|
409
|
+
.wedges { position: absolute; inset: 0; pointer-events: none; overflow: visible; z-index: 2 }
|
|
410
|
+
/* 反过来: 正在看的那个节点要压过连线, 否则文字被线划着读。只抬这两种 —— 一直
|
|
411
|
+
抬着某个节点等于把"线被卡片吃掉"这个毛病在它身上留一份。 */
|
|
412
|
+
.wnode:hover, .wnode.sel { z-index: 3 }
|
|
413
|
+
/* 多列流式而不是 grid: 聊天卡片的高度天差地别 (一个群 2 个 wizard, 另一个 20 个),
|
|
414
|
+
grid 的行高按本行最高的那张对齐, 于是矮卡片下面全是空白 —— 图看着比实际稀疏
|
|
415
|
+
一倍。CSS columns 是砌砖式的: 卡片顺着列往下码, 码满了换列, 没有行的概念。
|
|
416
|
+
列宽钉在 320 附近也顺带收住了"两张卡片各占半个屏"的过度拉伸。 */
|
|
417
|
+
.wgrid { position: relative; columns: 320px; column-gap: 16px }
|
|
418
|
+
|
|
419
|
+
.wchat {
|
|
420
|
+
background: #fff; border: 1px solid #d0d7de; border-radius: 12px; overflow: hidden;
|
|
421
|
+
/* 一张卡片不能被切成两列 —— 断在半截的家谱读不出父子关系。 */
|
|
422
|
+
break-inside: avoid; -webkit-column-break-inside: avoid; page-break-inside: avoid;
|
|
423
|
+
margin-bottom: 16px;
|
|
424
|
+
}
|
|
425
|
+
/* 卡片头可点 = 进那个群 (见 chat.js 的 openNode)。 */
|
|
426
|
+
.wch.go { cursor: pointer }
|
|
427
|
+
.wch.go:hover .nm { color: #0969da; text-decoration: underline }
|
|
428
|
+
/* 当前聊天是读图的锚点, 给它一道明确的边 —— 其余的群一律等价, 不再分轻重。 */
|
|
429
|
+
.wchat.self { border-color: #0969da66; box-shadow: 0 0 0 3px #0969da10 }
|
|
430
|
+
.wch {
|
|
431
|
+
display: flex; align-items: baseline; gap: 8px; padding: 9px 13px;
|
|
432
|
+
border-bottom: 1px solid #eaeef2; background: #f6f8fa;
|
|
433
|
+
}
|
|
434
|
+
.wchat.self .wch { background: #f0f6ff }
|
|
435
|
+
.wch .nm {
|
|
436
|
+
font-size: 13px; font-weight: 700; min-width: 0; overflow: hidden;
|
|
437
|
+
text-overflow: ellipsis; white-space: nowrap;
|
|
438
|
+
}
|
|
439
|
+
.wch .here {
|
|
440
|
+
font-size: 10px; color: #0969da; background: #0969da14; border-radius: 4px; padding: 1px 5px;
|
|
441
|
+
}
|
|
442
|
+
.wch .ct {
|
|
443
|
+
margin-left: auto; font-size: 11px; color: #8c959f; flex: none;
|
|
444
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
445
|
+
}
|
|
446
|
+
.wch .ct em { color: #1a7f37; font-style: normal; font-weight: 700 }
|
|
447
|
+
/* 左内边距是同卡片内连线的车道 —— 一条 `#a → #b` 的派活边从这里绕过去, 不必
|
|
448
|
+
拐出卡片 (拐出去就会被滚动容器裁掉)。 */
|
|
449
|
+
.wrows { padding: 7px 10px 10px 22px; display: flex; flex-direction: column; gap: 3px }
|
|
450
|
+
|
|
451
|
+
.wnode {
|
|
452
|
+
position: relative; display: flex; gap: 8px; align-items: flex-start;
|
|
453
|
+
padding: 6px 8px; border-radius: 8px; cursor: pointer;
|
|
454
|
+
border: 1px solid transparent; transition: opacity .15s, background .12s;
|
|
455
|
+
}
|
|
456
|
+
.wnode:hover { background: #f6f8fa }
|
|
457
|
+
.wnode.self { background: #fffbe8 }
|
|
458
|
+
.wnode.sel { background: #eef4fc; border-color: #0969da55 }
|
|
459
|
+
/* 聚焦时无关节点压暗而不是移除 —— 位置不动, 连点几个节点版面也不跳。 */
|
|
460
|
+
.wnode.dim { opacity: .3 }
|
|
461
|
+
/* 双击能走进去的那些 —— 外聊天的节点现在也算 (整页跳过去)。 */
|
|
462
|
+
.wnode.go { cursor: pointer }
|
|
463
|
+
.wnode.cold .wav { filter: grayscale(1); opacity: .55 }
|
|
464
|
+
/* 缩进本身就是家谱; 这道折线只是把"缩给谁看"指实。 */
|
|
465
|
+
.wnode .lin {
|
|
466
|
+
position: absolute; left: -9px; top: 0; bottom: 50%; width: 9px;
|
|
467
|
+
border-left: 1.5px solid #d8cdf0; border-bottom: 1.5px solid #d8cdf0;
|
|
468
|
+
border-bottom-left-radius: 6px;
|
|
469
|
+
}
|
|
470
|
+
/* 子 wizard 不是 fork: 折线换成虚线、换色, 与分身一眼分开 (同关系图的 spawn 边)。 */
|
|
471
|
+
.wnode .lin.sp { border-style: dashed; border-color: #f0d2bd }
|
|
472
|
+
.wav {
|
|
473
|
+
position: relative; width: 26px; height: 26px; flex: none; border-radius: 8px;
|
|
474
|
+
background: #f1f3f6; border: 1px solid #eaeef2;
|
|
475
|
+
display: flex; align-items: center; justify-content: center; font-size: 14px;
|
|
476
|
+
}
|
|
477
|
+
.wav .live {
|
|
478
|
+
position: absolute; right: -3px; bottom: -3px; width: 9px; height: 9px;
|
|
479
|
+
border-radius: 50%; background: #1a7f37; border: 2px solid #fff;
|
|
480
|
+
animation: pulse 1.4s infinite;
|
|
481
|
+
}
|
|
482
|
+
.wbody { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px }
|
|
483
|
+
.wl1 { display: flex; align-items: baseline; gap: 6px; min-width: 0 }
|
|
484
|
+
.wname {
|
|
485
|
+
font-size: 12.5px; font-weight: 600; min-width: 0; overflow: hidden;
|
|
486
|
+
text-overflow: ellipsis; white-space: nowrap;
|
|
487
|
+
}
|
|
488
|
+
.wnode.run .wname { color: #1a7f37 }
|
|
489
|
+
.wtag {
|
|
490
|
+
flex: none; font-size: 10.5px; color: #57606a; background: #f1f3f6;
|
|
491
|
+
border-radius: 4px; padding: 0 5px; font-family: ui-monospace, Menlo, monospace;
|
|
492
|
+
}
|
|
493
|
+
.wtag.def { color: #8c959f }
|
|
494
|
+
.wih { flex: none; font-size: 11px; color: #6639ba }
|
|
495
|
+
.wts {
|
|
496
|
+
margin-left: auto; flex: none; font-size: 10.5px; color: #8c959f;
|
|
497
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
498
|
+
}
|
|
499
|
+
.wjob { font-size: 11.5px; color: #0550ae; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
500
|
+
.wprev {
|
|
501
|
+
font-size: 11.5px; color: #656d76; overflow: hidden; line-height: 1.4;
|
|
502
|
+
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
|
503
|
+
}
|
|
504
|
+
.wmeta {
|
|
505
|
+
font-size: 10.5px; color: #8c959f; font-family: ui-monospace, Menlo, monospace;
|
|
506
|
+
display: flex; gap: 4px; flex-wrap: wrap; margin-top: 1px;
|
|
507
|
+
}
|
|
508
|
+
.wmeta .sep { opacity: .45 }
|
|
509
|
+
|
|
510
|
+
/* ══ 日程 ═══════════════════════════════════════════════════════════ */
|
|
511
|
+
.plan-in { flex: 1; min-height: 0; overflow-y: auto; padding: 18px 20px 28px }
|
|
512
|
+
.psec {
|
|
513
|
+
max-width: 900px; margin: 0 auto 30px;
|
|
514
|
+
/* 通用 section 规则给了白底+边框却没有内边距 —— 不补这一层, 刻度条/日程行/工单
|
|
515
|
+
行就直接贴着卡片的边。底边留小一点: 末行自己还带 10px 的 margin。 */
|
|
516
|
+
padding: 14px 18px 8px;
|
|
517
|
+
}
|
|
518
|
+
.psec h3 {
|
|
519
|
+
display: flex; align-items: baseline; gap: 8px; font-size: 13px; margin: 0 0 14px;
|
|
520
|
+
padding-bottom: 8px; border-bottom: 1px solid #d0d7de;
|
|
521
|
+
}
|
|
522
|
+
.psec h3 span {
|
|
523
|
+
font-size: 11px; color: #8c959f; font-weight: 400;
|
|
524
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
525
|
+
}
|
|
526
|
+
.pempty { color: #8c959f; font-size: 12px; padding: 18px 0; text-align: center }
|
|
527
|
+
|
|
528
|
+
/* 未来 12 小时的刻度条 —— 「今晚还有几枪要响」比一张日期列表更该先看见。 */
|
|
529
|
+
.strip {
|
|
530
|
+
background: #fff; border: 1px solid #d0d7de; border-radius: 10px;
|
|
531
|
+
padding: 12px 16px 11px; margin-bottom: 16px;
|
|
532
|
+
}
|
|
533
|
+
.strip .axis {
|
|
534
|
+
position: relative; height: 13px; font-size: 10px; color: #8c959f;
|
|
535
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
536
|
+
}
|
|
537
|
+
.strip .tk { position: absolute; transform: translateX(-50%); white-space: nowrap }
|
|
538
|
+
.strip .tk:first-child { transform: none }
|
|
539
|
+
.strip .rail {
|
|
540
|
+
position: relative; height: 8px; border-radius: 4px; background: #eaeef2; margin: 3px 0 7px;
|
|
541
|
+
}
|
|
542
|
+
.strip .pin {
|
|
543
|
+
position: absolute; top: -3px; width: 8px; height: 14px; margin-left: -4px;
|
|
544
|
+
border-radius: 3px; background: #9a6700; box-shadow: 0 0 0 2px #fff;
|
|
545
|
+
}
|
|
546
|
+
.strip .cap { font-size: 11px; color: #8c959f }
|
|
547
|
+
|
|
548
|
+
.prow {
|
|
549
|
+
display: flex; gap: 16px; background: #fff; border: 1px solid #d0d7de;
|
|
550
|
+
border-radius: 10px; padding: 12px 16px; margin-bottom: 10px;
|
|
551
|
+
}
|
|
552
|
+
.prow .pl { flex: none; width: 152px; border-right: 1px dashed #eaeef2; padding-right: 14px }
|
|
553
|
+
.pwhen { font-size: 11.5px; color: #9a6700; font-weight: 600 }
|
|
554
|
+
.pnext {
|
|
555
|
+
display: flex; align-items: baseline; gap: 6px; margin-top: 5px;
|
|
556
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
557
|
+
}
|
|
558
|
+
.pnext b { font-size: 12.5px; font-variant-numeric: tabular-nums }
|
|
559
|
+
.pnext span { font-size: 10.5px; color: #8c959f }
|
|
560
|
+
.prow .pr { min-width: 0; flex: 1 }
|
|
561
|
+
.ph { display: flex; align-items: center; gap: 7px; flex-wrap: wrap }
|
|
562
|
+
.ph .note { font-size: 11px; color: #57606a }
|
|
563
|
+
.ph .pid {
|
|
564
|
+
margin-left: auto; font-size: 10px; color: #b1bac4;
|
|
565
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
566
|
+
}
|
|
567
|
+
.ptext { font-size: 12px; color: #1f2328; margin-top: 6px; line-height: 1.5; word-break: break-word }
|
|
568
|
+
.pfoot { font-size: 10.5px; color: #8c959f; margin-top: 6px }
|
|
569
|
+
|
|
570
|
+
/* wizard 徽标 —— 日程/工单里凡是提到一个 wizard 都用它, 同聊天的可点进线程。 */
|
|
571
|
+
.wchip {
|
|
572
|
+
display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px;
|
|
573
|
+
background: #f1f3f6; border: 1px solid #eaeef2; border-radius: 12px; padding: 1px 8px;
|
|
574
|
+
max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
575
|
+
}
|
|
576
|
+
.wchip.go { cursor: pointer; border-color: #0969da33; color: #0550ae }
|
|
577
|
+
.wchip.go:hover { background: #ddf4ff; border-color: #0969da }
|
|
578
|
+
|
|
579
|
+
.jrow { background: #fff; border: 1px solid #d0d7de; border-radius: 10px; padding: 12px 16px; margin-bottom: 10px }
|
|
580
|
+
.jrow.open { border-color: #1a7f3755; box-shadow: 0 0 0 3px #1a7f370d }
|
|
581
|
+
.jh { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap }
|
|
582
|
+
.jid {
|
|
583
|
+
font-size: 10.5px; color: #57606a; background: #f1f3f6; border-radius: 4px;
|
|
584
|
+
padding: 0 6px; font-family: ui-monospace, Menlo, monospace;
|
|
585
|
+
}
|
|
586
|
+
.jt { font-size: 13px; font-weight: 600; min-width: 0 }
|
|
587
|
+
.jst { font-size: 10.5px; color: #8c959f }
|
|
588
|
+
.jrow.open .jst { color: #1a7f37; font-weight: 700 }
|
|
589
|
+
.jts {
|
|
590
|
+
margin-left: auto; font-size: 10.5px; color: #8c959f;
|
|
591
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
592
|
+
}
|
|
593
|
+
.jm { display: flex; flex-direction: column; gap: 5px; margin-top: 9px }
|
|
594
|
+
.jmm { display: flex; align-items: center; gap: 6px; min-width: 0 }
|
|
595
|
+
.jmm em {
|
|
596
|
+
font-style: normal; font-size: 11.5px; color: #656d76; min-width: 0;
|
|
597
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
598
|
+
}
|
|
599
|
+
.jmm .tmp {
|
|
600
|
+
font-style: normal; font-size: 9.5px; color: #9a6700; background: #d4a72c22;
|
|
601
|
+
border-radius: 3px; padding: 0 4px;
|
|
602
|
+
}
|
|
603
|
+
.jmm.none { font-size: 11.5px; color: #b1bac4 }
|
|
604
|
+
.jsum {
|
|
605
|
+
font-size: 12px; color: #1f2328; margin-top: 10px; padding-top: 10px;
|
|
606
|
+
border-top: 1px dashed #eaeef2; line-height: 1.5; white-space: pre-wrap;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
@media (max-width: 760px) {
|
|
610
|
+
.chathead .vb { padding: 3px 8px }
|
|
611
|
+
.wmap { padding: 12px }
|
|
612
|
+
.wgrid { columns: 1 }
|
|
613
|
+
.plan-in { padding: 12px }
|
|
614
|
+
.prow { flex-direction: column; gap: 6px }
|
|
615
|
+
.prow .pl { width: auto; border-right: 0; border-bottom: 1px dashed #eaeef2; padding: 0 0 8px }
|
|
616
|
+
.pnext { margin-top: 1px }
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/* 相关性筛掉的那些 —— 说一声它们还在, 但不占图的位置 (见 shared/world 的说明)。 */
|
|
620
|
+
.wmore {
|
|
621
|
+
padding: 5px 13px 9px; font-size: 10.5px; color: #b1bac4;
|
|
622
|
+
border-top: 1px dashed #eaeef2; margin-top: 2px;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/* ══ 关系视图 · 力导向布局 ═════════════════════════════════════════
|
|
626
|
+
卡片布局之外的另一种读法: 节点只画 wizard, 聊天不再是容器而是节点身上的一
|
|
627
|
+
道色条 + 一行群名。见 web/chat.js 的说明。 */
|
|
628
|
+
.wscroll.fx { overflow: hidden }
|
|
629
|
+
.wmap.force { position: absolute; inset: 0; padding: 0; min-width: 0 }
|
|
630
|
+
.fxn {
|
|
631
|
+
position: absolute; left: 0; top: 0; z-index: 2;
|
|
632
|
+
display: flex; align-items: center; gap: 7px; max-width: 210px;
|
|
633
|
+
padding: 5px 10px 5px 7px; border-radius: 10px;
|
|
634
|
+
background: #fff; border: 1px solid #d0d7de; border-left: 3px solid var(--c);
|
|
635
|
+
box-shadow: 0 1px 3px #1b1f2412; cursor: grab; touch-action: none;
|
|
636
|
+
user-select: none; -webkit-user-select: none;
|
|
637
|
+
transition: opacity .15s, box-shadow .12s;
|
|
638
|
+
will-change: transform;
|
|
639
|
+
}
|
|
640
|
+
.fxn .wav { width: 22px; height: 22px; border-radius: 7px; font-size: 12px }
|
|
641
|
+
.fxn .wav .live { width: 8px; height: 8px; right: -2px; bottom: -2px }
|
|
642
|
+
.fxb { min-width: 0; display: flex; flex-direction: column; gap: 0 }
|
|
643
|
+
.fxn .wname { font-size: 12px }
|
|
644
|
+
/* 群名压得比名字轻 —— 它是节点的属性, 不是节点本身。 */
|
|
645
|
+
.fxc {
|
|
646
|
+
font-size: 9.5px; color: var(--c); opacity: .85; overflow: hidden;
|
|
647
|
+
text-overflow: ellipsis; white-space: nowrap;
|
|
648
|
+
}
|
|
649
|
+
.fxn.self { background: #fffbe8 }
|
|
650
|
+
.fxn.cold { opacity: .72 }
|
|
651
|
+
.fxn.cold .wav { filter: grayscale(1); opacity: .55 }
|
|
652
|
+
.fxn.run .wname { color: #1a7f37 }
|
|
653
|
+
.fxn.dim { opacity: .22 }
|
|
654
|
+
.fxn.sel { box-shadow: 0 0 0 3px var(--c), 0 1px 3px #1b1f2412; }
|
|
655
|
+
.fxn.sel .fxc { opacity: 1 }
|
|
656
|
+
.fxn.drag { cursor: grabbing; box-shadow: 0 6px 16px #1b1f2426 }
|
|
657
|
+
/* 钉住 = 这个位置是人摆的, 力不再动它。 */
|
|
658
|
+
.fxn.pin::after {
|
|
659
|
+
content: "📌"; position: absolute; right: -5px; top: -7px; font-size: 9px;
|
|
660
|
+
opacity: .8; pointer-events: none;
|
|
661
|
+
}
|
|
662
|
+
/* 布局开关 —— 两种读法并列, 不是复选。 */
|
|
663
|
+
.wtools .wlay { display: inline-flex; border: 1px solid #d0d7de; border-radius: 7px; overflow: hidden }
|
|
664
|
+
.wtools .lb {
|
|
665
|
+
border: 0; background: #fff; cursor: pointer; padding: 3px 9px;
|
|
666
|
+
font-size: 11.5px; color: #57606a; font-family: inherit;
|
|
667
|
+
}
|
|
668
|
+
.wtools .lb + .lb { border-left: 1px solid #d0d7de }
|
|
669
|
+
.wtools .lb.on { background: #ddf4ff; color: #0550ae; font-weight: 600 }
|
|
670
|
+
|
|
671
|
+
/* ══ 关系 / 日程 ═══════════════════════════════════════════════════ */
|
|
672
|
+
/* 都是当前 role 的另一面, 侧栏 (它的会话) 留着 —— 点一个会话就回到对话。
|
|
673
|
+
会话标题条与页脚总账只属于对话视图。 */
|
|
674
|
+
.pane { flex: 1; min-height: 0; display: flex; flex-direction: column; background: #fafbfc }
|
|
675
|
+
|
|
676
|
+
/* 回复卡片里的工具行是 nowrap 的 —— 不裁一刀会把整列撑出横向滚动。 */
|
|
677
|
+
.mb > .turn-group { overflow: hidden }
|