dsh-auto-collapse 0.1.8 → 0.2.1
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/README.en.md +53 -68
- package/README.md +64 -68
- package/assets/screenshot.png +0 -0
- package/lib/client.js +843 -2339
- package/lib/index.js +4 -46
- package/lib/types/controller.d.ts +50 -0
- package/lib/types/dom-view.d.ts +36 -0
- package/lib/types/fold.d.ts +2 -323
- package/lib/types/host-contract.d.ts +17 -0
- package/lib/types/index.d.ts +12 -11
- package/lib/types/scheduler.d.ts +10 -0
- package/lib/types/status-text.d.ts +14 -0
- package/lib/types/summary.d.ts +17 -0
- package/lib/types/work-groups.d.ts +30 -0
- package/lib/types/work-info.d.ts +4 -0
- package/lib/types/work-model.d.ts +24 -0
- package/package.json +14 -3
package/lib/client.js
CHANGED
|
@@ -33,2408 +33,909 @@ var __dshcfBundle = (() => {
|
|
|
33
33
|
name: () => name
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
// src/
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
var
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
var STICK_BOTTOM_THRESHOLD_PX = 24;
|
|
45
|
-
var TOOL_LABELS = {
|
|
46
|
-
bash: "Bash",
|
|
47
|
-
pwsh: "Pwsh",
|
|
48
|
-
read: "Read",
|
|
49
|
-
web_fetch: "Read",
|
|
50
|
-
web_search: "Search",
|
|
51
|
-
grep: "Search",
|
|
52
|
-
glob: "Search",
|
|
53
|
-
write: "Write",
|
|
54
|
-
edit: "Edit",
|
|
55
|
-
run_code: "Code",
|
|
56
|
-
cordis_package_inspect: "Inspect",
|
|
57
|
-
cordis_runtime_inspect: "Inspect",
|
|
58
|
-
cordis_run: "Run",
|
|
59
|
-
cordis_stop: "Stop",
|
|
60
|
-
cordis_undefine: "Remove"
|
|
61
|
-
};
|
|
62
|
-
var TURN_PROCESS_KIND = "turn-process";
|
|
63
|
-
var NATIVE_OPEN_ATTR = "data-open";
|
|
64
|
-
var CHIP_CSS = `
|
|
65
|
-
.dshcf-chip {
|
|
66
|
-
box-sizing: border-box;
|
|
67
|
-
display: flex;
|
|
68
|
-
align-self: flex-start;
|
|
69
|
-
align-items: center;
|
|
70
|
-
gap: 6px;
|
|
71
|
-
width: fit-content;
|
|
72
|
-
max-width: 100%;
|
|
73
|
-
min-height: 24px;
|
|
74
|
-
/* chip \u63D2\u5728\u5757\u5BBF\u4E3B\uFF08flowItem\uFF09\u5185\uFF0C\u4EAB\u53D7\u4E0D\u5230\u884C\u7684 row-gap 16px\uFF1B
|
|
75
|
-
\u5C55\u5F00\u6001\u8865 margin-bottom \u5BF9\u9F50\u884C\u95F4\u8282\u594F\uFF1B\u6536\u8D77\u6001\u884C\u5DF2\u9690\u85CF\uFF0C\u82E5\u4ECD\u8865
|
|
76
|
-
margin \u4F1A\u4E0E\u5757\u95F4 gap \u53E0\u52A0\u6210 32px\uFF0C\u6240\u4EE5\u6536\u8D77\u6001\u4E3A 0\u3002 */
|
|
77
|
-
margin-bottom: 0;
|
|
78
|
-
padding: 0;
|
|
79
|
-
border: none;
|
|
80
|
-
background: transparent;
|
|
81
|
-
color: var(--dsw-alias-label-primary);
|
|
82
|
-
font: 400 14px/24px system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
83
|
-
text-align: left;
|
|
84
|
-
cursor: pointer;
|
|
85
|
-
user-select: none;
|
|
86
|
-
/* \u5C55\u5F00\u6001\u8865\u7684 margin-bottom 16px \u7531 aria-expanded/has-body \u7FFB\u8F6C\u9A71\u52A8\uFF0C
|
|
87
|
-
\u4E00\u5E27\u77AC\u5F00\uFF08\u4E0E\u4E09\u7EA7\u884C display \u7FFB\u8F6C\u540C pass \u540C\u5E27\uFF0C\u65E0\u4E0B\u63A8\uFF09\uFF1B\u6536\u8D77\u65B9\u5411
|
|
88
|
-
\u7531 JS \u4FA7\u9489\u4F4F\u95F4\u8DDD\uFF08\u6536\u8D77 fade \u671F\u95F4\u5185\u8054 16px\uFF0C\u6700\u540E\u4E00\u6761\u5728\u9014\u6E10\u9690 settle
|
|
89
|
-
\u540E\u5F52\u96F6\uFF0C\u89C1 reconcileBlock / hasPendingCollapse\uFF09\u3002\u4E0D\u8BBE CSS transition
|
|
90
|
-
\u2014\u2014v13 \u7684\u8FC7\u6E21\u4E0E chip \u5143\u7D20\u751F\u547D\u5468\u671F\u968F\u673A\u4EA4\u4E92\uFF0C\u4EA7\u751F\u5C55\u5F00\u65B9\u5411\u53CC\u91CD\u4EBA\u683C
|
|
91
|
-
\uFF08\u590D\u7528\u5143\u7D20\u7F13\u52A8\u4E0B\u63A8\u4E09\u7EA7\u884C vs \u65B0\u5EFA\u5143\u7D20\u77AC\u5F00\uFF09\uFF0C\u540C\u7C7B\u578B\u5757\u4E0D\u4E00\u81F4\u3002 */
|
|
92
|
-
}
|
|
93
|
-
.dshcf-chip[aria-expanded="true"],
|
|
94
|
-
.dshcf-chip.dshcf-has-body {
|
|
95
|
-
margin-bottom: 16px;
|
|
96
|
-
}
|
|
97
|
-
/* context \u7B49 before-mounted chip \u662F flow \u7684\u76F4\u63A5\u5B50\u9879\uFF0C\u5DF2\u7ECF\u4EAB\u53D7\u5BBF\u4E3B
|
|
98
|
-
row-gap: 16px\uFF1B\u5C55\u5F00\u65F6\u4E0D\u80FD\u518D\u53E0\u52A0\u81EA\u8EAB margin\uFF0C\u5426\u5219\u4E8C\u7EA7\u5230\u4E09\u7EA7\u4F1A\u53D8 32px\u3002 */
|
|
99
|
-
.dshcf-chip.dshcf-flow-chip {
|
|
100
|
-
margin-bottom: 0;
|
|
101
|
-
}
|
|
102
|
-
.dshcf-chip:hover {
|
|
103
|
-
background: transparent;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/* leading\uFF1A\u56FA\u5B9A 14x14\uFF08\u601D\u8003\u5757 = \u539F\u751F think \u56FE\u6807\uFF1B\u5DE5\u5177\u5757 = \u539F\u751F command
|
|
107
|
-
\u56FE\u6807 IconApiOutline14\uFF0C\u514B\u9686\u81EA\u771F\u5B9E GenericCommandCard leading\uFF0C\u627E\u4E0D\u5230\u65F6
|
|
108
|
-
\u9000\u56DE\u7EC8\u7AEF\u5C0F\u65B9\u5757\uFF09\uFF0C\u884C\u9AD8 24px \u4E0E\u539F\u751F\u884C\u5BF9\u9F50\uFF1B\u8FD0\u884C\u4E2D\u8DF3\u52A8\u3002svg \u5C3A\u5BF8\u7531\u5404\u81EA
|
|
109
|
-
width/height \u5C5E\u6027\u51B3\u5B9A\uFF08command 14x14\u3001think 14x14\u3001\u7EC8\u7AEF 12x10 \u515C\u5E95\uFF09\uFF0C
|
|
110
|
-
\u4E0D\u5728\u6B64\u5904\u5F3A\u5236\u3002 */
|
|
111
|
-
.dshcf-chip .dshcf-leading {
|
|
112
|
-
flex: none;
|
|
113
|
-
display: flex;
|
|
114
|
-
align-items: center;
|
|
115
|
-
justify-content: center;
|
|
116
|
-
width: 16px;
|
|
117
|
-
height: 16px;
|
|
118
|
-
}
|
|
119
|
-
.dshcf-chip .dshcf-leading svg {
|
|
120
|
-
display: block;
|
|
121
|
-
color: var(--dsw-alias-label-tertiary);
|
|
122
|
-
}
|
|
123
|
-
.dshcf-chip.running .dshcf-leading svg {
|
|
124
|
-
/* \u8FD0\u884C\u8272\u4FDD\u7559\uFF1B\u56FE\u6807\u8DF3\u52A8\u52A8\u753B\u5DF2\u6309\u7528\u6237\u8981\u6C42\u79FB\u9664\u3002 */
|
|
125
|
-
color: var(--dsw-static-deepseek-500, #4d6bfe);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/* \u8FD0\u884C\u6307\u793A\u4E09\u4E2A\u70B9\uFF1A\u5DF2\u6309\u7528\u6237\u8981\u6C42\u79FB\u9664\uFF08\u4E0D\u518D\u521B\u5EFA/\u663E\u793A\uFF09\u3002 */
|
|
129
|
-
|
|
130
|
-
/* \u51FA\u9519\u7EA2 / \u4E2D\u65AD\u7425\u73C0\uFF08\u9759\u6B62\u6001\uFF09\u3002 */
|
|
131
|
-
.dshcf-chip.error:not(.running) .dshcf-leading svg {
|
|
132
|
-
color: var(--dsw-alias-state-error-primary, #e5484d);
|
|
133
|
-
}
|
|
134
|
-
.dshcf-chip.stopped:not(.running) .dshcf-leading svg {
|
|
135
|
-
color: var(--dsw-alias-state-warning-primary, #f5a524);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
.dshcf-chip .dshcf-chip-title {
|
|
139
|
-
flex: none;
|
|
140
|
-
font-weight: 400;
|
|
141
|
-
max-width: 70%;
|
|
142
|
-
overflow: hidden;
|
|
143
|
-
text-overflow: ellipsis;
|
|
144
|
-
white-space: nowrap;
|
|
145
|
-
}
|
|
146
|
-
.dshcf-chip .dshcf-chip-sep {
|
|
147
|
-
flex: none;
|
|
148
|
-
width: 2px;
|
|
149
|
-
height: 2px;
|
|
150
|
-
border-radius: 1px;
|
|
151
|
-
background: var(--dsw-alias-label-caption, rgba(127, 127, 127, 0.5));
|
|
152
|
-
}
|
|
153
|
-
/* \u6458\u8981\u4E0D\u6491\u6EE1\uFF08flex 0 1\uFF09\uFF0C\u8BA9 chevron \u7D27\u8DDF\u5728\u6587\u672C\u53F3\u65B9\u800C\u975E\u884C\u5C3E\u3002 */
|
|
154
|
-
.dshcf-chip .dshcf-chip-summary {
|
|
155
|
-
flex: 0 1 auto;
|
|
156
|
-
min-width: 0;
|
|
157
|
-
max-width: 100%;
|
|
158
|
-
overflow: hidden;
|
|
159
|
-
text-overflow: ellipsis;
|
|
160
|
-
white-space: nowrap;
|
|
161
|
-
}
|
|
162
|
-
/* running \u6458\u8981\uFF1A\u8DDF\u968F\u6EDA\u52A8\u663E\u793A\u6700\u65B0\u5185\u5BB9\uFF08text-overflow: clip\uFF0C\u539F\u751F\u540C\u6B3E\uFF09\u3002 */
|
|
163
|
-
.dshcf-chip.running .dshcf-chip-summary {
|
|
164
|
-
text-overflow: clip;
|
|
165
|
-
}
|
|
166
|
-
/* \u6298\u53E0\u884C\u6587\u5B57\uFF1A\u590D\u7528 DSH \u539F\u751F label token\uFF08\u5DE5\u5177\u884C\u540C\u6E90\uFF09\uFF0C\u533A\u522B\u4E8E\u6B63\u6587\u7EAF\u767D\u3002 */
|
|
167
|
-
.dshcf-chip .dshcf-chip-title {
|
|
168
|
-
color: var(--dsw-alias-label-primary);
|
|
169
|
-
}
|
|
170
|
-
.dshcf-chip .dshcf-chip-summary {
|
|
171
|
-
color: var(--dsw-alias-label-tertiary);
|
|
172
|
-
}
|
|
173
|
-
/* \u5DE5\u5177\u884C\u6458\u8981\uFF08\u547D\u4EE4/\u8DEF\u5F84\uFF09\u7B49\u5BBD\u5B57\u4F53 + \u4EE3\u7801\u886C\u5E95\uFF08\u7D20\u6750 Codex \u540C\u6B3E\uFF09\u3002
|
|
174
|
-
\u884C\u9AD8\u4E0E chip \u4E00\u81F4\uFF0824px\uFF09\uFF0C\u6D41\u5F0F\u66F4\u65B0\u65F6\u6458\u8981\u5355\u884C ellipsis \u4E0D\u6362\u884C\u4E0D\u6491\u9AD8\u3002 */
|
|
175
|
-
.dshcf-chip[data-kind="tool"] .dshcf-chip-summary {
|
|
176
|
-
font-family: ui-monospace, SFMono-Regular, Consolas, "Courier New", monospace;
|
|
177
|
-
font-size: 12px;
|
|
178
|
-
line-height: 24px;
|
|
179
|
-
background: rgba(127, 127, 127, 0.14);
|
|
180
|
-
border-radius: 4px;
|
|
181
|
-
padding: 0 6px;
|
|
182
|
-
}
|
|
183
|
-
.dshcf-chip[data-kind="tool"] .dshcf-chip-summary:empty {
|
|
184
|
-
background: none;
|
|
185
|
-
padding: 0;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/* \u8FD0\u884C\u4E2D\u6587\u5B57\u4F7F\u7528\u5E73\u6ED1\u547C\u5438\u52A8\u753B\uFF08Pulse\uFF09\uFF0C\u9002\u914D\u6D45\u8272/\u6DF1\u8272\u4E3B\u9898\uFF0C\u907F\u514D background-clip \u88C1\u5207\u95EE\u9898\u3002 */
|
|
189
|
-
.dshcf-chip.running .dshcf-chip-title,
|
|
190
|
-
.dshcf-chip.running .dshcf-chip-summary {
|
|
191
|
-
color: var(--dsw-alias-label-tertiary, #8b8f99);
|
|
192
|
-
-webkit-text-fill-color: currentColor;
|
|
193
|
-
animation: dshcf-pulse 1.6s ease-in-out infinite;
|
|
194
|
-
}
|
|
195
|
-
.dshcf-chip.running[data-kind="tool"] .dshcf-chip-summary {
|
|
196
|
-
background: transparent;
|
|
197
|
-
}
|
|
198
|
-
@keyframes dshcf-pulse {
|
|
199
|
-
0%, 100% { opacity: 1; }
|
|
200
|
-
50% { opacity: 0.5; }
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/* "\u5DF2\u5904\u7406"\u884C\uFF1A\u6700\u7EC8\u8F93\u51FA\u51FA\u73B0\u540E\u5DE5\u4F5C\u8FC7\u7A0B\u6574\u4F53\u9690\u85CF\uFF0C\u53EA\u7559\u8FD9\u4E00\u884C + \u65F6\u957F\u3002
|
|
204
|
-
\u5B57\u4F53\u4E0E\u4E8C\u7EA7 chip \u5BF9\u9F50\uFF0814px/24px\uFF09\uFF0C\u5DE6\u53F3\u65E0\u5185\u8FB9\u8DDD\uFF08\u4E0E\u6B63\u6587\u5DE6\u7F18\u5BF9\u9F50\uFF09\u3002 */
|
|
205
|
-
.dshcf-processed {
|
|
206
|
-
display: inline-flex;
|
|
207
|
-
align-self: flex-start;
|
|
208
|
-
width: fit-content;
|
|
209
|
-
max-width: 100%;
|
|
210
|
-
align-items: center;
|
|
211
|
-
gap: 6px;
|
|
212
|
-
padding: 2px 0;
|
|
213
|
-
border: none;
|
|
214
|
-
background: none;
|
|
215
|
-
font: 400 14px/24px system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
216
|
-
/* \u5BF9\u9F50 DSH \u539F\u751F\u5DE5\u5177\u884C\u6458\u8981\u7684\u6B21\u7EA7\u5C42\u7EA7\uFF08label-tertiary\uFF09\u3002 */
|
|
217
|
-
color: var(--dsw-alias-label-tertiary);
|
|
218
|
-
cursor: pointer;
|
|
219
|
-
user-select: none;
|
|
220
|
-
border-radius: 4px;
|
|
221
|
-
transition: color 0.15s ease;
|
|
222
|
-
}
|
|
223
|
-
.dshcf-processed:hover {
|
|
224
|
-
color: var(--dsw-alias-label-primary);
|
|
225
|
-
background: transparent;
|
|
226
|
-
}
|
|
227
|
-
.dshcf-processed:focus-visible {
|
|
228
|
-
outline: 2px solid var(--dsw-alias-state-focus-ring, rgba(77, 107, 254, 0.8));
|
|
229
|
-
outline-offset: 2px;
|
|
230
|
-
}
|
|
231
|
-
/* \u6298\u53E0\u7BAD\u5934\uFF1A\u4F7F\u7528 DSH \u539F\u751F IconChevronDownOutline14 \u7684 14x14 path\u3002 */
|
|
232
|
-
.dshcf-processed .dshcf-processed-chevron {
|
|
233
|
-
display: inline-flex;
|
|
234
|
-
flex: none;
|
|
235
|
-
width: 14px;
|
|
236
|
-
height: 14px;
|
|
237
|
-
color: var(--dsw-alias-label-tertiary);
|
|
238
|
-
opacity: 0.55;
|
|
239
|
-
transform: rotate(-90deg);
|
|
240
|
-
transition: transform 0.12s ease, opacity 0.1s ease;
|
|
241
|
-
}
|
|
242
|
-
.dshcf-processed:hover .dshcf-processed-chevron {
|
|
243
|
-
opacity: 0.9;
|
|
244
|
-
}
|
|
245
|
-
.dshcf-processed[aria-expanded="true"] .dshcf-processed-chevron {
|
|
246
|
-
transform: rotate(0deg);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/* \u4E09\u7EA7\u5408\u5E76\u601D\u8003\u884C\uFF1A\u5C55\u5F00\u4E8C\u7EA7\u540E\u8FDE\u7EED\u601D\u8003\u5408\u5E76\u4E3A\u4E00\u884C\uFF08\u6807\u9898 = \u7B2C\u4E00\u884C\u601D\u8003\u5185\u5BB9\uFF09\u3002
|
|
250
|
-
\u6837\u5F0F\u4E0E chip \u540C\u65CF\uFF0816px \u56FE\u6807\u76D2\u300114px/24px\u3001\u539F\u751F label token \u8272\uFF09\u3002 */
|
|
251
|
-
.dshcf-merged-think {
|
|
252
|
-
box-sizing: border-box;
|
|
253
|
-
display: flex;
|
|
254
|
-
align-self: flex-start;
|
|
255
|
-
align-items: center;
|
|
256
|
-
gap: 6px;
|
|
257
|
-
width: fit-content;
|
|
258
|
-
max-width: 100%;
|
|
259
|
-
min-height: 24px;
|
|
260
|
-
margin: 0;
|
|
261
|
-
padding: 0;
|
|
262
|
-
border: none;
|
|
263
|
-
background: transparent;
|
|
264
|
-
color: var(--dsw-alias-label-primary);
|
|
265
|
-
font: 400 14px/24px system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
266
|
-
text-align: left;
|
|
267
|
-
cursor: pointer;
|
|
268
|
-
user-select: none;
|
|
269
|
-
}
|
|
270
|
-
.dshcf-merged-think .dshcf-leading {
|
|
271
|
-
flex: none;
|
|
272
|
-
display: flex;
|
|
273
|
-
align-items: center;
|
|
274
|
-
justify-content: center;
|
|
275
|
-
width: 16px;
|
|
276
|
-
height: 16px;
|
|
277
|
-
}
|
|
278
|
-
.dshcf-merged-think .dshcf-leading svg {
|
|
279
|
-
display: block;
|
|
280
|
-
color: var(--dsw-alias-label-tertiary);
|
|
281
|
-
}
|
|
282
|
-
.dshcf-merged-think .dshcf-merged-title {
|
|
283
|
-
flex: 0 1 auto;
|
|
284
|
-
min-width: 0;
|
|
285
|
-
max-width: 85%;
|
|
286
|
-
overflow: hidden;
|
|
287
|
-
text-overflow: ellipsis;
|
|
288
|
-
white-space: nowrap;
|
|
289
|
-
color: var(--dsw-alias-label-secondary);
|
|
290
|
-
font-weight: 400;
|
|
291
|
-
}
|
|
292
|
-
.dshcf-merged-think .dshcf-chevron {
|
|
293
|
-
flex: none;
|
|
294
|
-
width: 14px;
|
|
295
|
-
height: 14px;
|
|
296
|
-
color: var(--dsw-alias-label-secondary);
|
|
297
|
-
opacity: 0.55;
|
|
298
|
-
transform: rotate(-90deg);
|
|
299
|
-
transition: transform 0.12s ease, opacity 0.1s ease;
|
|
300
|
-
}
|
|
301
|
-
.dshcf-merged-think:hover .dshcf-chevron,
|
|
302
|
-
.dshcf-merged-think:focus-visible .dshcf-chevron {
|
|
303
|
-
opacity: 0.9;
|
|
304
|
-
}
|
|
305
|
-
.dshcf-merged-think[aria-expanded="true"] .dshcf-chevron {
|
|
306
|
-
transform: rotate(0deg);
|
|
307
|
-
}
|
|
308
|
-
/* \u5408\u5E76\u601D\u8003\u5185\u5BB9\u5757\uFF1A\u56DB\u4E2A\u601D\u8003\u5408\u5E76\u4E3A\u4E00\u4E2A\u6574\u4F53\uFF08\u5BF9\u9F50\u56FE\u6807\u53F3\u4FA7\u7F29\u8FDB\uFF09\u3002 */
|
|
309
|
-
.dshcf-merged-body {
|
|
310
|
-
margin: 0 0 16px;
|
|
311
|
-
padding-left: 22px;
|
|
312
|
-
color: var(--dsw-alias-label-secondary);
|
|
313
|
-
font: 400 13px/22px system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
314
|
-
white-space: pre-wrap;
|
|
315
|
-
word-break: break-word;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/* chevron\uFF1A\u9ED8\u8BA4\u9690\u85CF\uFF0Chover/focus \u6D6E\u73B0\uFF0C\u5C55\u5F00\u65F6\u65CB\u8F6C 90\xB0\uFF08Codex \u540C\u6B3E\uFF09\u3002 */
|
|
319
|
-
.dshcf-chip .dshcf-chevron {
|
|
320
|
-
flex: none;
|
|
321
|
-
width: 14px;
|
|
322
|
-
height: 14px;
|
|
323
|
-
color: var(--dsw-alias-label-tertiary);
|
|
324
|
-
opacity: 0.5;
|
|
325
|
-
transform: rotate(-90deg);
|
|
326
|
-
transition: opacity 0.1s ease, transform 0.12s ease;
|
|
327
|
-
}
|
|
328
|
-
.dshcf-chip:hover .dshcf-chevron,
|
|
329
|
-
.dshcf-chip:focus-visible .dshcf-chevron,
|
|
330
|
-
.dshcf-chip[aria-expanded="true"] .dshcf-chevron {
|
|
331
|
-
opacity: 0.9;
|
|
332
|
-
}
|
|
333
|
-
.dshcf-chip[aria-expanded="true"] .dshcf-chevron {
|
|
334
|
-
transform: rotate(0deg);
|
|
335
|
-
}
|
|
336
|
-
.dshcf-chip:focus-visible {
|
|
337
|
-
outline: 2px solid var(--dsw-alias-state-focus-ring, rgba(77, 107, 254, 0.8));
|
|
338
|
-
outline-offset: 2px;
|
|
339
|
-
border-radius: 4px;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
@media (prefers-reduced-motion: reduce) {
|
|
343
|
-
.dshcf-chip.running .dshcf-leading svg { animation: none; }
|
|
344
|
-
.dshcf-chip.running .dshcf-chip-title,
|
|
345
|
-
.dshcf-chip.running .dshcf-chip-summary {
|
|
346
|
-
animation: none;
|
|
347
|
-
opacity: 1;
|
|
36
|
+
// src/host-contract.ts
|
|
37
|
+
var FLOW = "[data-chat-flow]";
|
|
38
|
+
var SEAT = "[data-chat-flow-kind]";
|
|
39
|
+
var OWNED = "[data-dshcf-group]";
|
|
40
|
+
var FOLDED = "data-dshcf-folded";
|
|
41
|
+
var NATIVE_HIDDEN = "[data-turn-process-hidden], [data-turn-process-inline]";
|
|
42
|
+
function elementOf(node) {
|
|
43
|
+
return node.nodeType === 1 ? node : node.parentElement;
|
|
348
44
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
this.blockExpanded = /* @__PURE__ */ new Map();
|
|
371
|
-
/** host → 三级合并思考行(展开二级后连续思考合并显示为一个三级行)。 */
|
|
372
|
-
this.mergedThinks = /* @__PURE__ */ new Map();
|
|
373
|
-
/** 合并思考行的展开状态(true = 显示合并内容块)。 */
|
|
374
|
-
this.mergedExpanded = /* @__PURE__ */ new WeakSet();
|
|
375
|
-
/** 合并内容缓存(首次从原生行读取后保存,pass 重建内容块时不再重新展开原生行)。 */
|
|
376
|
-
this.mergedBodyTexts = /* @__PURE__ */ new WeakMap();
|
|
377
|
-
/** 合并行标题缓存(原生行展开态提取不到摘要时保持首次标题,不丢成“思考”)。 */
|
|
378
|
-
this.mergedTitles = /* @__PURE__ */ new WeakMap();
|
|
379
|
-
/** 稳定 segment key → 一级折叠行与展开状态。 */
|
|
380
|
-
this.segmentStates = /* @__PURE__ */ new Map();
|
|
381
|
-
/** segment 首次观察到 running 的时间,用于没有官方时长的实时回合。 */
|
|
382
|
-
this.runningSince = /* @__PURE__ */ new Map();
|
|
383
|
-
/** 曾完成过的 segment key:段恢复运行时据此重开本地计时,防止重新结算
|
|
384
|
-
* 的本地时长吞掉完成间隙。 */
|
|
385
|
-
this.completedOnce = /* @__PURE__ */ new Set();
|
|
386
|
-
/** 插件改写 display 前的精确原值;受控集合用于分类漂移和 stop() 恢复。 */
|
|
387
|
-
this.originalDisplay = /* @__PURE__ */ new WeakMap();
|
|
388
|
-
this.controlledDisplay = /* @__PURE__ */ new Set();
|
|
389
|
-
/** 元素 → 插件最后确保的 display 值:恢复前与当前内联值比对,漂移即视为
|
|
390
|
-
* 外部介入(镜像 turnStatusTexts 的 original/written 双快照守卫,issue #11)。 */
|
|
391
|
-
this.writtenDisplay = /* @__PURE__ */ new WeakMap();
|
|
392
|
-
/** 被改写为状态提示词的原生状态文本:original = 宿主原文(卸载还原用),
|
|
393
|
-
* written = 插件最后一次写入的值(仅当节点仍等于它时才还原,避免覆盖
|
|
394
|
-
* 宿主在插件写入之后的状态更新)。 */
|
|
395
|
-
this.turnStatusTexts = /* @__PURE__ */ new Map();
|
|
396
|
-
/** 正文判定缓存(消息元素 → 有无正文):流式期间只有被 mutation 命中的
|
|
397
|
-
* 消息失效重算,历史消息跨 pass 复用,避免每帧全量 TreeWalker。 */
|
|
398
|
-
this.bodyTextCache = /* @__PURE__ */ new WeakMap();
|
|
399
|
-
/** 自上次 pass 以来子树发生变化的 flow 顶层消息;pass 开头统一失效。 */
|
|
400
|
-
this.dirtyMessages = /* @__PURE__ */ new Set();
|
|
401
|
-
/** 在途显示动画(元素 → 记录):冲突仲裁、记账对齐与生命周期清理的依据。
|
|
402
|
-
/** 在途显示动画(元素 → 记录):冲突仲裁、记账对齐与生命周期清理的依据。
|
|
403
|
-
* 用 Map 不用 WeakMap——switchFlow/stop 需要遍历全量 cancel。 */
|
|
404
|
-
this.pendingAnims = /* @__PURE__ */ new Map();
|
|
405
|
-
/** 手势点击的一次性可动画 block key;segment 级点击另保留中间正文的门控。 */
|
|
406
|
-
this.animatableKeys = /* @__PURE__ */ new Set();
|
|
407
|
-
/** segment 点击时只让点击前已存在的 block 播放 reveal;流式中新出现的
|
|
408
|
-
* 临时分裂块直接显示,避免分类收敛时留下半透明 stale chip。 */
|
|
409
|
-
this.animatableSegmentBlocks = /* @__PURE__ */ new Map();
|
|
410
|
-
/** 外部变更对账定时器句柄(自重排 setTimeout 链,见 armAuditLoop)。 */
|
|
411
|
-
this.auditTimer = 0;
|
|
412
|
-
/** 上一轮 pass 记录的关键元素内联 display,用于 audit 轻量检测漂移。
|
|
413
|
-
* audit 只读这份快照,不在页面稳定时重新执行完整 pass。 */
|
|
414
|
-
this.auditDisplays = /* @__PURE__ */ new Map();
|
|
415
|
-
/** 自上次 pass 以来 flow 子树发生过结构变化(childList)或正文判定翻转:
|
|
416
|
-
* 为 true 时 pass 重建分块快照,否则复用 currentBlocks——characterData/
|
|
417
|
-
* attributes 批次(流式文本、data-state 翻转)不改变块结构,跳过全量
|
|
418
|
-
* querySelectorAll 重扫(issue #14:长会话下每轮重扫造成主线程卡顿)。 */
|
|
419
|
-
this.structureDirty = true;
|
|
420
|
-
/** 本轮 pass 的原生 turn 摘要快照(turn id → 开合态):每轮重建,不跨轮复用;
|
|
421
|
-
* data-open 翻转不改变块结构也能驱动重算,见 buildNativeTurnMap。 */
|
|
422
|
-
this.nativeTurns = /* @__PURE__ */ new Map();
|
|
423
|
-
/** 被折叠掏空后藏起的中间包装层(真机 44px vs 28px 真因:think 行全隐后
|
|
424
|
-
* 其父容器变零高度空壳,仍作为 flex item 参与父级 gap,凭空多出 16px)。
|
|
425
|
-
* 内容恢复(展开/流式追加/块转世)时同函数恢复显示;switchFlow 清空。 */
|
|
426
|
-
this.emptiedWrappers = /* @__PURE__ */ new Set();
|
|
427
|
-
/** 本轮 pass 内有过 display 实写(hide/restore 瞬时路径)。空洞发现的触发
|
|
428
|
-
* 条件之一:纯 display 收放不产生 childList,不触发 structureDirty。
|
|
429
|
-
* settle 触发的后续 pass 则由 settleFired 覆盖。读后即清(见发现调用点)。 */
|
|
430
|
-
this.displayTouched = false;
|
|
431
|
-
/** 有 fade 自然结算过(chipSettle 跑过)。结算本身不写 display,但它意味着
|
|
432
|
-
* 某行刚变隐藏——空洞可能刚形成。 */
|
|
433
|
-
this.settleFired = false;
|
|
434
|
-
/** 滚动稳定化(issue #14):flow 最近的滚动容器缓存(按 flow 身份失效)。 */
|
|
435
|
-
this.scrollContainer = null;
|
|
436
|
-
this.scrollContainerFlow = null;
|
|
437
|
-
/** 回到前台立即补一轮对账;后台 tab 由 document.hidden 门控跳过。 */
|
|
438
|
-
this.onVisibilityChange = () => {
|
|
439
|
-
if (typeof document === "undefined" || document.hidden !== true) this.schedule();
|
|
440
|
-
};
|
|
441
|
-
this.statusTextProvider = statusTextProvider2 ?? (() => DEFAULT_STATUS_TEXT);
|
|
442
|
-
this.auditIntervalMs = options?.auditIntervalMs ?? AUDIT_TICK_MS;
|
|
443
|
-
}
|
|
444
|
-
/** 设置变更后重跑一轮,让状态提示词立即生效。 */
|
|
445
|
-
refresh() {
|
|
446
|
-
this.schedule();
|
|
447
|
-
}
|
|
448
|
-
start() {
|
|
449
|
-
if (this.disposed || this.started || this.waitingForBody) return;
|
|
450
|
-
if (typeof document === "undefined") return;
|
|
451
|
-
if (document.body === null) {
|
|
452
|
-
if (typeof document.addEventListener !== "function") return;
|
|
453
|
-
this.waitingForBody = true;
|
|
454
|
-
document.addEventListener("DOMContentLoaded", this.onDomContentLoaded, { once: true });
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
injectStyle();
|
|
458
|
-
try {
|
|
459
|
-
this.observer = new MutationObserver((records) => {
|
|
460
|
-
if (this.shouldSchedule(records)) {
|
|
461
|
-
this.markDirty(records);
|
|
462
|
-
this.schedule();
|
|
463
|
-
}
|
|
464
|
-
});
|
|
465
|
-
this.observer.observe(document.body, {
|
|
466
|
-
childList: true,
|
|
467
|
-
subtree: true,
|
|
468
|
-
attributes: true,
|
|
469
|
-
// 原生 TurnProcess 开合只改 attribute(成员 data-turn-process-hidden、
|
|
470
|
-
// 摘要按钮 data-open),必须进过滤器才能即时调度;插件自身从不写
|
|
471
|
-
// 这两个属性,不会自激。
|
|
472
|
-
attributeFilter: ["data-selected", "data-state", "data-turn-process-hidden", "data-open"],
|
|
473
|
-
// 流式文本更新(React 改 text node 的 data)属于 characterData
|
|
474
|
-
// mutation:不观察则二级摘要/滚动跟随只能靠偶发结构变化驱动,
|
|
475
|
-
// 变成“隔几秒跳一次”。所有文本写入都有守卫(值不变不写),
|
|
476
|
-
// 不会自激。
|
|
477
|
-
characterData: true
|
|
478
|
-
});
|
|
479
|
-
this.started = true;
|
|
480
|
-
this.armAuditLoop();
|
|
481
|
-
this.schedule();
|
|
482
|
-
} catch (error) {
|
|
483
|
-
this.observer?.disconnect();
|
|
484
|
-
this.observer = null;
|
|
485
|
-
this.started = false;
|
|
486
|
-
this.reportError(error);
|
|
487
|
-
throw error;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
/** 外部显示变更对账循环(issue #11 Bug B):外部对宿主行的 style 写入不产生
|
|
491
|
-
* observer record(style 不在 attributeFilter 内,监听会因插件自身直写 style
|
|
492
|
-
* 自激),改用低频轻量对账兜底——发现漂移后才由 pass 收敛;
|
|
493
|
-
* 后台 tab 由 document.hidden 门控跳过,回前台由 visibilitychange 立即补一轮。
|
|
494
|
-
* 用自重排 setTimeout 链而非 setInterval:与 schedule 的兜底定时器同源,
|
|
495
|
-
* 测试桩 clearTimers 后链条自然熄灭。 */
|
|
496
|
-
armAuditLoop() {
|
|
497
|
-
if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
|
|
498
|
-
document.addEventListener("visibilitychange", this.onVisibilityChange);
|
|
499
|
-
}
|
|
500
|
-
this.rearmAudit();
|
|
501
|
-
}
|
|
502
|
-
rearmAudit() {
|
|
503
|
-
if (this.disposed || this.auditTimer !== 0) return;
|
|
504
|
-
this.auditTimer = setTimeout(() => {
|
|
505
|
-
this.auditTimer = 0;
|
|
506
|
-
if (this.disposed) return;
|
|
507
|
-
if (typeof document !== "undefined" && document.hidden === true) {
|
|
508
|
-
this.rearmAudit();
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
this.audit();
|
|
512
|
-
this.rearmAudit();
|
|
513
|
-
}, this.auditIntervalMs);
|
|
45
|
+
function pluginOwned(node) {
|
|
46
|
+
return elementOf(node)?.closest(OWNED) != null;
|
|
47
|
+
}
|
|
48
|
+
function seatOf(node, flow) {
|
|
49
|
+
let element = elementOf(node);
|
|
50
|
+
while (element !== null && element.parentElement !== flow) element = element.parentElement;
|
|
51
|
+
return element instanceof HTMLElement && element.matches(SEAT) ? element : null;
|
|
52
|
+
}
|
|
53
|
+
function seatKey(seat) {
|
|
54
|
+
return seat.getAttribute("data-chat-flow-key") ?? seat.getAttribute("data-chat-anchor-key");
|
|
55
|
+
}
|
|
56
|
+
function nativeHidden(row, flow) {
|
|
57
|
+
const owner = row.closest(NATIVE_HIDDEN);
|
|
58
|
+
return owner !== null && flow.contains(owner);
|
|
59
|
+
}
|
|
60
|
+
function nativeOwnsHidden(element) {
|
|
61
|
+
return element.matches(NATIVE_HIDDEN);
|
|
62
|
+
}
|
|
63
|
+
function findFlow() {
|
|
64
|
+
for (const flow of document.querySelectorAll(FLOW)) {
|
|
65
|
+
if (flow.getClientRects().length > 0 && getComputedStyle(flow).visibility !== "hidden") return flow;
|
|
514
66
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
if (
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
function nativeControls(flow) {
|
|
70
|
+
const result = /* @__PURE__ */ new Map();
|
|
71
|
+
for (const button of flow.querySelectorAll("button[data-turn-process]")) {
|
|
72
|
+
const turn = button.getAttribute("data-turn-process");
|
|
73
|
+
if (turn !== null && button.closest('[data-chat-flow-kind="turn-process"]') !== null) result.set(turn, button);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
function nativeCollapsed(button) {
|
|
78
|
+
if (button === void 0 || !button.isConnected) return false;
|
|
79
|
+
const expanded = button.getAttribute("aria-expanded");
|
|
80
|
+
return expanded === "false" || expanded === null && !button.hasAttribute("data-open");
|
|
81
|
+
}
|
|
82
|
+
function openNative(button) {
|
|
83
|
+
if (button !== void 0 && nativeCollapsed(button)) {
|
|
84
|
+
const focused = button.ownerDocument.activeElement;
|
|
85
|
+
button.click();
|
|
86
|
+
if (focused instanceof HTMLElement && focused.isConnected && focused !== button) {
|
|
87
|
+
focused.focus({ preventScroll: true });
|
|
88
|
+
if (focused === button.ownerDocument.body && button.ownerDocument.activeElement === button) button.blur();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
var OBSERVED_ATTRIBUTES = [
|
|
93
|
+
"data-chat-flow-key",
|
|
94
|
+
"data-chat-anchor-key",
|
|
95
|
+
"data-chat-flow-kind",
|
|
96
|
+
"data-chat-turn",
|
|
97
|
+
"data-variant",
|
|
98
|
+
"data-tool",
|
|
99
|
+
"data-state",
|
|
100
|
+
"data-selected",
|
|
101
|
+
"data-open",
|
|
102
|
+
"data-expanded",
|
|
103
|
+
"aria-expanded",
|
|
104
|
+
"data-turn-process-member",
|
|
105
|
+
"data-turn-process-hidden",
|
|
106
|
+
"data-turn-process-inline",
|
|
107
|
+
"hidden",
|
|
108
|
+
"style",
|
|
109
|
+
"class"
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
// src/work-model.ts
|
|
113
|
+
var ASSISTANT_KINDS = /* @__PURE__ */ new Set(["assistant-step", "assistant"]);
|
|
114
|
+
var CONTEXT_KINDS = /* @__PURE__ */ new Set(["context", "system-prompt"]);
|
|
115
|
+
var COMMAND_KINDS = /* @__PURE__ */ new Set(["command", "manual-compaction"]);
|
|
116
|
+
function readSeat(seat, fallback) {
|
|
117
|
+
const kind = seat.getAttribute("data-chat-flow-kind");
|
|
118
|
+
const key = seatKey(seat);
|
|
119
|
+
const turn = seat.getAttribute("data-chat-turn") || null;
|
|
120
|
+
const result = { tokens: [], items: [] };
|
|
121
|
+
if (kind === "turn-process") return result;
|
|
122
|
+
if (key === null || kind === null) {
|
|
123
|
+
result.tokens.push({ type: "boundary", id: `unknown:${key ?? fallback}`, turn: null, hard: true });
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
const boundary = (id, hard) => ({ type: "boundary", id: `${key}:${id}`, turn, hard });
|
|
127
|
+
const add = (row, workKind, suffix) => {
|
|
128
|
+
const item = {
|
|
129
|
+
id: JSON.stringify([key, workKind, suffix]),
|
|
130
|
+
turn,
|
|
131
|
+
kind: workKind,
|
|
132
|
+
seat,
|
|
133
|
+
row,
|
|
134
|
+
cover: row,
|
|
135
|
+
followNativeTurn: kind === "system-prompt" && turn !== null
|
|
136
|
+
};
|
|
137
|
+
result.items.push(item);
|
|
138
|
+
return item;
|
|
139
|
+
};
|
|
140
|
+
const token = (item) => ({ type: "work", id: item.id, turn, kind: item.kind });
|
|
141
|
+
if (CONTEXT_KINDS.has(kind) || COMMAND_KINDS.has(kind)) {
|
|
142
|
+
if (seat.childNodes.length === 0) return result;
|
|
143
|
+
const item = add(seat, CONTEXT_KINDS.has(kind) ? "context" : "command", "");
|
|
144
|
+
result.tokens.push(token(item));
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
if (!ASSISTANT_KINDS.has(kind) && kind !== "tool-call") {
|
|
148
|
+
result.tokens.push(boundary("fence", true));
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
const roots = /* @__PURE__ */ new Map();
|
|
152
|
+
const paths = /* @__PURE__ */ new Set([seat]);
|
|
153
|
+
const candidates = [...seat.querySelectorAll('[data-chat-call-id], [data-variant="think"]')];
|
|
154
|
+
let thinkIndex = 0;
|
|
155
|
+
for (const row of candidates) {
|
|
156
|
+
if (pluginOwned(row) || row.closest("[data-subcalls]") !== null) continue;
|
|
157
|
+
const outerCall = row.parentElement?.closest("[data-chat-call-id]");
|
|
158
|
+
if (outerCall !== null && outerCall !== void 0 && seat.contains(outerCall)) continue;
|
|
159
|
+
const isTool = row.hasAttribute("data-chat-call-id");
|
|
160
|
+
if (!isTool && row.hasAttribute("data-tool")) continue;
|
|
161
|
+
if (row.querySelector("[data-disclosure-row]") === null) continue;
|
|
162
|
+
const item = add(row, isTool ? "tool" : "think", isTool ? row.getAttribute("data-chat-call-id") : String(thinkIndex++));
|
|
163
|
+
roots.set(row, item);
|
|
164
|
+
for (let parent = row.parentElement; parent !== null && seat.contains(parent); parent = parent.parentElement) paths.add(parent);
|
|
165
|
+
}
|
|
166
|
+
let bodyIndex = 0;
|
|
167
|
+
const visit = (node) => {
|
|
168
|
+
if (pluginOwned(node) || node.nodeType === 8) return [];
|
|
169
|
+
if (node.nodeType === 3) return (node.textContent ?? "").trim() === "" ? [] : [boundary(`body:${bodyIndex++}`, false)];
|
|
170
|
+
if (!(node instanceof Element)) return [];
|
|
171
|
+
if (node.matches("script, style, template")) return [];
|
|
172
|
+
const work = roots.get(node);
|
|
173
|
+
if (work !== void 0) return [token(work)];
|
|
174
|
+
if (!paths.has(node)) {
|
|
175
|
+
return [boundary(`body:${bodyIndex++}`, false)];
|
|
176
|
+
}
|
|
177
|
+
const children = [...node.childNodes].flatMap(visit);
|
|
178
|
+
if (children.length > 0 && children.every((child) => child.type === "work") && node instanceof HTMLElement) {
|
|
179
|
+
const ids = new Set(children.map((child) => child.id));
|
|
180
|
+
for (const item of result.items) if (ids.has(item.id)) item.cover = node;
|
|
181
|
+
}
|
|
182
|
+
return children;
|
|
183
|
+
};
|
|
184
|
+
result.tokens = visit(seat);
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
var WorkModel = class {
|
|
188
|
+
constructor() {
|
|
189
|
+
this.seats = /* @__PURE__ */ new Map();
|
|
190
|
+
this.identities = /* @__PURE__ */ new WeakMap();
|
|
191
|
+
this.sequence = 0;
|
|
192
|
+
}
|
|
193
|
+
read(flow, dirty, force = false) {
|
|
194
|
+
const live = /* @__PURE__ */ new Set();
|
|
195
|
+
const tokens = [];
|
|
196
|
+
const items = /* @__PURE__ */ new Map();
|
|
197
|
+
for (const child of flow.children) {
|
|
198
|
+
if (!(child instanceof HTMLElement) || pluginOwned(child)) continue;
|
|
199
|
+
if (!child.hasAttribute("data-chat-flow-kind")) {
|
|
545
200
|
continue;
|
|
546
201
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
this.
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
stop() {
|
|
555
|
-
this.disposed = true;
|
|
556
|
-
this.started = false;
|
|
557
|
-
this.waitingForBody = false;
|
|
558
|
-
if (typeof document !== "undefined" && typeof document.removeEventListener === "function") {
|
|
559
|
-
document.removeEventListener("DOMContentLoaded", this.onDomContentLoaded);
|
|
560
|
-
}
|
|
561
|
-
if (this.raf !== 0) cancelAnimationFrame(this.raf);
|
|
562
|
-
if (this.timer !== 0) clearTimeout(this.timer);
|
|
563
|
-
if (this.auditTimer !== 0) {
|
|
564
|
-
clearTimeout(this.auditTimer);
|
|
565
|
-
this.auditTimer = 0;
|
|
566
|
-
}
|
|
567
|
-
if (typeof document !== "undefined" && typeof document.removeEventListener === "function") {
|
|
568
|
-
document.removeEventListener("visibilitychange", this.onVisibilityChange);
|
|
569
|
-
}
|
|
570
|
-
this.observer?.disconnect();
|
|
571
|
-
this.switchFlow(null);
|
|
572
|
-
removeStyle();
|
|
573
|
-
}
|
|
574
|
-
/** body 级 observer 只负责发现 flow 替换;已有 flow 外的文本变化不再触发全量扫描。 */
|
|
575
|
-
shouldSchedule(records) {
|
|
576
|
-
if (records.length === 0 || this.flow === null || !this.flow.isConnected) return true;
|
|
577
|
-
return records.some((record) => this.isRelevantMutation(record));
|
|
578
|
-
}
|
|
579
|
-
/** 判断 mutation 是否会影响宿主 flow;插件自有节点的回写直接忽略,
|
|
580
|
-
* 避免“pass 插入 chip → observer 再开一轮 pass”的自激循环。 */
|
|
581
|
-
isRelevantMutation(record) {
|
|
582
|
-
const flow = this.flow;
|
|
583
|
-
if (flow === null || !flow.isConnected) return true;
|
|
584
|
-
if (!nodeWithin(record.target, flow) && !nodeWithin(flow, record.target)) return false;
|
|
585
|
-
if (record.type === "characterData" && record.target instanceof Text && this.turnStatusTexts.has(record.target)) {
|
|
586
|
-
return false;
|
|
587
|
-
}
|
|
588
|
-
const changed = [
|
|
589
|
-
...Array.from(record.addedNodes ?? []),
|
|
590
|
-
...Array.from(record.removedNodes ?? [])
|
|
591
|
-
];
|
|
592
|
-
if (changed.length > 0 && changed.every(isPluginOwnedNode)) return false;
|
|
593
|
-
if (changed.length === 0 && isPluginOwnedNode(record.target)) return false;
|
|
594
|
-
return true;
|
|
595
|
-
}
|
|
596
|
-
/** 记录本批 mutation 命中的 flow 顶层消息,供正文判定缓存定向失效。
|
|
597
|
-
* 从 record.target 沿 parentNode 走到 flow 的直接子级即所属消息。
|
|
598
|
-
* 失效粒度(issue #14):只有 childList 使分块快照失效;flow 直挂层的
|
|
599
|
-
* 插件节点/文本节点与 flow 外的混批记录不影响任何消息的正文判定,跳过
|
|
600
|
-
* 而非全量失效——旧逻辑把它们全部放大成 O(全会话) 的 TreeWalker 重扫,
|
|
601
|
-
* 长会话流式期间每帧如此。空批次仍保守全量失效(测试桩的调度通知)。 */
|
|
602
|
-
markDirty(records) {
|
|
603
|
-
const flow = this.flow;
|
|
604
|
-
if (flow === null || !flow.isConnected) return;
|
|
605
|
-
if (records.length === 0) {
|
|
606
|
-
this.bodyTextCache = /* @__PURE__ */ new WeakMap();
|
|
607
|
-
this.dirtyMessages.clear();
|
|
608
|
-
this.structureDirty = true;
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
for (const record of records.filter((record2) => this.isRelevantMutation(record2))) {
|
|
612
|
-
if (record.type === "childList") this.structureDirty = true;
|
|
613
|
-
if (record.target === flow) {
|
|
614
|
-
for (const node of record.addedNodes ?? []) {
|
|
615
|
-
if (node instanceof HTMLElement) this.dirtyMessages.add(node);
|
|
202
|
+
live.add(child);
|
|
203
|
+
let snapshot = this.seats.get(child);
|
|
204
|
+
if (force || snapshot === void 0 || dirty.has(child)) {
|
|
205
|
+
let identity = this.identities.get(child);
|
|
206
|
+
if (identity === void 0) {
|
|
207
|
+
identity = String(++this.sequence);
|
|
208
|
+
this.identities.set(child, identity);
|
|
616
209
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
const owner = flowChildOwner(record.target, flow);
|
|
620
|
-
if (owner !== null) this.dirtyMessages.add(owner);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
schedule() {
|
|
624
|
-
if (this.disposed || this.raf !== 0) return;
|
|
625
|
-
this.raf = requestAnimationFrame(() => {
|
|
626
|
-
this.raf = 0;
|
|
627
|
-
if (this.timer !== 0) {
|
|
628
|
-
clearTimeout(this.timer);
|
|
629
|
-
this.timer = 0;
|
|
630
|
-
}
|
|
631
|
-
this.runPass();
|
|
632
|
-
});
|
|
633
|
-
if (this.timer !== 0) clearTimeout(this.timer);
|
|
634
|
-
this.timer = setTimeout(() => {
|
|
635
|
-
this.timer = 0;
|
|
636
|
-
if (this.raf !== 0) {
|
|
637
|
-
cancelAnimationFrame(this.raf);
|
|
638
|
-
this.raf = 0;
|
|
639
|
-
this.runPass();
|
|
210
|
+
snapshot = readSeat(child, identity);
|
|
211
|
+
this.seats.set(child, snapshot);
|
|
640
212
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
/** 异步 observer 异常不能静默杀死协调器;保留非可视诊断并允许后续 mutation 重试。 */
|
|
644
|
-
runPass() {
|
|
645
|
-
try {
|
|
646
|
-
this.pass();
|
|
647
|
-
this.lastPassError = "";
|
|
648
|
-
const style = document.getElementById(STYLE_ID);
|
|
649
|
-
style?.setAttribute("data-dshcf-state", "active");
|
|
650
|
-
style?.removeAttribute("data-dshcf-error");
|
|
651
|
-
} catch (error) {
|
|
652
|
-
this.reportError(error);
|
|
653
|
-
} finally {
|
|
654
|
-
this.animatableKeys.clear();
|
|
655
|
-
this.animatableSegmentBlocks.clear();
|
|
213
|
+
tokens.push(...snapshot.tokens);
|
|
214
|
+
for (const item of snapshot.items) items.set(item.id, item);
|
|
656
215
|
}
|
|
216
|
+
for (const seat of this.seats.keys()) if (!live.has(seat)) this.seats.delete(seat);
|
|
217
|
+
return { tokens, items };
|
|
657
218
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const style = document.getElementById(STYLE_ID);
|
|
661
|
-
style?.setAttribute("data-dshcf-state", "error");
|
|
662
|
-
style?.setAttribute("data-dshcf-error", message.slice(0, 500));
|
|
663
|
-
if (message === this.lastPassError) return;
|
|
664
|
-
this.lastPassError = message;
|
|
665
|
-
console.error("[dsh-auto-collapse] fold pass failed", error);
|
|
219
|
+
clear() {
|
|
220
|
+
this.seats.clear();
|
|
666
221
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
const prev = this.segmentStates.get(snapshot.key);
|
|
706
|
-
prev?.row?.remove();
|
|
707
|
-
if (prev !== void 0) this.segmentStates.delete(snapshot.key);
|
|
708
|
-
continue;
|
|
709
|
-
}
|
|
710
|
-
let state = this.segmentStates.get(snapshot.key);
|
|
711
|
-
if (state === void 0) {
|
|
712
|
-
state = { key: snapshot.key, row: null, expanded: false, snapshot };
|
|
713
|
-
this.segmentStates.set(snapshot.key, state);
|
|
714
|
-
} else {
|
|
715
|
-
state.snapshot = snapshot;
|
|
716
|
-
}
|
|
717
|
-
const started = this.runningSince.get(snapshot.key);
|
|
718
|
-
const parsed = snapshot.boundary === null ? void 0 : parseTurnDuration(snapshot.boundary);
|
|
719
|
-
if (parsed !== void 0) state.duration = parsed;
|
|
720
|
-
else if (state.duration === void 0 && started !== void 0) state.duration = Date.now() - started;
|
|
721
|
-
if (state.row === null || !state.row.isConnected) state.row = this.createProcessedRow(state);
|
|
722
|
-
this.syncProcessedRow(state);
|
|
723
|
-
}
|
|
724
|
-
for (const [key, state] of [...this.segmentStates]) {
|
|
725
|
-
if (completedKeys.has(key)) continue;
|
|
726
|
-
state.row?.remove();
|
|
727
|
-
this.segmentStates.delete(key);
|
|
728
|
-
}
|
|
729
|
-
const segmentByBlock = /* @__PURE__ */ new Map();
|
|
730
|
-
for (const segment of segments) {
|
|
731
|
-
for (const block of segment.blocks) segmentByBlock.set(block.key, segment);
|
|
732
|
-
}
|
|
733
|
-
const desiredHidden = /* @__PURE__ */ new Set();
|
|
734
|
-
const seenBlocks = /* @__PURE__ */ new Set();
|
|
735
|
-
for (const block of blocks) {
|
|
736
|
-
seenBlocks.add(block.key);
|
|
737
|
-
this.reconcileBlock(block, segmentByBlock.get(block.key) ?? null, desiredHidden);
|
|
738
|
-
}
|
|
739
|
-
if (rebuildBlocks || this.displayTouched || this.settleFired) {
|
|
740
|
-
const hosts = /* @__PURE__ */ new Set();
|
|
741
|
-
for (const block of this.currentBlocks.values()) {
|
|
742
|
-
if (block.host.isConnected) hosts.add(block.host);
|
|
743
|
-
}
|
|
744
|
-
for (const segment of segments) {
|
|
745
|
-
if (segment.finalStep?.isConnected) hosts.add(segment.finalStep);
|
|
746
|
-
for (const middle of segment.middleSteps) {
|
|
747
|
-
if (middle.isConnected) hosts.add(middle);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
this.discoverHollowWrappers(hosts, desiredHidden);
|
|
751
|
-
}
|
|
752
|
-
this.displayTouched = false;
|
|
753
|
-
this.settleFired = false;
|
|
754
|
-
for (const segment of segments) {
|
|
755
|
-
const state = this.segmentStates.get(segment.key);
|
|
756
|
-
const collapse = state !== void 0 && !state.expanded;
|
|
757
|
-
const animate = this.animatableKeys.has(segment.key);
|
|
758
|
-
for (const middle of segment.middleSteps) {
|
|
759
|
-
if (collapse) this.hideElement(middle, desiredHidden, animate);
|
|
760
|
-
else this.restoreElement(middle, animate);
|
|
761
|
-
}
|
|
762
|
-
if (segment.finalStep !== null) this.restoreElement(segment.finalStep);
|
|
763
|
-
}
|
|
764
|
-
for (const segment of segments) {
|
|
765
|
-
if (segment.hasWork && hasVisibleSegmentWork(segment)) continue;
|
|
766
|
-
const state = this.segmentStates.get(segment.key);
|
|
767
|
-
if (state !== void 0 && state.row !== null) {
|
|
768
|
-
state.row.remove();
|
|
769
|
-
state.row = null;
|
|
770
|
-
}
|
|
771
|
-
for (const block of segment.blocks) this.suppressBlock(block, desiredHidden);
|
|
772
|
-
for (const middle of segment.middleSteps) this.retainDisplayControl(middle, desiredHidden);
|
|
773
|
-
if (segment.finalStep !== null) this.retainDisplayControl(segment.finalStep, desiredHidden);
|
|
774
|
-
}
|
|
775
|
-
this.cleanupStaleChips(seenBlocks);
|
|
776
|
-
for (const [el, record] of [...this.pendingAnims]) {
|
|
777
|
-
if (record.target === "hidden" && record.kind === "fade" && this.pendingAnims.get(el) === record && this.isAnimOverdue(record.anim)) {
|
|
778
|
-
this.finishFadeCollapse(el, record);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
this.restoreUnusedDisplays(desiredHidden);
|
|
782
|
-
for (const state of this.segmentStates.values()) this.placeProcessedRow(flow, state);
|
|
783
|
-
this.stabilizeScrollAfterFold(scrollAnchor);
|
|
784
|
-
for (const key of [...this.runningSince.keys()]) {
|
|
785
|
-
if (!liveSegmentKeys.has(key)) this.runningSince.delete(key);
|
|
786
|
-
}
|
|
787
|
-
for (const key of [...this.completedOnce]) {
|
|
788
|
-
if (!liveSegmentKeys.has(key)) this.completedOnce.delete(key);
|
|
789
|
-
}
|
|
790
|
-
for (const [node] of [...this.turnStatusTexts]) {
|
|
791
|
-
if (!node.isConnected) this.turnStatusTexts.delete(node);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// src/work-groups.ts
|
|
225
|
+
function resolveTurns(tokens) {
|
|
226
|
+
const nextTurns = new Array(tokens.length).fill(null);
|
|
227
|
+
let next = null;
|
|
228
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
229
|
+
const token = tokens[i];
|
|
230
|
+
if (token.type === "boundary" && token.hard) next = null;
|
|
231
|
+
nextTurns[i] = next;
|
|
232
|
+
if (!(token.type === "boundary" && token.hard) && token.turn !== null) next = token.turn;
|
|
233
|
+
}
|
|
234
|
+
let previous = null;
|
|
235
|
+
return tokens.map((token, i) => {
|
|
236
|
+
if (token.type === "boundary" && token.hard) {
|
|
237
|
+
previous = null;
|
|
238
|
+
return token;
|
|
239
|
+
}
|
|
240
|
+
if (token.turn !== null) {
|
|
241
|
+
previous = token.turn;
|
|
242
|
+
return token;
|
|
243
|
+
}
|
|
244
|
+
if (token.type !== "work") return token;
|
|
245
|
+
const following = nextTurns[i];
|
|
246
|
+
const turn = previous === null ? following : following === null || previous === following ? previous : null;
|
|
247
|
+
return { ...token, turn };
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
function groupWork(tokens) {
|
|
251
|
+
const groups = [];
|
|
252
|
+
let left = "start";
|
|
253
|
+
let current = null;
|
|
254
|
+
let lastItem = "";
|
|
255
|
+
for (const token of resolveTurns(tokens)) {
|
|
256
|
+
if (token.type === "boundary") {
|
|
257
|
+
left = token.id;
|
|
258
|
+
current = null;
|
|
259
|
+
continue;
|
|
792
260
|
}
|
|
793
|
-
|
|
794
|
-
|
|
261
|
+
if (current !== null && current.turn !== token.turn) {
|
|
262
|
+
left = `turn-after:${lastItem}`;
|
|
263
|
+
current = null;
|
|
795
264
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
} else {
|
|
800
|
-
replaceTurnStatus(flow, this.turnStatusTexts, statusText);
|
|
265
|
+
if (current === null) {
|
|
266
|
+
current = { id: JSON.stringify([left, token.turn]), turn: token.turn, items: [] };
|
|
267
|
+
groups.push(current);
|
|
801
268
|
}
|
|
802
|
-
|
|
269
|
+
current.items.push(token.id);
|
|
270
|
+
lastItem = token.id;
|
|
803
271
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
272
|
+
return groups;
|
|
273
|
+
}
|
|
274
|
+
var GroupState = class {
|
|
275
|
+
constructor() {
|
|
276
|
+
this.groups = /* @__PURE__ */ new Map();
|
|
277
|
+
this.expanded = /* @__PURE__ */ new Map();
|
|
278
|
+
}
|
|
279
|
+
reconcile(groups) {
|
|
280
|
+
const previousItems = /* @__PURE__ */ new Map();
|
|
281
|
+
for (const group of this.groups.values()) {
|
|
282
|
+
for (const id of group.items) previousItems.set(id, this.isExpanded(group.id));
|
|
811
283
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
const oy = getComputedStyle(node).overflowY;
|
|
816
|
-
if ((oy === "auto" || oy === "scroll") && node.scrollHeight > node.clientHeight) break;
|
|
817
|
-
}
|
|
818
|
-
node = node.parentElement;
|
|
284
|
+
const next = /* @__PURE__ */ new Map();
|
|
285
|
+
for (const group of groups) {
|
|
286
|
+
next.set(group.id, this.expanded.get(group.id) ?? group.items.some((id) => previousItems.get(id) === true));
|
|
819
287
|
}
|
|
820
|
-
|
|
821
|
-
this.
|
|
822
|
-
this.scrollContainerFlow = flow;
|
|
823
|
-
return node;
|
|
288
|
+
this.groups = new Map(groups.map((group) => [group.id, group]));
|
|
289
|
+
this.expanded = next;
|
|
824
290
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
* (用户正在滚动浏览)时不干预——视口上方的高度变化由浏览器 scroll
|
|
828
|
-
* anchoring 补偿,视口下方的折叠不可见,插件再写 scrollTop 只会加入
|
|
829
|
-
* 上游吸底回写的拉锯。 */
|
|
830
|
-
captureScrollAnchor(flow) {
|
|
831
|
-
const scroller = this.findScrollContainer(flow);
|
|
832
|
-
if (scroller === null) return null;
|
|
833
|
-
const dist = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight;
|
|
834
|
-
return dist <= STICK_BOTTOM_THRESHOLD_PX ? { el: scroller } : null;
|
|
291
|
+
isExpanded(id) {
|
|
292
|
+
return this.expanded.get(id) ?? false;
|
|
835
293
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
* 中间的空档让触控板惯性滚动乘虚而入,反复折叠时表现为上下抽搐(用户
|
|
839
|
-
* 实测:长会话滚到底部附近无法稳定定位)。同一帧内钉回后,宿主的吸底
|
|
840
|
-
* 回写成为幂等 no-op,不再是第二个 scrollTop 写入方。 */
|
|
841
|
-
stabilizeScrollAfterFold(anchor) {
|
|
842
|
-
if (anchor === null) return;
|
|
843
|
-
const el = anchor.el;
|
|
844
|
-
if (!el.isConnected) return;
|
|
845
|
-
const dist = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
846
|
-
if (dist > STICK_BOTTOM_THRESHOLD_PX) el.scrollTop = el.scrollHeight - el.clientHeight;
|
|
294
|
+
setExpanded(id, value) {
|
|
295
|
+
if (this.groups.has(id)) this.expanded.set(id, value);
|
|
847
296
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
for (const [el, record] of this.pendingAnims) {
|
|
852
|
-
record.anim.cancel();
|
|
853
|
-
if (record.kind === "height") this.clearCollapseLock(el);
|
|
854
|
-
}
|
|
855
|
-
this.pendingAnims.clear();
|
|
856
|
-
this.animatableKeys.clear();
|
|
857
|
-
this.animatableSegmentBlocks.clear();
|
|
858
|
-
for (const record of this.chips.values()) record.chip.remove();
|
|
859
|
-
this.chips.clear();
|
|
860
|
-
for (const host of [...this.mergedThinks.keys()]) this.removeMergedThink(host);
|
|
861
|
-
for (const state of this.segmentStates.values()) state.row?.remove();
|
|
862
|
-
this.segmentStates.clear();
|
|
863
|
-
this.currentBlocks.clear();
|
|
864
|
-
this.blockExpanded.clear();
|
|
865
|
-
this.runningSince.clear();
|
|
866
|
-
this.completedOnce.clear();
|
|
867
|
-
this.bodyTextCache = /* @__PURE__ */ new WeakMap();
|
|
868
|
-
this.dirtyMessages.clear();
|
|
869
|
-
this.auditDisplays.clear();
|
|
870
|
-
this.nativeTurns.clear();
|
|
871
|
-
this.emptiedWrappers.clear();
|
|
872
|
-
this.structureDirty = true;
|
|
873
|
-
this.restoreAllDisplays();
|
|
874
|
-
restoreTurnStatus(this.turnStatusTexts);
|
|
875
|
-
this.flow = next;
|
|
297
|
+
clear() {
|
|
298
|
+
this.groups.clear();
|
|
299
|
+
this.expanded.clear();
|
|
876
300
|
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// src/summary.ts
|
|
304
|
+
function toolAction(tool) {
|
|
305
|
+
if (/^(write|edit|str_replace|apply_patch)/.test(tool)) return "\u7F16\u8F91\u4E86\u6587\u4EF6";
|
|
306
|
+
if (/^(read|glob|grep|list_directory|file_search)/.test(tool)) return "\u8BFB\u53D6\u4E86\u6587\u4EF6";
|
|
307
|
+
if (/^(web_search|search)/.test(tool)) return "\u641C\u7D22\u4E86\u8D44\u6599";
|
|
308
|
+
if (/(browser|browse|playwright|chrome|web_fetch)/.test(tool)) return "\u4F7F\u7528\u4E86\u6D4F\u89C8\u5668";
|
|
309
|
+
if (/^(bash|pwsh|shell|run_code|exec|cordis_run)/.test(tool)) return "\u8FD0\u884C\u4E86\u547D\u4EE4";
|
|
310
|
+
return tool === "" ? "\u8C03\u7528\u4E86\u5DE5\u5177" : `\u8C03\u7528\u4E86 ${tool}`;
|
|
311
|
+
}
|
|
312
|
+
function normalizeStatus(value) {
|
|
313
|
+
if (value === "running") return "running";
|
|
314
|
+
if (value === "error" || value === "failed") return "error";
|
|
315
|
+
if (value === "stopped" || value === "interrupted" || value === "cancelled") return "stopped";
|
|
316
|
+
if (value === "waiting" || value === "pending" || value === "approval") return "waiting";
|
|
317
|
+
return "done";
|
|
318
|
+
}
|
|
319
|
+
function summarize(infos) {
|
|
320
|
+
const last = infos[infos.length - 1];
|
|
321
|
+
const running = [...infos].reverse().find((info) => info.status === "running" || info.status === "waiting");
|
|
322
|
+
const current = running ?? last;
|
|
323
|
+
const actions = [...new Set(infos.map((info) => info.action))];
|
|
324
|
+
const status = running?.status ?? (infos.some((info) => info.status === "error") ? "error" : infos.some((info) => info.status === "stopped") ? "stopped" : "done");
|
|
325
|
+
let text = actions.join(" \xB7 ");
|
|
326
|
+
let detail = "";
|
|
327
|
+
if (running !== void 0) {
|
|
328
|
+
const active = running.status === "waiting" ? "\u7B49\u5F85\u64CD\u4F5C" : running.kind === "think" ? "\u6B63\u5728\u601D\u8003" : running.kind === "context" ? "\u6B63\u5728\u6CE8\u5165\u4E0A\u4E0B\u6587" : "\u6B63\u5728\u8FD0\u884C";
|
|
329
|
+
text = [...actions.filter((action) => action !== running.action), active].join(" \xB7 ");
|
|
330
|
+
detail = running.detail;
|
|
331
|
+
} else if (status === "error") text += " \xB7 \u51FA\u9519";
|
|
332
|
+
else if (status === "stopped") text += " \xB7 \u5DF2\u505C\u6B62";
|
|
333
|
+
return { text: text || "\u5DE5\u4F5C\u8FC7\u7A0B", detail, status, kind: current?.kind ?? "tool" };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/work-info.ts
|
|
337
|
+
function preview(row) {
|
|
338
|
+
const follow = row.querySelector("[data-follow-end]");
|
|
339
|
+
if (follow !== null) return (follow.textContent ?? "").trim().slice(-240);
|
|
340
|
+
const disclosure = row.querySelector("[data-disclosure-row]");
|
|
341
|
+
if (disclosure === null) return "";
|
|
342
|
+
for (const child of [...disclosure.children].slice(2)) {
|
|
343
|
+
if (child.getAttribute("aria-hidden") === "true") continue;
|
|
344
|
+
const value = (child.textContent ?? "").trim();
|
|
345
|
+
if (value !== "") return value.replace(/\s+/g, " ").slice(0, 240);
|
|
346
|
+
}
|
|
347
|
+
return "";
|
|
348
|
+
}
|
|
349
|
+
function readWorkInfo(item) {
|
|
350
|
+
const root = item.kind === "tool" ? item.row.querySelector("[data-tool]") ?? item.row : item.kind === "command" ? item.row.querySelector("[data-state]") ?? item.row : item.row;
|
|
351
|
+
const action = item.kind === "think" ? "\u5DF2\u601D\u8003" : item.kind === "context" ? "\u5DF2\u6CE8\u5165\u4E0A\u4E0B\u6587" : item.kind === "command" ? "\u8FD0\u884C\u4E86\u547D\u4EE4" : toolAction((root.getAttribute("data-tool") ?? "").slice(0, 80));
|
|
352
|
+
return { kind: item.kind, action, detail: preview(item.row), status: normalizeStatus(root.getAttribute("data-state")) };
|
|
353
|
+
}
|
|
354
|
+
function needsAttention(items, infos) {
|
|
355
|
+
return items.some(
|
|
356
|
+
(item) => item.cover.contains(document.activeElement) || item.row.hasAttribute("data-selected") || item.row.querySelector("[data-selected], input:not(:disabled), textarea:not(:disabled), select:not(:disabled)") !== null || infos.get(item.id)?.status === "waiting"
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/styles.css
|
|
361
|
+
var styles_default = '/* Keep native content searchable while removing folded work from flex/grid spacing. */\n[data-dshcf-folded][hidden="until-found"] {\n position: absolute !important;\n content-visibility: hidden !important;\n inline-size: 0 !important;\n block-size: 0 !important;\n min-inline-size: 0 !important;\n min-block-size: 0 !important;\n margin: 0 !important;\n padding: 0 !important;\n border-width: 0 !important;\n overflow: clip !important;\n}\n.dshcf-group {\n appearance: none;\n display: flex;\n align-items: center;\n align-self: flex-start;\n gap: 6px;\n box-sizing: border-box;\n max-width: 100%;\n min-width: 0;\n min-height: 24px;\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #919191);\n font: 400 var(--dsh-content-font-size, 14px)/24px system-ui, sans-serif;\n text-align: left;\n cursor: pointer;\n}\n.dshcf-group[hidden] { display: none; }\n.dshcf-group:hover { color: var(--dsw-alias-label-primary, #d5d5d5); }\n.dshcf-group:focus-visible { outline: 2px solid var(--dsw-alias-state-focus-ring, #4d6bfe); outline-offset: 3px; border-radius: 3px; }\n.dshcf-group-icon { display: flex; flex: none; width: 16px; height: 16px; align-items: center; justify-content: center; }\n.dshcf-group-icon svg { display: block; width: 14px; height: 14px; }\n.dshcf-group-label { flex: 0 0 auto; min-width: 0; max-width: calc(100% - 40px); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }\n.dshcf-group[data-status="running"] .dshcf-group-label,\n.dshcf-group[data-status="waiting"] .dshcf-group-label { max-width: 65%; }\n.dshcf-group-detail { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, #aaa); }\n.dshcf-group-detail:empty { display: none; }\n.dshcf-group-chevron { flex: none; width: 12px; height: 12px; opacity: .65; transform: rotate(-90deg); }\n.dshcf-group[aria-expanded="true"] .dshcf-group-chevron { transform: none; }\n.dshcf-group[data-status="running"] .dshcf-group-label { animation: dshcf-pulse 1.6s ease-in-out infinite; }\n.dshcf-group[data-status="error"] { color: var(--dsw-alias-state-error-primary, #e5484d); }\n.dshcf-group[data-status="stopped"], .dshcf-group[data-status="waiting"] { color: var(--dsw-alias-state-warning-primary, #c58a2c); }\n@keyframes dshcf-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }\n@media (prefers-reduced-motion: reduce) { .dshcf-group[data-status="running"] .dshcf-group-label { animation: none; } }\n';
|
|
362
|
+
|
|
363
|
+
// src/dom-view.ts
|
|
364
|
+
var GroupView = class {
|
|
365
|
+
constructor(flow, toggle) {
|
|
366
|
+
this.flow = flow;
|
|
367
|
+
this.toggle = toggle;
|
|
368
|
+
this.owner = `v2-${Math.random().toString(36).slice(2)}`;
|
|
369
|
+
this.rows = /* @__PURE__ */ new Map();
|
|
370
|
+
this.leases = /* @__PURE__ */ new Map();
|
|
371
|
+
this.writes = /* @__PURE__ */ new Map();
|
|
372
|
+
this.displays = /* @__PURE__ */ new Map();
|
|
373
|
+
this.style = document.createElement("style");
|
|
374
|
+
this.style.id = "dshcf-v2-style";
|
|
375
|
+
this.style.textContent = styles_default;
|
|
376
|
+
document.head.append(this.style);
|
|
377
|
+
}
|
|
378
|
+
writeHidden(element, value) {
|
|
379
|
+
if (element.getAttribute("hidden") === value) return;
|
|
380
|
+
this.writes.set(element, value);
|
|
381
|
+
if (value === null) element.removeAttribute("hidden");
|
|
382
|
+
else element.setAttribute("hidden", value);
|
|
383
|
+
}
|
|
384
|
+
ownMutation(record) {
|
|
385
|
+
return record.type === "attributes" && record.attributeName === "hidden" && record.target instanceof HTMLElement && this.writes.has(record.target) && this.writes.get(record.target) === record.target.getAttribute("hidden");
|
|
386
|
+
}
|
|
387
|
+
clearWrites() {
|
|
388
|
+
this.writes.clear();
|
|
389
|
+
}
|
|
390
|
+
detachedGroups(nodes) {
|
|
391
|
+
return nodes.flatMap((node) => {
|
|
392
|
+
if (!(node instanceof HTMLElement) || node.isConnected) return [];
|
|
393
|
+
const id = node.getAttribute("data-dshcf-group");
|
|
394
|
+
return id !== null && this.rows.get(id)?.button === node ? [id] : [];
|
|
891
395
|
});
|
|
892
|
-
return row;
|
|
893
|
-
}
|
|
894
|
-
syncProcessedRow(state) {
|
|
895
|
-
const row = state.row;
|
|
896
|
-
if (row === null) return;
|
|
897
|
-
const text = row.firstElementChild;
|
|
898
|
-
const label = state.duration === void 0 ? "\u5DF2\u5904\u7406" : `\u5DF2\u5904\u7406 ${formatDuration(state.duration)}`;
|
|
899
|
-
if (text !== null && text.textContent !== label) text.textContent = label;
|
|
900
|
-
const expanded = String(state.expanded);
|
|
901
|
-
if (row.getAttribute("aria-expanded") !== expanded) row.setAttribute("aria-expanded", expanded);
|
|
902
|
-
row.title = state.expanded ? "\u6536\u8D77\u5DE5\u4F5C\u8FC7\u7A0B" : "\u5C55\u5F00\u5DE5\u4F5C\u8FC7\u7A0B";
|
|
903
|
-
}
|
|
904
|
-
placeProcessedRow(flow, state) {
|
|
905
|
-
const row = state.row;
|
|
906
|
-
if (row === null) return;
|
|
907
|
-
if (!state.snapshot.hasWork || !hasVisibleSegmentWork(state.snapshot)) {
|
|
908
|
-
row.remove();
|
|
909
|
-
state.row = null;
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
let target = state.snapshot.firstWork ?? state.snapshot.finalStep ?? state.snapshot.boundary;
|
|
913
|
-
if (target === null || target.parentElement !== flow) {
|
|
914
|
-
row.remove();
|
|
915
|
-
state.row = null;
|
|
916
|
-
return;
|
|
917
|
-
}
|
|
918
|
-
while (target.previousElementSibling?.classList.contains("dshcf-flow-chip") === true) {
|
|
919
|
-
target = target.previousElementSibling;
|
|
920
|
-
}
|
|
921
|
-
if (row.parentElement !== flow || row.nextElementSibling !== target) target.before(row);
|
|
922
396
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
* 在途 WAAPI 动画不追踪取消——收尾回调只做幂等终态对齐,无残留。 */
|
|
926
|
-
hideOverlayForNativeCollapse(block) {
|
|
927
|
-
const existing = this.chips.get(block.key)?.chip;
|
|
928
|
-
if (existing !== void 0 && existing.style.display !== "none") {
|
|
929
|
-
existing.style.marginBottom = "";
|
|
930
|
-
existing.style.display = "none";
|
|
931
|
-
}
|
|
932
|
-
const row = this.mergedThinks.get(block.host);
|
|
933
|
-
if (row !== void 0 && row.style.display !== "none") {
|
|
934
|
-
row.style.display = "none";
|
|
935
|
-
const body = row.nextElementSibling;
|
|
936
|
-
if (body instanceof HTMLElement && body.classList.contains("dshcf-merged-body") && body.style.display !== "none") {
|
|
937
|
-
body.style.display = "none";
|
|
938
|
-
}
|
|
939
|
-
}
|
|
397
|
+
beginRead() {
|
|
398
|
+
this.displays.clear();
|
|
940
399
|
}
|
|
941
|
-
|
|
942
|
-
if (
|
|
943
|
-
this.
|
|
400
|
+
fold(element, group) {
|
|
401
|
+
if (nativeOwnsHidden(element)) {
|
|
402
|
+
this.release(element);
|
|
944
403
|
return;
|
|
945
404
|
}
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
if (keepHost) this.restoreElement(block.host);
|
|
954
|
-
else hostFade = this.hideElement(block.host, desiredHidden, animate);
|
|
955
|
-
for (const container of block.containers) this.hideElement(container, desiredHidden, animate);
|
|
956
|
-
for (const row of block.rows) this.hideElement(row, desiredHidden, animate);
|
|
957
|
-
const existing = this.chips.get(block.key)?.chip;
|
|
958
|
-
if (existing !== void 0 && existing.style.display !== "none") {
|
|
959
|
-
existing.style.marginBottom = "";
|
|
960
|
-
if (block.mount === "before" || keepHost) {
|
|
961
|
-
if (animate && this.canAnimate(existing)) this.startFadeCollapse(existing);
|
|
962
|
-
else existing.style.display = "none";
|
|
963
|
-
} else if (!hostFade) {
|
|
964
|
-
existing.style.display = "none";
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
this.syncEmptiedWrappers(block, desiredHidden);
|
|
405
|
+
const existing = this.leases.get(element);
|
|
406
|
+
if (existing === void 0) {
|
|
407
|
+
if (element.hasAttribute("hidden")) return;
|
|
408
|
+
this.leases.set(element, { hidden: null, marker: element.getAttribute(FOLDED), group });
|
|
409
|
+
element.setAttribute(FOLDED, this.owner);
|
|
410
|
+
} else if (element.getAttribute(FOLDED) !== this.owner) {
|
|
411
|
+
this.leases.delete(element);
|
|
968
412
|
return;
|
|
969
413
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
this.settleFired = true;
|
|
988
|
-
if (!this.hasPendingCollapse(block)) this.unpinChipMargin(chip);
|
|
989
|
-
};
|
|
990
|
-
for (const container of block.containers) {
|
|
991
|
-
if (expanded) this.restoreElement(container, animate);
|
|
992
|
-
else {
|
|
993
|
-
const started = this.hideElement(container, desiredHidden, animate, chipSettle);
|
|
994
|
-
if (started) this.pinChipMargin(chip);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
for (const row of block.rows) {
|
|
998
|
-
if (expanded) this.restoreElement(row, animate);
|
|
999
|
-
else {
|
|
1000
|
-
const started = this.hideElement(row, desiredHidden, animate, chipSettle);
|
|
1001
|
-
if (started) this.pinChipMargin(chip);
|
|
414
|
+
this.writeHidden(element, "until-found");
|
|
415
|
+
}
|
|
416
|
+
release(element) {
|
|
417
|
+
const lease = this.leases.get(element);
|
|
418
|
+
if (lease === void 0) return;
|
|
419
|
+
this.leases.delete(element);
|
|
420
|
+
if (element.getAttribute(FOLDED) !== this.owner) return;
|
|
421
|
+
if (!nativeOwnsHidden(element) && element.getAttribute("hidden") === "until-found") this.writeHidden(element, lease.hidden);
|
|
422
|
+
if (lease.marker === null) element.removeAttribute(FOLDED);
|
|
423
|
+
else element.setAttribute(FOLDED, lease.marker);
|
|
424
|
+
}
|
|
425
|
+
externallyHidden(item) {
|
|
426
|
+
for (let element = item.row; element !== null && element !== this.flow; element = element.parentElement) {
|
|
427
|
+
let display = this.displays.get(element);
|
|
428
|
+
if (display === void 0) {
|
|
429
|
+
display = getComputedStyle(element).display;
|
|
430
|
+
this.displays.set(element, display);
|
|
1002
431
|
}
|
|
432
|
+
if (display === "none") return true;
|
|
433
|
+
if (element.hasAttribute("hidden") && !this.leases.has(element)) return true;
|
|
1003
434
|
}
|
|
1004
|
-
|
|
1005
|
-
this.syncMergedThink(block.host, block.rows, desiredHidden, animate);
|
|
1006
|
-
} else {
|
|
1007
|
-
if (this.releaseMergedThink(block.host, animate, chipSettle)) this.pinChipMargin(chip);
|
|
1008
|
-
}
|
|
1009
|
-
this.syncEmptiedWrappers(block, desiredHidden);
|
|
1010
|
-
chip.classList.toggle("dshcf-has-body", block.mount === "inside" && this.hasBodyCached(block.host));
|
|
1011
|
-
updateChip(chip, block.rows, expanded);
|
|
435
|
+
return false;
|
|
1012
436
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
437
|
+
create(id) {
|
|
438
|
+
const button = document.createElement("button");
|
|
439
|
+
button.type = "button";
|
|
440
|
+
button.className = "dshcf-group";
|
|
441
|
+
button.setAttribute("data-dshcf-group", id);
|
|
442
|
+
const icon = document.createElement("span");
|
|
443
|
+
icon.className = "dshcf-group-icon";
|
|
444
|
+
icon.setAttribute("aria-hidden", "true");
|
|
445
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
446
|
+
svg.setAttribute("viewBox", "0 0 16 16");
|
|
447
|
+
svg.setAttribute("fill", "none");
|
|
448
|
+
const path = document.createElementNS(svg.namespaceURI, "path");
|
|
449
|
+
path.setAttribute("d", "M3 3.5h10M3 8h10M3 12.5h10M1 3.5h.1M1 8h.1M1 12.5h.1");
|
|
450
|
+
path.setAttribute("stroke", "currentColor");
|
|
451
|
+
path.setAttribute("stroke-width", "1.3");
|
|
452
|
+
path.setAttribute("stroke-linecap", "round");
|
|
453
|
+
svg.append(path);
|
|
454
|
+
icon.append(svg);
|
|
455
|
+
const label = document.createElement("span");
|
|
456
|
+
label.className = "dshcf-group-label";
|
|
457
|
+
const detail = document.createElement("span");
|
|
458
|
+
detail.className = "dshcf-group-detail";
|
|
459
|
+
const chevron = document.createElementNS(svg.namespaceURI, "svg");
|
|
460
|
+
chevron.setAttribute("class", "dshcf-group-chevron");
|
|
461
|
+
chevron.setAttribute("viewBox", "0 0 12 12");
|
|
462
|
+
chevron.setAttribute("aria-hidden", "true");
|
|
463
|
+
const arrow = document.createElementNS(svg.namespaceURI, "path");
|
|
464
|
+
arrow.setAttribute("d", "m3 4.5 3 3 3-3");
|
|
465
|
+
arrow.setAttribute("fill", "none");
|
|
466
|
+
arrow.setAttribute("stroke", "currentColor");
|
|
467
|
+
chevron.append(arrow);
|
|
468
|
+
button.append(icon, label, detail, chevron);
|
|
469
|
+
button.addEventListener("click", () => {
|
|
470
|
+
if (this.rows.get(id)?.button === button) this.toggle(id);
|
|
471
|
+
});
|
|
472
|
+
const row = { button, label, detail, targets: /* @__PURE__ */ new Set() };
|
|
473
|
+
this.rows.set(id, row);
|
|
474
|
+
return row;
|
|
1054
475
|
}
|
|
1055
|
-
|
|
1056
|
-
const
|
|
1057
|
-
if (
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1067
|
-
const scopes = /* @__PURE__ */ new Set();
|
|
1068
|
-
const collect = (el) => {
|
|
1069
|
-
const scope = scopeOf(el);
|
|
1070
|
-
if (scope === null) return;
|
|
1071
|
-
scopes.add(scope);
|
|
1072
|
-
let node = el?.parentElement ?? null;
|
|
1073
|
-
while (node instanceof HTMLElement && node !== scope) {
|
|
1074
|
-
if (!seen.has(node)) {
|
|
1075
|
-
seen.add(node);
|
|
1076
|
-
candidates.push(node);
|
|
1077
|
-
}
|
|
1078
|
-
node = node.parentElement;
|
|
1079
|
-
}
|
|
476
|
+
summarize(id, summary, expanded) {
|
|
477
|
+
const row = this.rows.get(id);
|
|
478
|
+
if (row === void 0) return;
|
|
479
|
+
if (row.label.textContent !== summary.text) row.label.textContent = summary.text;
|
|
480
|
+
const detail = expanded ? "" : summary.detail;
|
|
481
|
+
if (row.detail.textContent !== detail) row.detail.textContent = detail;
|
|
482
|
+
const attributes = {
|
|
483
|
+
"aria-expanded": String(expanded),
|
|
484
|
+
"aria-label": `${expanded ? "\u6536\u8D77" : "\u5C55\u5F00"}\u5DE5\u4F5C\u8FC7\u7A0B\uFF1A${summary.text}${detail ? ` \xB7 ${detail}` : ""}`,
|
|
485
|
+
"data-status": summary.status,
|
|
486
|
+
title: `${summary.text}${detail ? ` \xB7 ${detail}` : ""}`
|
|
1080
487
|
};
|
|
1081
|
-
for (const
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
if (
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
continue;
|
|
1120
|
-
}
|
|
1121
|
-
if (!(child instanceof HTMLElement)) continue;
|
|
1122
|
-
if (isDisplayed(child)) return false;
|
|
1123
|
-
}
|
|
1124
|
-
return true;
|
|
1125
|
-
}
|
|
1126
|
-
ensureChip(block) {
|
|
1127
|
-
let record = this.chips.get(block.key);
|
|
1128
|
-
const validParent = record !== void 0 && (block.mount === "inside" ? record.chip.parentElement === block.host : record.chip.parentElement === block.host.parentElement);
|
|
1129
|
-
if (record === void 0 || record.host !== block.host || !record.chip.isConnected || !validParent) {
|
|
1130
|
-
if (record !== void 0) {
|
|
1131
|
-
record.chip.remove();
|
|
1132
|
-
this.removeMergedThink(record.host);
|
|
1133
|
-
}
|
|
1134
|
-
const chip2 = document.createElement("button");
|
|
1135
|
-
chip2.type = "button";
|
|
1136
|
-
chip2.className = block.mount === "before" ? "dshcf-chip dshcf-flow-chip" : "dshcf-chip";
|
|
1137
|
-
chip2.setAttribute("aria-expanded", "false");
|
|
1138
|
-
chip2.setAttribute("data-dshcf-block-key", block.key);
|
|
1139
|
-
const leading = document.createElement("span");
|
|
1140
|
-
leading.className = "dshcf-leading";
|
|
1141
|
-
leading.appendChild(createCommandIcon());
|
|
1142
|
-
chip2.appendChild(leading);
|
|
1143
|
-
chip2.appendChild(createSpan("dshcf-chip-title"));
|
|
1144
|
-
chip2.appendChild(createSpan("dshcf-chip-sep"));
|
|
1145
|
-
chip2.appendChild(createSpan("dshcf-chip-summary"));
|
|
1146
|
-
chip2.appendChild(createChevronIcon("dshcf-chevron"));
|
|
1147
|
-
chip2.style.display = "none";
|
|
1148
|
-
chip2.addEventListener("click", () => {
|
|
1149
|
-
this.blockExpanded.set(block.key, !(this.blockExpanded.get(block.key) ?? false));
|
|
1150
|
-
this.animatableKeys.add(block.key);
|
|
1151
|
-
this.schedule();
|
|
1152
|
-
});
|
|
1153
|
-
record = { host: block.host, chip: chip2 };
|
|
1154
|
-
this.chips.set(block.key, record);
|
|
1155
|
-
}
|
|
1156
|
-
const chip = record.chip;
|
|
1157
|
-
if (block.mount === "inside") {
|
|
1158
|
-
if (chip.parentElement !== block.host || block.host.firstElementChild !== chip) block.host.prepend(chip);
|
|
1159
|
-
chip.classList.remove("dshcf-flow-chip");
|
|
1160
|
-
} else {
|
|
1161
|
-
if (chip.parentElement !== block.host.parentElement || chip.nextElementSibling !== block.host) block.host.before(chip);
|
|
1162
|
-
chip.classList.add("dshcf-flow-chip");
|
|
1163
|
-
}
|
|
1164
|
-
return chip;
|
|
1165
|
-
}
|
|
1166
|
-
suppressBlock(block, desiredHidden) {
|
|
1167
|
-
const existing = this.chips.get(block.key)?.chip;
|
|
1168
|
-
if (existing !== void 0 && existing.style.display !== "none") {
|
|
1169
|
-
existing.style.marginBottom = "";
|
|
1170
|
-
existing.style.display = "none";
|
|
1171
|
-
}
|
|
1172
|
-
this.removeMergedThink(block.host);
|
|
1173
|
-
this.retainDisplayControl(block.host, desiredHidden);
|
|
1174
|
-
for (const row of block.rows) this.retainDisplayControl(row, desiredHidden);
|
|
1175
|
-
for (const container of block.containers) this.retainDisplayControl(container, desiredHidden);
|
|
1176
|
-
}
|
|
1177
|
-
retainDisplayControl(el, desiredHidden) {
|
|
1178
|
-
if (this.controlledDisplay.has(el)) desiredHidden.add(el);
|
|
488
|
+
for (const [name2, value] of Object.entries(attributes)) if (row.button.getAttribute(name2) !== value) row.button.setAttribute(name2, value);
|
|
489
|
+
}
|
|
490
|
+
prepare(id, items, expanded, summary, turnCollapsed) {
|
|
491
|
+
const available = items.filter((item) => item.row.isConnected && !nativeHidden(item.row, this.flow) && !this.externallyHidden(item));
|
|
492
|
+
const hiddenByTurn = (item) => turnCollapsed && item.followNativeTurn;
|
|
493
|
+
const anchor = available.find((item) => !hiddenByTurn(item))?.cover;
|
|
494
|
+
const desired = new Set(available.filter((item) => !expanded || hiddenByTurn(item)).map((item) => item.cover));
|
|
495
|
+
return { id, expanded, summary, anchor, desired };
|
|
496
|
+
}
|
|
497
|
+
apply({ id, expanded, summary, anchor, desired }) {
|
|
498
|
+
const row = this.rows.get(id) ?? this.create(id);
|
|
499
|
+
row.button.hidden = anchor === void 0;
|
|
500
|
+
if (anchor !== void 0 && (row.button.parentNode !== anchor.parentNode || row.button.nextSibling !== anchor)) {
|
|
501
|
+
const focused = document.activeElement === row.button;
|
|
502
|
+
anchor.before(row.button);
|
|
503
|
+
if (focused) row.button.focus({ preventScroll: true });
|
|
504
|
+
}
|
|
505
|
+
for (const target of row.targets) if (!desired.has(target)) this.release(target);
|
|
506
|
+
for (const target of desired) this.fold(target, id);
|
|
507
|
+
row.targets = desired;
|
|
508
|
+
this.summarize(id, summary, expanded);
|
|
509
|
+
}
|
|
510
|
+
focus(id) {
|
|
511
|
+
this.rows.get(id)?.button.focus({ preventScroll: true });
|
|
512
|
+
}
|
|
513
|
+
remove(id) {
|
|
514
|
+
const row = this.rows.get(id);
|
|
515
|
+
if (row === void 0) return;
|
|
516
|
+
for (const target of row.targets) this.release(target);
|
|
517
|
+
row.button.remove();
|
|
518
|
+
this.rows.delete(id);
|
|
519
|
+
}
|
|
520
|
+
dispose() {
|
|
521
|
+
for (const id of [...this.rows.keys()]) this.remove(id);
|
|
522
|
+
for (const target of [...this.leases.keys()]) this.release(target);
|
|
523
|
+
this.style.remove();
|
|
524
|
+
this.writes.clear();
|
|
525
|
+
this.displays.clear();
|
|
1179
526
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
/** 连续思考合并行:插在第一个思考行前,标题用第一行思考内容;
|
|
1190
|
-
* 点击切换显示/隐藏全部原始思考行。 */
|
|
1191
|
-
syncMergedThink(host, rows, desiredHidden, animate = false) {
|
|
1192
|
-
let row = this.mergedThinks.get(host);
|
|
1193
|
-
if (row === void 0 || !row.isConnected) {
|
|
1194
|
-
row = document.createElement("button");
|
|
1195
|
-
row.type = "button";
|
|
1196
|
-
row.className = "dshcf-merged-think";
|
|
1197
|
-
row.setAttribute("aria-expanded", "false");
|
|
1198
|
-
const leading = document.createElement("span");
|
|
1199
|
-
leading.className = "dshcf-leading";
|
|
1200
|
-
leading.appendChild(createThinkIcon());
|
|
1201
|
-
const title = document.createElement("span");
|
|
1202
|
-
title.className = "dshcf-merged-title";
|
|
1203
|
-
const chevron = createChevronIcon("dshcf-chevron");
|
|
1204
|
-
row.append(leading, title, chevron);
|
|
1205
|
-
row.style.display = "none";
|
|
1206
|
-
const btn = row;
|
|
1207
|
-
btn.addEventListener("click", () => {
|
|
1208
|
-
if (this.mergedThinks.get(host) !== btn) return;
|
|
1209
|
-
const next = !this.mergedExpanded.has(host);
|
|
1210
|
-
if (next) {
|
|
1211
|
-
if (this.expandMergedBody(host, btn)) {
|
|
1212
|
-
this.mergedExpanded.add(host);
|
|
1213
|
-
btn.setAttribute("aria-expanded", "true");
|
|
1214
|
-
}
|
|
1215
|
-
} else {
|
|
1216
|
-
this.mergedExpanded.delete(host);
|
|
1217
|
-
btn.setAttribute("aria-expanded", "false");
|
|
1218
|
-
this.collapseMergedBody(host);
|
|
1219
|
-
}
|
|
1220
|
-
});
|
|
1221
|
-
rows[0].before(row);
|
|
1222
|
-
this.mergedThinks.set(host, row);
|
|
1223
|
-
row = btn;
|
|
1224
|
-
}
|
|
1225
|
-
const titleEl = row.querySelector(".dshcf-merged-title");
|
|
1226
|
-
if (titleEl !== null) {
|
|
1227
|
-
let title = this.mergedTitles.get(host);
|
|
1228
|
-
if (title === void 0) {
|
|
1229
|
-
const first = truncateSummary(stripMarkdown(thinkSummary(rows[0])), 36);
|
|
1230
|
-
if (first !== "" && first !== "\u601D\u8003") {
|
|
1231
|
-
title = `Think \xB7 ${first}`;
|
|
1232
|
-
this.mergedTitles.set(host, title);
|
|
1233
|
-
} else {
|
|
1234
|
-
title = "\u601D\u8003";
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
if (titleEl.textContent !== title) titleEl.textContent = title;
|
|
1238
|
-
}
|
|
1239
|
-
const expanded = this.mergedExpanded.has(host);
|
|
1240
|
-
if (row.getAttribute("aria-expanded") !== String(expanded)) row.setAttribute("aria-expanded", String(expanded));
|
|
1241
|
-
const rowWasHidden = row.style.display === "none";
|
|
1242
|
-
if (row.style.display !== "") row.style.display = "";
|
|
1243
|
-
if (rowWasHidden && animate) this.revealVisual(row);
|
|
1244
|
-
for (const r of rows) this.hideElement(r, desiredHidden);
|
|
1245
|
-
if (expanded) {
|
|
1246
|
-
const result = this.ensureMergedBody(host, row, false);
|
|
1247
|
-
if (result !== null && result.created && animate) this.revealMergedBody(result.body);
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
/** 展开合并行:直接读各思考行文本合成内容块(不依赖原生行展开:
|
|
1251
|
-
* 程序化 click 不触发 React 展开,且后台 tab 的 rAF 不执行)。
|
|
1252
|
-
* 返回是否成功——思考行已不可读(parts 为空)时返回 false,调用方
|
|
1253
|
-
* 据此保持收起态,避免展开状态与内容块脱节。 */
|
|
1254
|
-
expandMergedBody(host, btn) {
|
|
1255
|
-
const cached = this.mergedBodyTexts.get(host);
|
|
1256
|
-
if (cached === void 0) {
|
|
1257
|
-
const parts = this.currentThinkRows(host).map((r) => r.textContent.replace(/^Think\s*/, "").trim()).filter(Boolean);
|
|
1258
|
-
if (parts.length === 0) return false;
|
|
1259
|
-
this.mergedBodyTexts.set(host, parts.join("\n\n"));
|
|
1260
|
-
}
|
|
1261
|
-
const result = this.ensureMergedBody(host, btn, true);
|
|
1262
|
-
if (result === null) return false;
|
|
1263
|
-
if (result.created) {
|
|
1264
|
-
this.revealMergedBody(result.body);
|
|
1265
|
-
} else {
|
|
1266
|
-
this.cancelPendingSync(result.body);
|
|
1267
|
-
}
|
|
1268
|
-
return true;
|
|
1269
|
-
}
|
|
1270
|
-
/** 创建/更新合并内容块(缓存优先,不重新展开原生行)。
|
|
1271
|
-
* 返回内容块与其是否为本次新建(新建才走展开动画)。 */
|
|
1272
|
-
ensureMergedBody(host, btn, force) {
|
|
1273
|
-
const cached = this.mergedBodyTexts.get(host);
|
|
1274
|
-
if (cached === void 0) return null;
|
|
1275
|
-
let body = btn.nextElementSibling;
|
|
1276
|
-
let created = false;
|
|
1277
|
-
if (body === null || !body.classList.contains("dshcf-merged-body")) {
|
|
1278
|
-
const next = document.createElement("div");
|
|
1279
|
-
next.className = "dshcf-merged-body";
|
|
1280
|
-
btn.after(next);
|
|
1281
|
-
body = next;
|
|
1282
|
-
created = true;
|
|
1283
|
-
}
|
|
1284
|
-
if (force || body.textContent !== cached) body.textContent = cached;
|
|
1285
|
-
return { body, created };
|
|
1286
|
-
}
|
|
1287
|
-
/** 清理合并 think 行(v12):状态 map 立即清除;DOM 在手势动画路径下
|
|
1288
|
-
* 渐隐后移除(settle 回调),其余路径瞬删。渐隐中途被反向取消时元素
|
|
1289
|
-
* 保留,由后续 pass 的 syncMergedThink 重建/复用。
|
|
1290
|
-
* settle 透传给每个渐隐目标的移除回调之后(chip 间距钉住的结算探测点,
|
|
1291
|
-
* AI 评审 P0:merged 行渐隐不走 block.rows,必须纳入同一钉住体系)。 */
|
|
1292
|
-
releaseMergedThink(host, animate = false, settle) {
|
|
1293
|
-
const row = this.mergedThinks.get(host);
|
|
1294
|
-
this.mergedExpanded.delete(host);
|
|
1295
|
-
this.mergedBodyTexts.delete(host);
|
|
1296
|
-
if (row === void 0) return false;
|
|
1297
|
-
this.mergedThinks.delete(host);
|
|
1298
|
-
const body = row.nextElementSibling;
|
|
1299
|
-
const targets = body !== null && body.classList.contains("dshcf-merged-body") ? [row, body] : [row];
|
|
1300
|
-
if (animate && this.canAnimate(row)) {
|
|
1301
|
-
for (const t of targets) this.startFadeCollapse(t, () => {
|
|
1302
|
-
t.remove();
|
|
1303
|
-
settle?.();
|
|
1304
|
-
});
|
|
1305
|
-
return true;
|
|
1306
|
-
} else {
|
|
1307
|
-
for (const t of targets) t.remove();
|
|
1308
|
-
return false;
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
/** merged-body 展开高度动画(机制样板:插件全资 DOM)。
|
|
1312
|
-
* 关键帧含 marginBottom 0→16px——其 CSS 有常量 margin-bottom:16px,
|
|
1313
|
-
* 高度从 0 起步时这 16px 会先占位产生小跳变。fill:'forwards' 托住终态,
|
|
1314
|
-
* onfinish 清内联后 cancel 释放,无闪烁窗口。收起由 collapseMergedBody
|
|
1315
|
-
* 做镜像高度卷下(同款账本与身份守卫),开合对称。 */
|
|
1316
|
-
revealMergedBody(body) {
|
|
1317
|
-
if (!this.canAnimate(body)) return;
|
|
1318
|
-
this.cancelPendingSync(body);
|
|
1319
|
-
const targetHeight = body.getBoundingClientRect().height;
|
|
1320
|
-
if (!(targetHeight > 0)) return;
|
|
1321
|
-
body.style.height = "0px";
|
|
1322
|
-
body.style.overflow = "hidden";
|
|
1323
|
-
body.style.marginBottom = "0px";
|
|
1324
|
-
const anim = body.animate(
|
|
1325
|
-
[
|
|
1326
|
-
{ height: "0px", marginBottom: "0px" },
|
|
1327
|
-
{ height: `${targetHeight}px`, marginBottom: "16px" }
|
|
1328
|
-
],
|
|
1329
|
-
{ duration: ANIM_DURATION_MS, easing: ANIM_EASING, fill: "forwards" }
|
|
1330
|
-
);
|
|
1331
|
-
const record = { anim, target: "visible", kind: "height" };
|
|
1332
|
-
this.pendingAnims.set(body, record);
|
|
1333
|
-
anim.onfinish = () => {
|
|
1334
|
-
if (this.pendingAnims.get(body) !== record) return;
|
|
1335
|
-
this.pendingAnims.delete(body);
|
|
1336
|
-
body.style.height = "";
|
|
1337
|
-
body.style.overflow = "";
|
|
1338
|
-
body.style.marginBottom = "";
|
|
1339
|
-
anim.cancel();
|
|
1340
|
-
this.schedule();
|
|
1341
|
-
};
|
|
1342
|
-
anim.oncancel = () => {
|
|
1343
|
-
if (this.pendingAnims.get(body) !== record) return;
|
|
1344
|
-
this.pendingAnims.delete(body);
|
|
1345
|
-
};
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// src/scheduler.ts
|
|
530
|
+
var Scheduler = class {
|
|
531
|
+
constructor(run) {
|
|
532
|
+
this.run = run;
|
|
533
|
+
this.frame = 0;
|
|
534
|
+
this.timeout = 0;
|
|
535
|
+
this.disposed = false;
|
|
1346
536
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
const body = btn.nextElementSibling;
|
|
1355
|
-
if (body === null || !body.classList.contains("dshcf-merged-body")) return;
|
|
1356
|
-
const el = body;
|
|
1357
|
-
if (!this.canAnimate(el)) {
|
|
1358
|
-
el.remove();
|
|
1359
|
-
return;
|
|
1360
|
-
}
|
|
1361
|
-
this.cancelPendingSync(el);
|
|
1362
|
-
const current = el.getBoundingClientRect().height;
|
|
1363
|
-
if (!(current > 0)) {
|
|
1364
|
-
el.remove();
|
|
1365
|
-
return;
|
|
1366
|
-
}
|
|
1367
|
-
el.style.height = `${current}px`;
|
|
1368
|
-
el.style.overflow = "hidden";
|
|
1369
|
-
const anim = el.animate(
|
|
1370
|
-
[
|
|
1371
|
-
{ height: `${current}px`, marginBottom: "16px" },
|
|
1372
|
-
{ height: "0px", marginBottom: "0px" }
|
|
1373
|
-
],
|
|
1374
|
-
{ duration: ANIM_DURATION_MS, easing: ANIM_EASING, fill: "forwards" }
|
|
1375
|
-
);
|
|
1376
|
-
const record = { anim, target: "hidden", kind: "height" };
|
|
1377
|
-
this.pendingAnims.set(el, record);
|
|
1378
|
-
anim.onfinish = () => {
|
|
1379
|
-
if (this.pendingAnims.get(el) !== record) return;
|
|
1380
|
-
this.pendingAnims.delete(el);
|
|
1381
|
-
el.remove();
|
|
1382
|
-
anim.cancel();
|
|
1383
|
-
this.schedule();
|
|
1384
|
-
};
|
|
1385
|
-
anim.oncancel = () => {
|
|
1386
|
-
if (this.pendingAnims.get(el) !== record) return;
|
|
1387
|
-
this.pendingAnims.delete(el);
|
|
1388
|
-
this.clearCollapseLock(el);
|
|
537
|
+
schedule() {
|
|
538
|
+
if (this.disposed || this.frame !== 0 || this.timeout !== 0) return;
|
|
539
|
+
const flush = () => {
|
|
540
|
+
if (this.frame !== 0) cancelAnimationFrame(this.frame);
|
|
541
|
+
if (this.timeout !== 0) clearTimeout(this.timeout);
|
|
542
|
+
this.frame = this.timeout = 0;
|
|
543
|
+
if (!this.disposed) this.run();
|
|
1389
544
|
};
|
|
545
|
+
this.frame = requestAnimationFrame(flush);
|
|
546
|
+
this.timeout = window.setTimeout(flush, 60);
|
|
1390
547
|
}
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
}
|
|
1397
|
-
/** 移除合并思考行(二级收起 / 一级收起时),恢复行由 applyRows 控制。
|
|
1398
|
-
* 合并内容块(btn 的兄弟节点)一并移除,避免宿主展开后残留文本。 */
|
|
1399
|
-
removeMergedThink(host) {
|
|
1400
|
-
const row = this.mergedThinks.get(host);
|
|
1401
|
-
if (row !== void 0) {
|
|
1402
|
-
const body = row.nextElementSibling;
|
|
1403
|
-
if (body !== null && body.classList.contains("dshcf-merged-body")) body.remove();
|
|
1404
|
-
row.remove();
|
|
1405
|
-
this.mergedThinks.delete(host);
|
|
1406
|
-
}
|
|
1407
|
-
this.mergedExpanded.delete(host);
|
|
1408
|
-
this.mergedBodyTexts.delete(host);
|
|
1409
|
-
}
|
|
1410
|
-
/** 正文判定(带缓存):同一消息子树未变时直接复用上次结果。失效由
|
|
1411
|
-
* markDirty(mutation 定向)与 switchFlow(整体重置)驱动;缓存的是
|
|
1412
|
-
* 纯文本/媒体存在性判定,与 display 状态无关,插件自身的显隐切换
|
|
1413
|
-
* 不会产生脏数据。 */
|
|
1414
|
-
hasBodyCached(el) {
|
|
1415
|
-
const cached = this.bodyTextCache.get(el);
|
|
1416
|
-
if (cached !== void 0) return cached;
|
|
1417
|
-
const value = hasBodyContent(el);
|
|
1418
|
-
this.bodyTextCache.set(el, value);
|
|
1419
|
-
return value;
|
|
1420
|
-
}
|
|
1421
|
-
/** 本块是否有在途收起渐隐(rows/containers/merged 行/body 任一)。
|
|
1422
|
-
* 基于 pendingAnims 账本无状态判定:onfinish/oncancel 都会即时清账,
|
|
1423
|
-
* 取消路径天然解锁(计数器/最后注册者会卡死)。merged 行渐隐时已被
|
|
1424
|
-
* releaseMergedThink 摘出 mergedThinks,按 DOM 类名现查。 */
|
|
1425
|
-
hasPendingCollapse(block) {
|
|
1426
|
-
const check = (el) => el !== null && el !== void 0 && this.pendingAnims.get(el)?.target === "hidden";
|
|
1427
|
-
if (block.containers.some(check)) return true;
|
|
1428
|
-
if (block.rows.some(check)) return true;
|
|
1429
|
-
const mergedRow = block.host.querySelector(".dshcf-merged-think");
|
|
1430
|
-
if (check(mergedRow)) return true;
|
|
1431
|
-
const mergedBody = mergedRow?.nextElementSibling;
|
|
1432
|
-
if (mergedBody instanceof HTMLElement && check(mergedBody)) return true;
|
|
1433
|
-
return false;
|
|
1434
|
-
}
|
|
1435
|
-
/** 钉住 chip 与首行的 16px 间距(收起 fade 期间;内联优先于 aria=false 的 0)。
|
|
1436
|
-
* flow-chip(context 等 before-mounted)豁免:其间距由宿主 row-gap 16px
|
|
1437
|
-
* 提供、自身 CSS 恒 0,钉住 16px 会叠加成 32px(真机实测:收起上下文
|
|
1438
|
-
* 注入时二级与三级间距瞬间扩大)。
|
|
1439
|
-
*/
|
|
1440
|
-
pinChipMargin(chip) {
|
|
1441
|
-
if (chip.classList.contains("dshcf-flow-chip")) return;
|
|
1442
|
-
if (chip.style.marginBottom !== "16px") chip.style.marginBottom = "16px";
|
|
1443
|
-
}
|
|
1444
|
-
/** 解除钉住(aria=true 的 16px 或 aria=false 的 0 由 CSS 接管)。 */
|
|
1445
|
-
unpinChipMargin(chip) {
|
|
1446
|
-
if (chip.style.marginBottom !== "") chip.style.marginBottom = "";
|
|
548
|
+
dispose() {
|
|
549
|
+
this.disposed = true;
|
|
550
|
+
if (this.frame !== 0) cancelAnimationFrame(this.frame);
|
|
551
|
+
if (this.timeout !== 0) clearTimeout(this.timeout);
|
|
552
|
+
this.frame = this.timeout = 0;
|
|
1447
553
|
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
// src/status-text.ts
|
|
557
|
+
var COPY = /Deep diving[.…]*|深度求索中\s*[.…]*/;
|
|
558
|
+
var StatusTextController = class {
|
|
559
|
+
constructor(read) {
|
|
560
|
+
this.read = read;
|
|
561
|
+
this.flow = null;
|
|
562
|
+
this.observer = null;
|
|
563
|
+
this.texts = /* @__PURE__ */ new Map();
|
|
564
|
+
this.scheduler = new Scheduler(() => this.update());
|
|
1456
565
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
this.
|
|
1460
|
-
this.
|
|
1461
|
-
this.
|
|
1462
|
-
|
|
566
|
+
setFlow(flow) {
|
|
567
|
+
if (flow === this.flow) return;
|
|
568
|
+
this.observer?.disconnect();
|
|
569
|
+
this.restore();
|
|
570
|
+
this.flow = flow;
|
|
571
|
+
if (flow === null) return;
|
|
572
|
+
this.observer = new MutationObserver((records) => {
|
|
573
|
+
if (records.some((record) => {
|
|
574
|
+
const element = record.target.nodeType === 1 ? record.target : record.target.parentElement;
|
|
575
|
+
const status = element?.closest('[role="status"]');
|
|
576
|
+
if (status?.closest("[data-chat-flow-kind]") != null) return false;
|
|
577
|
+
if (record.type === "characterData" && record.target instanceof Text) {
|
|
578
|
+
return status != null && this.texts.get(record.target)?.written !== record.target.data;
|
|
579
|
+
}
|
|
580
|
+
return record.type === "childList" && (status != null || [...record.addedNodes].some((node) => node instanceof Element && (node.matches('[role="status"]') || node.querySelector('[role="status"]') !== null)));
|
|
581
|
+
})) this.refresh();
|
|
582
|
+
});
|
|
583
|
+
this.observer.observe(flow, { subtree: true, childList: true, characterData: true });
|
|
584
|
+
this.refresh();
|
|
1463
585
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
hideElement(el, desired, animate = false, settle) {
|
|
1467
|
-
desired.add(el);
|
|
1468
|
-
const pending = this.pendingAnims.get(el);
|
|
1469
|
-
if (pending !== void 0) {
|
|
1470
|
-
if (pending.target === "hidden") return false;
|
|
1471
|
-
this.cancelPendingSync(el);
|
|
1472
|
-
}
|
|
1473
|
-
if (!this.originalDisplay.has(el) && !isDisplayed(el)) return false;
|
|
1474
|
-
if (this.hasAnimatingAncestor(el)) return false;
|
|
1475
|
-
if (!this.originalDisplay.has(el) || this.displayForeign(el)) {
|
|
1476
|
-
this.originalDisplay.set(el, el.style.display);
|
|
1477
|
-
this.writtenDisplay.set(el, el.style.display);
|
|
1478
|
-
el.style.setProperty(DISPLAY_OWNED_PROP, "1");
|
|
1479
|
-
}
|
|
1480
|
-
this.controlledDisplay.add(el);
|
|
1481
|
-
if (el.style.display === "none") return false;
|
|
1482
|
-
if (animate && this.canAnimate(el)) {
|
|
1483
|
-
this.startFadeCollapse(el, settle);
|
|
1484
|
-
return true;
|
|
1485
|
-
}
|
|
1486
|
-
el.style.display = "none";
|
|
1487
|
-
this.writtenDisplay.set(el, "none");
|
|
1488
|
-
this.displayTouched = true;
|
|
1489
|
-
return false;
|
|
586
|
+
refresh() {
|
|
587
|
+
this.scheduler.schedule();
|
|
1490
588
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
this.
|
|
1496
|
-
}
|
|
1497
|
-
if (!this.originalDisplay.has(el)) return;
|
|
1498
|
-
if (this.displayForeign(el)) {
|
|
1499
|
-
this.releaseDisplayLedger(el);
|
|
589
|
+
update() {
|
|
590
|
+
if (this.flow === null) return;
|
|
591
|
+
const replacement = this.read();
|
|
592
|
+
if (replacement === "" || replacement === void 0) {
|
|
593
|
+
this.restore();
|
|
1500
594
|
return;
|
|
1501
595
|
}
|
|
1502
|
-
const
|
|
1503
|
-
|
|
1504
|
-
if (
|
|
1505
|
-
|
|
1506
|
-
|
|
596
|
+
for (const text of this.texts.keys()) if (!text.isConnected) this.texts.delete(text);
|
|
597
|
+
for (const status of this.flow.querySelectorAll('[role="status"]')) {
|
|
598
|
+
if (status.closest("[data-chat-flow-kind]") !== null) continue;
|
|
599
|
+
const walker = document.createTreeWalker(status, NodeFilter.SHOW_TEXT);
|
|
600
|
+
let node;
|
|
601
|
+
while ((node = walker.nextNode()) !== null) {
|
|
602
|
+
const text = node;
|
|
603
|
+
const previous = this.texts.get(text);
|
|
604
|
+
const original = previous?.written === text.data ? previous.original : text.data;
|
|
605
|
+
if (!COPY.test(original)) continue;
|
|
606
|
+
const written = original.replace(COPY, () => replacement);
|
|
607
|
+
this.texts.set(text, { original, written });
|
|
608
|
+
if (text.data !== written) text.data = written;
|
|
1507
609
|
}
|
|
1508
|
-
this.releaseDisplayLedger(el);
|
|
1509
|
-
return;
|
|
1510
610
|
}
|
|
1511
|
-
if (el.style.display !== original) {
|
|
1512
|
-
el.style.display = original;
|
|
1513
|
-
this.displayTouched = true;
|
|
1514
|
-
}
|
|
1515
|
-
this.writtenDisplay.set(el, original);
|
|
1516
|
-
this.startReveal(el);
|
|
1517
611
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
if (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches) return false;
|
|
1522
|
-
return true;
|
|
612
|
+
restore() {
|
|
613
|
+
for (const [text, record] of this.texts) if (text.data === record.written) text.data = record.original;
|
|
614
|
+
this.texts.clear();
|
|
1523
615
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
[
|
|
1530
|
-
{ opacity: "0", transform: "translateY(4px)" },
|
|
1531
|
-
{ opacity: "1", transform: "translateY(0)" }
|
|
1532
|
-
],
|
|
1533
|
-
{ duration: ANIM_DURATION_MS, easing: ANIM_EASING }
|
|
1534
|
-
);
|
|
1535
|
-
const record = { anim, target: "visible", kind: "fade" };
|
|
1536
|
-
this.pendingAnims.set(el, record);
|
|
1537
|
-
anim.onfinish = () => {
|
|
1538
|
-
if (this.pendingAnims.get(el) !== record) return;
|
|
1539
|
-
this.pendingAnims.delete(el);
|
|
1540
|
-
this.releaseDisplayLedger(el);
|
|
1541
|
-
this.schedule();
|
|
1542
|
-
};
|
|
1543
|
-
anim.oncancel = () => {
|
|
1544
|
-
if (this.pendingAnims.get(el) !== record) return;
|
|
1545
|
-
this.pendingAnims.delete(el);
|
|
1546
|
-
};
|
|
1547
|
-
}
|
|
1548
|
-
/** 同步取消在途动画并清账:收起动画需同时清锁高内联(height/overflow/
|
|
1549
|
-
* marginBottom),否则取消方写完终态后元素仍被锁高裁剪一帧以上。 */
|
|
1550
|
-
cancelPendingSync(el) {
|
|
1551
|
-
const pending = this.pendingAnims.get(el);
|
|
1552
|
-
if (pending === void 0) return;
|
|
1553
|
-
pending.anim.cancel();
|
|
1554
|
-
this.pendingAnims.delete(el);
|
|
1555
|
-
if (pending.kind === "height") this.clearCollapseLock(el);
|
|
1556
|
-
}
|
|
1557
|
-
clearCollapseLock(el) {
|
|
1558
|
-
el.style.height = "";
|
|
1559
|
-
el.style.overflow = "";
|
|
1560
|
-
el.style.marginBottom = "";
|
|
1561
|
-
el.style.boxSizing = "";
|
|
1562
|
-
}
|
|
1563
|
-
/** 祖先 seat 在途动画检测:沿 parentNode 走到 flow,任一祖先在 pendingAnims
|
|
1564
|
-
* 即视为在途。分层规则——同一视觉变化只动画一层。 */
|
|
1565
|
-
hasAnimatingAncestor(el) {
|
|
1566
|
-
const flow = this.flow;
|
|
1567
|
-
if (flow === null) return false;
|
|
1568
|
-
let node = el.parentElement;
|
|
1569
|
-
while (node !== null && node !== flow) {
|
|
1570
|
-
if (this.pendingAnims.has(node)) return true;
|
|
1571
|
-
node = node.parentElement;
|
|
1572
|
-
}
|
|
1573
|
-
return false;
|
|
616
|
+
dispose() {
|
|
617
|
+
this.observer?.disconnect();
|
|
618
|
+
this.scheduler.dispose();
|
|
619
|
+
this.restore();
|
|
620
|
+
this.flow = null;
|
|
1574
621
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
);
|
|
1586
|
-
|
|
1587
|
-
this.
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
// src/controller.ts
|
|
625
|
+
var STRUCTURAL_ATTRIBUTES = /* @__PURE__ */ new Set(["data-chat-flow-key", "data-chat-anchor-key", "data-chat-flow-kind", "data-chat-turn", "data-variant", "data-tool"]);
|
|
626
|
+
var FoldController = class {
|
|
627
|
+
constructor(statusText) {
|
|
628
|
+
this.flow = null;
|
|
629
|
+
this.observer = null;
|
|
630
|
+
this.view = null;
|
|
631
|
+
this.model = new WorkModel();
|
|
632
|
+
this.state = new GroupState();
|
|
633
|
+
this.scheduler = new Scheduler(() => this.flush());
|
|
634
|
+
this.items = /* @__PURE__ */ new Map();
|
|
635
|
+
this.groups = /* @__PURE__ */ new Map();
|
|
636
|
+
this.infos = /* @__PURE__ */ new Map();
|
|
637
|
+
this.itemGroups = /* @__PURE__ */ new Map();
|
|
638
|
+
this.rowItems = /* @__PURE__ */ new WeakMap();
|
|
639
|
+
this.seatGroups = /* @__PURE__ */ new Map();
|
|
640
|
+
this.turnGroups = /* @__PURE__ */ new Map();
|
|
641
|
+
this.controls = /* @__PURE__ */ new Map();
|
|
642
|
+
this.dirtySeats = /* @__PURE__ */ new Set();
|
|
643
|
+
this.dirtyItems = /* @__PURE__ */ new Set();
|
|
644
|
+
this.dirtyGroups = /* @__PURE__ */ new Set();
|
|
645
|
+
this.modelDirty = true;
|
|
646
|
+
this.discover = true;
|
|
647
|
+
this.force = true;
|
|
648
|
+
this.started = false;
|
|
649
|
+
this.disposed = false;
|
|
650
|
+
this.lastError = "";
|
|
651
|
+
this.stats = { passes: 0, structures: 0, groupUpdates: 0, infoUpdates: 0, lastDurationMs: 0 };
|
|
652
|
+
this.ready = () => this.start();
|
|
653
|
+
this.visible = () => {
|
|
654
|
+
if (!document.hidden) {
|
|
655
|
+
this.discover = true;
|
|
656
|
+
this.refresh();
|
|
657
|
+
}
|
|
1592
658
|
};
|
|
659
|
+
this.reveal = (event) => this.revealAt(event.target, event.type === "beforematch");
|
|
660
|
+
this.status = statusText === void 0 ? null : new StatusTextController(statusText);
|
|
1593
661
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
* 钉住永久残留。重挂载同节点不可能(React 只建新节点),重启动的新动画
|
|
1599
|
-
* 由第一条守卫覆盖,无误伤。
|
|
1600
|
-
*/
|
|
1601
|
-
finishFadeCollapse(el, record) {
|
|
1602
|
-
const cur = this.pendingAnims.get(el);
|
|
1603
|
-
if (cur !== void 0 && cur !== record) return;
|
|
1604
|
-
this.pendingAnims.delete(el);
|
|
1605
|
-
if (this.displayForeign(el)) {
|
|
1606
|
-
this.releaseDisplayLedger(el);
|
|
1607
|
-
this.schedule();
|
|
1608
|
-
record.anim.cancel();
|
|
662
|
+
start() {
|
|
663
|
+
if (this.started || this.disposed || typeof document === "undefined") return;
|
|
664
|
+
if (document.body === null) {
|
|
665
|
+
document.addEventListener("DOMContentLoaded", this.ready, { once: true });
|
|
1609
666
|
return;
|
|
1610
667
|
}
|
|
1611
|
-
|
|
1612
|
-
this.
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
this.
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
const a = anim;
|
|
1621
|
-
if (a.playState === "finished") return true;
|
|
1622
|
-
if (typeof a.currentTime === "number") {
|
|
1623
|
-
const end = a.effect?.getComputedTiming?.().endTime;
|
|
1624
|
-
if (typeof end === "number" && a.currentTime >= end) return true;
|
|
1625
|
-
}
|
|
1626
|
-
return false;
|
|
1627
|
-
}
|
|
1628
|
-
/** 轻量视觉 reveal(opacity + 4px 微位移):用于插件全资元素的即时显示
|
|
1629
|
-
* 路径——chip(一级展开时出现)与 merged-think 行(二级展开时出现)。
|
|
1630
|
-
* 这些元素的 display 完全由插件直写、无 React 协调竞争,因此不入
|
|
1631
|
-
* pendingAnims 账本、无仲裁;收起同为直写 display:none,无 fill 的在途
|
|
1632
|
-
* 动画残留在隐藏元素上自然失效。门控沿用 animate 布尔(手势路径才调)。 */
|
|
1633
|
-
revealVisual(el) {
|
|
1634
|
-
if (!this.canAnimate(el)) return;
|
|
1635
|
-
el.animate(
|
|
1636
|
-
[
|
|
1637
|
-
{ opacity: "0", transform: "translateY(4px)" },
|
|
1638
|
-
{ opacity: "1", transform: "translateY(0)" }
|
|
1639
|
-
],
|
|
1640
|
-
{ duration: ANIM_DURATION_MS, easing: ANIM_EASING }
|
|
1641
|
-
);
|
|
1642
|
-
}
|
|
1643
|
-
restoreUnusedDisplays(desired) {
|
|
1644
|
-
for (const el of [...this.controlledDisplay]) {
|
|
1645
|
-
if (desired.has(el)) continue;
|
|
1646
|
-
if (!el.isConnected && this.pendingAnims.get(el)?.target === "hidden") {
|
|
1647
|
-
this.releaseDisplayLedger(el);
|
|
1648
|
-
continue;
|
|
1649
|
-
}
|
|
1650
|
-
this.restoreElement(el);
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
/** 收集 audit 需要观察的 display 集合;只读取内联样式,不触发布局计算。 */
|
|
1654
|
-
collectAuditDisplays(flow) {
|
|
1655
|
-
const nodes = new Set(flowItems(flow));
|
|
1656
|
-
for (const el of this.controlledDisplay) {
|
|
1657
|
-
if (el.isConnected && nodeWithin(el, flow)) nodes.add(el);
|
|
1658
|
-
}
|
|
1659
|
-
for (const { chip } of this.chips.values()) {
|
|
1660
|
-
if (chip.isConnected && nodeWithin(chip, flow)) nodes.add(chip);
|
|
1661
|
-
}
|
|
1662
|
-
for (const row of this.mergedThinks.values()) {
|
|
1663
|
-
if (row.isConnected && nodeWithin(row, flow)) nodes.add(row);
|
|
1664
|
-
}
|
|
1665
|
-
const displays = /* @__PURE__ */ new Map();
|
|
1666
|
-
for (const el of nodes) displays.set(el, el.style.display);
|
|
1667
|
-
return displays;
|
|
1668
|
-
}
|
|
1669
|
-
/** 在完整 pass 完成后保存 display 基线,供下一次轻量 audit 比对。 */
|
|
1670
|
-
captureAuditDisplays(flow) {
|
|
1671
|
-
this.auditDisplays = this.collectAuditDisplays(flow);
|
|
1672
|
-
}
|
|
1673
|
-
restoreAllDisplays() {
|
|
1674
|
-
for (const el of [...this.controlledDisplay]) this.restoreElement(el);
|
|
1675
|
-
this.controlledDisplay.clear();
|
|
1676
|
-
this.originalDisplay = /* @__PURE__ */ new WeakMap();
|
|
1677
|
-
this.writtenDisplay = /* @__PURE__ */ new WeakMap();
|
|
1678
|
-
}
|
|
1679
|
-
};
|
|
1680
|
-
function createSpan(cls) {
|
|
1681
|
-
const span = document.createElement("span");
|
|
1682
|
-
span.className = cls;
|
|
1683
|
-
return span;
|
|
1684
|
-
}
|
|
1685
|
-
var COMMAND_ICON_PATHS = [
|
|
1686
|
-
{
|
|
1687
|
-
transform: "translate(0.6689 1.073)",
|
|
1688
|
-
d: "M11.4818 5.57813C11.4818 4.45301 11.4807 3.66237 11.4075 3.05908C11.3359 2.46953 11.2024 2.13852 10.9939 1.89441C10.9247 1.81341 10.8493 1.73801 10.7683 1.66882C10.5242 1.46033 10.1932 1.32686 9.60364 1.25525C9.00034 1.18198 8.20974 1.18091 7.0846 1.18091L5.57813 1.18091C4.45301 1.18091 3.66238 1.18198 3.05908 1.25525C2.46953 1.32686 2.13852 1.46033 1.89441 1.66882C1.81341 1.73801 1.73801 1.81341 1.66882 1.89441C1.46033 2.13852 1.32686 2.46953 1.25525 3.05908C1.18198 3.66238 1.18091 4.45301 1.18091 5.57813L1.18091 6.2771C1.18091 7.40218 1.18197 8.19288 1.25525 8.79614C1.32687 9.38553 1.46036 9.71674 1.66882 9.96082C1.73797 10.0417 1.81347 10.1173 1.89441 10.1864C2.13851 10.3948 2.13965 10.5275 3.05908 10.5991C3.66238 10.6724 4.45298 10.6735 5.57813 10.6735L7.0846 10.6735C8.20977 10.6735 9.00033 10.6724 9.60364 10.5991C10.1931 10.5275 10.5242 10.3948 10.7683 10.1864C10.8493 10.1173 10.9247 10.0417 10.9939 9.96082C11.2024 9.71674 11.3358 9.38553 11.4075 8.79614C11.4808 8.19288 11.4818 7.40218 11.4818 6.2771L11.4818 5.57813ZM12.6627 6.2771C12.6627 7.37222 12.6637 8.247 12.5798 8.93799C12.4942 9.64284 12.3133 10.2359 11.8928 10.7282C11.7834 10.8562 11.6637 10.9751 11.5356 11.0845C11.0434 11.5049 10.4511 11.6867 9.74634 11.7723C9.05525 11.8563 8.17999 11.8552 7.0846 11.8552L5.57813 11.8552C4.48273 11.8552 3.60747 11.8563 2.91638 11.7723C2.21157 11.6867 1.61933 11.5049 1.12708 11.0845C0.99901 10.9751 0.879281 10.8562 0.769898 10.7282C0.349454 10.2359 0.168506 9.64284 0.0828864 8.93799C-0.00101964 8.247 4.88512e-07 7.37222 6.47206e-07 6.2771L6.47206e-07 5.57813C6.47206e-07 4.48273 -0.00106163 3.60747 0.0828864 2.91638C0.168502 2.21168 0.349594 1.61928 0.769898 1.12708C0.879302 0.998981 0.998981 0.879302 1.12708 0.769898C1.61928 0.349594 2.21168 0.168502 2.91638 0.0828864C3.60747 -0.00106163 4.48273 6.47206e-07 5.57813 6.47206e-07L7.0846 6.47206e-07C8.17999 6.47206e-07 9.05525 -0.00106163 9.74634 0.0828864C10.451 0.168505 11.0434 0.349587 11.5356 0.769898C11.6637 0.879302 11.7834 0.998981 11.8928 1.12708C12.3131 1.61928 12.4942 2.21169 12.5798 2.91638C12.6638 3.60747 12.6627 4.48273 12.6627 5.57813L12.6627 6.2771Z"
|
|
1689
|
-
},
|
|
1690
|
-
{
|
|
1691
|
-
transform: "translate(0.6689 1.073)",
|
|
1692
|
-
d: "M6.02607 5.50955L6.44306 5.9274L3.84284 8.52762L3.425 8.11063L3.00715 7.69278L4.77253 5.9274L3.00715 4.16202L3.84284 3.32633L6.02607 5.50955Z"
|
|
1693
|
-
},
|
|
1694
|
-
{
|
|
1695
|
-
transform: "translate(0.6689 1.073)",
|
|
1696
|
-
d: "M9.23789 7.35397L9.23789 8.53488L6.96238 8.53488L6.96238 7.35397L9.23789 7.35397Z"
|
|
1697
|
-
}
|
|
1698
|
-
];
|
|
1699
|
-
var NATIVE_CHEVRON_DOWN_PATH = "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z";
|
|
1700
|
-
function createChevronIcon(className) {
|
|
1701
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1702
|
-
svg.setAttribute("width", "14");
|
|
1703
|
-
svg.setAttribute("height", "14");
|
|
1704
|
-
svg.setAttribute("class", className);
|
|
1705
|
-
svg.setAttribute("viewBox", "0 0 14 14");
|
|
1706
|
-
svg.setAttribute("fill", "none");
|
|
1707
|
-
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
1708
|
-
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1709
|
-
path.setAttribute("d", NATIVE_CHEVRON_DOWN_PATH);
|
|
1710
|
-
path.setAttribute("fill", "currentColor");
|
|
1711
|
-
svg.appendChild(path);
|
|
1712
|
-
return svg;
|
|
1713
|
-
}
|
|
1714
|
-
var THINK_ICON_PATHS = [
|
|
1715
|
-
{
|
|
1716
|
-
d: "M7.06431 5.93342C7.68763 5.93342 8.19307 6.43904 8.19322 7.06233C8.19322 7.68573 7.68772 8.19123 7.06431 8.19123C6.44099 8.19113 5.9354 7.68567 5.9354 7.06233C5.93555 6.43911 6.44108 5.93353 7.06431 5.93342Z"
|
|
1717
|
-
},
|
|
1718
|
-
{
|
|
1719
|
-
evenodd: true,
|
|
1720
|
-
d: "M8.6815 0.963693C10.1169 0.447019 11.6266 0.374829 12.5633 1.31135C13.5 2.24805 13.4277 3.75776 12.911 5.19319C12.7126 5.74431 12.4386 6.31796 12.0965 6.89729C12.4969 7.54638 12.8141 8.19018 13.036 8.80647C13.5527 10.2419 13.6251 11.7516 12.6883 12.6883C11.7516 13.625 10.242 13.5527 8.8065 13.036C8.19022 12.8141 7.54641 12.4969 6.89732 12.0965C6.31797 12.4386 5.74435 12.7125 5.19322 12.911C3.75777 13.4276 2.2481 13.5 1.31138 12.5633C0.374859 11.6266 0.447049 10.1168 0.963724 8.68147C1.17185 8.10338 1.46321 7.50063 1.82896 6.8924C1.52182 6.35711 1.27235 5.82825 1.08872 5.31819C0.572068 3.88278 0.499714 2.37306 1.43638 1.43635C2.37308 0.499655 3.8828 0.572044 5.31822 1.08869C5.82828 1.27232 6.35715 1.5218 6.89243 1.82893C7.50066 1.46318 8.10341 1.17181 8.6815 0.963693ZM11.3573 8.01154C10.9083 8.62253 10.3901 9.22873 9.80943 9.8094C9.22877 10.3901 8.62255 10.9083 8.01158 11.3572C8.4257 11.5841 8.8287 11.7688 9.21275 11.9071C10.5456 12.3868 11.4246 12.2547 11.8397 11.8397C12.2548 11.4246 12.3869 10.5456 11.9071 9.21272C11.7688 8.82866 11.5841 8.42568 11.3573 8.01154ZM2.56529 8.02912C2.37344 8.39322 2.21495 8.74796 2.09263 9.08772C1.61291 10.4204 1.74512 11.2995 2.16001 11.7147C2.57505 12.1297 3.45415 12.2618 4.78697 11.7821C5.11057 11.6656 5.44786 11.5164 5.7938 11.3367C5.249 10.9223 4.70922 10.4533 4.19029 9.9344C3.57578 9.31987 3.03169 8.67633 2.56529 8.02912ZM6.90708 3.2469C6.24065 3.70479 5.5646 4.26321 4.91392 4.91389C4.26325 5.56456 3.70482 6.24063 3.24693 6.90705C3.72674 7.63325 4.32777 8.37459 5.03892 9.08576C5.64943 9.69627 6.28183 10.2265 6.90806 10.6678C7.59368 10.2025 8.2908 9.63076 8.96079 8.96076C9.6308 8.29075 10.2025 7.59366 10.6678 6.90803C10.2265 6.2818 9.69631 5.6494 9.08579 5.03889C8.37462 4.32773 7.63328 3.72672 6.90708 3.2469ZM11.7147 2.15998C11.2996 1.74509 10.4204 1.61288 9.08775 2.0926C8.74835 2.21479 8.39382 2.37271 8.03013 2.56428C8.67728 3.03065 9.31995 3.5758 9.93443 4.19026C10.4534 4.7092 10.9223 5.24896 11.3368 5.79377C11.5164 5.44785 11.6656 5.11052 11.7821 4.78694C12.2618 3.45416 12.1297 2.57502 11.7147 2.15998ZM4.91197 2.2176C3.57922 1.73788 2.70004 1.86995 2.28501 2.28498C1.87001 2.70003 1.73791 3.5792 2.21763 4.91194C2.31709 5.18822 2.44112 5.47427 2.58677 5.7674C3.01931 5.1887 3.51474 4.6158 4.06529 4.06526C4.61584 3.5147 5.18872 3.01928 5.76743 2.58674C5.47431 2.4411 5.18824 2.31706 4.91197 2.2176Z"
|
|
668
|
+
const owner = document;
|
|
669
|
+
if (owner.__dshAutoCollapseV2 !== this) owner.__dshAutoCollapseV2?.stop();
|
|
670
|
+
owner.__dshAutoCollapseV2 = this;
|
|
671
|
+
document.removeEventListener("DOMContentLoaded", this.ready);
|
|
672
|
+
this.started = true;
|
|
673
|
+
this.observer = new MutationObserver((records) => this.mutations(records));
|
|
674
|
+
this.observer.observe(document.body, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: OBSERVED_ATTRIBUTES });
|
|
675
|
+
document.addEventListener("visibilitychange", this.visible);
|
|
676
|
+
this.scheduler.schedule();
|
|
1721
677
|
}
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
return null;
|
|
1730
|
-
}
|
|
1731
|
-
function createThinkIcon() {
|
|
1732
|
-
const native = findNativeThinkSvg();
|
|
1733
|
-
if (native !== null) return native.cloneNode(true);
|
|
1734
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1735
|
-
svg.setAttribute("viewBox", "0 0 14 14");
|
|
1736
|
-
svg.setAttribute("width", "14");
|
|
1737
|
-
svg.setAttribute("height", "14");
|
|
1738
|
-
svg.setAttribute("fill", "none");
|
|
1739
|
-
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
1740
|
-
for (const p of THINK_ICON_PATHS) {
|
|
1741
|
-
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1742
|
-
if (p.evenodd === true) {
|
|
1743
|
-
path.setAttribute("fill-rule", "evenodd");
|
|
1744
|
-
path.setAttribute("clip-rule", "evenodd");
|
|
1745
|
-
}
|
|
1746
|
-
path.setAttribute("d", p.d);
|
|
1747
|
-
path.setAttribute("fill", "currentColor");
|
|
1748
|
-
svg.appendChild(path);
|
|
1749
|
-
}
|
|
1750
|
-
return svg;
|
|
1751
|
-
}
|
|
1752
|
-
function findNativeCommandSvg() {
|
|
1753
|
-
const selector = '[data-chat-call-id] [data-disclosure-row], [data-chat-flow-kind="command"] [data-disclosure-row], [data-chat-flow-kind="manual-compaction"] [data-disclosure-row]';
|
|
1754
|
-
for (const drow of document.querySelectorAll(selector)) {
|
|
1755
|
-
for (const svg of drow.querySelectorAll("svg")) {
|
|
1756
|
-
if (svg.querySelectorAll("path").length === 3 && isIcon14(svg)) return svg;
|
|
1757
|
-
}
|
|
678
|
+
refresh() {
|
|
679
|
+
if (this.disposed) return;
|
|
680
|
+
this.force = this.modelDirty = true;
|
|
681
|
+
this.status?.refresh();
|
|
682
|
+
this.scheduler.schedule();
|
|
1758
683
|
}
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
function isIcon14(svg) {
|
|
1762
|
-
if (svg.getAttribute("width") === "14" && svg.getAttribute("height") === "14") return true;
|
|
1763
|
-
const vb = (svg.getAttribute("viewBox") ?? "").trim().split(/\s+/);
|
|
1764
|
-
return vb.length === 4 && Number(vb[2]) === 14 && Number(vb[3]) === 14;
|
|
1765
|
-
}
|
|
1766
|
-
var cachedNativeCommandSvg = null;
|
|
1767
|
-
function createCommandIcon() {
|
|
1768
|
-
if (cachedNativeCommandSvg !== null) return cachedNativeCommandSvg.cloneNode(true);
|
|
1769
|
-
const native = findNativeCommandSvg();
|
|
1770
|
-
if (native !== null) {
|
|
1771
|
-
cachedNativeCommandSvg = native;
|
|
1772
|
-
return native.cloneNode(true);
|
|
684
|
+
refreshStatus() {
|
|
685
|
+
if (!this.disposed) this.status?.refresh();
|
|
1773
686
|
}
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1778
|
-
svg.setAttribute("viewBox", "0 0 14 14");
|
|
1779
|
-
svg.setAttribute("width", "14");
|
|
1780
|
-
svg.setAttribute("height", "14");
|
|
1781
|
-
svg.setAttribute("fill", "none");
|
|
1782
|
-
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
1783
|
-
for (const p of COMMAND_ICON_PATHS) {
|
|
1784
|
-
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1785
|
-
path.setAttribute("transform", p.transform);
|
|
1786
|
-
path.setAttribute("d", p.d);
|
|
1787
|
-
path.setAttribute("fill", "currentColor");
|
|
1788
|
-
svg.appendChild(path);
|
|
687
|
+
/** Small diagnostics for regressions/performance, never rendered into the chat. */
|
|
688
|
+
diagnostics() {
|
|
689
|
+
return { ...this.stats, groups: this.groups.size, items: this.items.size };
|
|
1789
690
|
}
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
d: "M11.2426 4.80473V6.10551H4.75819V4.80473H11.2426Z"
|
|
1795
|
-
},
|
|
1796
|
-
{
|
|
1797
|
-
d: "M9.40858 7.84478V9.14557H4.75819V7.84478H9.40858Z"
|
|
1798
|
-
},
|
|
1799
|
-
{
|
|
1800
|
-
d: "M9.23438 0.546389C10.1941 0.546389 10.9683 0.544914 11.5859 0.611819C12.2161 0.680096 12.7634 0.825745 13.2393 1.17139C13.5172 1.3733 13.7619 1.61812 13.9639 1.896C14.3096 2.37183 14.4551 2.91922 14.5234 3.54932C14.5903 4.16686 14.5889 4.94133 14.5889 5.90088V10.0981C14.5889 11.0576 14.5903 11.8321 14.5234 12.4497C14.4552 13.0798 14.3094 13.6272 13.9639 14.103C13.7619 14.381 13.5172 14.6257 13.2393 14.8276C12.7633 15.1734 12.2163 15.3189 11.5859 15.3872C10.9683 15.4541 10.1942 15.4536 9.23438 15.4536H6.76563C5.80591 15.4536 5.03168 15.4541 4.41407 15.3872C3.78385 15.3189 3.23665 15.1734 2.76074 14.8276C2.48291 14.6257 2.23802 14.3809 2.03614 14.103C1.69066 13.6272 1.54483 13.0798 1.47657 12.4497C1.40973 11.8321 1.41114 11.0576 1.41114 10.0981V5.90088C1.41113 4.94132 1.40966 4.16686 1.47657 3.54932C1.54488 2.91921 1.69042 2.37184 2.03614 1.896C2.2381 1.61807 2.4828 1.37333 2.76074 1.17139C3.23665 0.825682 3.78386 0.680109 4.41407 0.611819C5.03168 0.544905 5.80591 0.546389 6.76563 0.546389H9.23438ZM6.76563 1.896C5.77586 1.896 5.0876 1.89738 4.55957 1.95459C4.0443 2.01043 3.76214 2.11349 3.55469 2.26416C3.39135 2.38284 3.24761 2.52662 3.12891 2.68994C2.97821 2.89736 2.8752 3.17967 2.81934 3.69483C2.76214 4.22279 2.76075 4.91131 2.76074 5.90088V10.0981C2.76074 11.0876 2.76221 11.7762 2.81934 12.3042C2.87516 12.8194 2.97829 13.1026 3.12891 13.3101C3.24754 13.4733 3.39147 13.6172 3.55469 13.7358C3.76213 13.8865 4.04438 13.9896 4.55957 14.0454C5.0876 14.1026 5.77586 14.103 6.76563 14.103H9.23438C10.2242 14.103 10.9124 14.1026 11.4404 14.0454C11.9556 13.9896 12.2379 13.8865 12.4453 13.7358C12.6086 13.6172 12.7525 13.4733 12.8711 13.3101C13.0217 13.1026 13.1248 12.8195 13.1807 12.3042C13.2378 11.7762 13.2393 11.0876 13.2393 10.0981V5.90088C13.2393 4.91131 13.2379 4.22279 13.1807 3.69483C13.1248 3.17969 13.0218 2.89736 12.8711 2.68994C12.7524 2.52667 12.6086 2.38281 12.4453 2.26416C12.2379 2.11355 11.9556 2.01041 11.4404 1.95459C10.9124 1.8974 10.2241 1.896 9.23438 1.896H6.76563Z"
|
|
1801
|
-
}
|
|
1802
|
-
];
|
|
1803
|
-
function findNativeContextSvg() {
|
|
1804
|
-
for (const ctx of document.querySelectorAll('[data-chat-flow-kind="context"]')) {
|
|
1805
|
-
const drow = ctx.querySelector("[data-disclosure-row]");
|
|
1806
|
-
if (drow === null) continue;
|
|
1807
|
-
for (const svg of drow.querySelectorAll("svg")) {
|
|
1808
|
-
if (svg.querySelectorAll("path").length >= 2) return svg;
|
|
691
|
+
itemAt(node) {
|
|
692
|
+
for (let element = elementOf(node); element !== null && element !== this.flow; element = element.parentElement) {
|
|
693
|
+
const id = this.rowItems.get(element);
|
|
694
|
+
if (id !== void 0) return id;
|
|
1809
695
|
}
|
|
696
|
+
return void 0;
|
|
1810
697
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
svg.setAttribute("viewBox", "0 0 16 16");
|
|
1818
|
-
svg.setAttribute("width", "14");
|
|
1819
|
-
svg.setAttribute("height", "14");
|
|
1820
|
-
svg.setAttribute("fill", "none");
|
|
1821
|
-
for (const p of CONTEXT_ICON_PATHS) {
|
|
1822
|
-
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1823
|
-
path.setAttribute("d", p.d);
|
|
1824
|
-
path.setAttribute("fill", "currentColor");
|
|
1825
|
-
svg.appendChild(path);
|
|
1826
|
-
}
|
|
1827
|
-
return svg;
|
|
1828
|
-
}
|
|
1829
|
-
var WRITE_ICON_PATH = "M9.94076 1.34942C10.7047 0.90231 11.6503 0.902415 12.4143 1.34942C12.7061 1.52015 12.9688 1.79118 13.3104 2.13284C13.6521 2.47448 13.9231 2.73721 14.0939 3.02894C14.5408 3.79294 14.5409 4.73856 14.0939 5.50251C13.9231 5.79415 13.652 6.05704 13.3104 6.39861L6.65932 13.0497C6.28068 13.4284 6.00695 13.7108 5.66543 13.9097C5.32391 14.1085 4.94315 14.2074 4.42705 14.3498L3.24394 14.6761C2.77527 14.8054 2.34538 14.9262 2.00131 14.9684C1.65196 15.0112 1.17964 15.0013 0.810764 14.6325C0.441921 14.2637 0.432107 13.7913 0.47486 13.442C0.517035 13.0979 0.6379 12.668 0.767181 12.1993L1.09352 11.0162C1.23588 10.5001 1.33481 10.1193 1.5336 9.77784C1.7325 9.43632 2.0149 9.1626 2.39355 8.78395L9.04466 2.13284C9.38625 1.79126 9.64911 1.52016 9.94076 1.34942ZM15.5427 14.8398H7.55223L8.96707 13.425H15.5427V14.8398ZM3.39382 9.78422C2.965 10.213 2.84244 10.3436 2.75709 10.49C2.67183 10.6366 2.61862 10.8079 2.45733 11.3925L2.13099 12.5756C2.00183 13.0439 1.92194 13.3419 1.88863 13.5536C2.10041 13.5204 2.39872 13.4416 2.86764 13.3123L4.05075 12.9859C4.63544 12.8246 4.80669 12.7715 4.95323 12.6862C5.09968 12.6008 5.23022 12.4783 5.65905 12.0494L10.721 6.98644L8.45577 4.72121L3.39382 9.78422ZM11.7 2.57079C11.3774 2.38198 10.9777 2.38198 10.6551 2.57079C10.5602 2.62647 10.4487 2.72931 10.0449 3.13311L9.45604 3.72094L11.7213 5.98617L12.3102 5.39833C12.7139 4.99457 12.8168 4.88307 12.8725 4.78818C13.0613 4.46561 13.0612 4.06585 12.8725 3.74326C12.8169 3.64827 12.7146 3.53752 12.3102 3.13311C11.9057 2.72863 11.795 2.6264 11.7 2.57079Z";
|
|
1830
|
-
function findNativeWriteSvg() {
|
|
1831
|
-
const selector = '[data-tool="write"] [data-disclosure-row], [data-tool="edit"] [data-disclosure-row]';
|
|
1832
|
-
for (const drow of document.querySelectorAll(selector)) {
|
|
1833
|
-
for (const svg of drow.querySelectorAll("svg")) {
|
|
1834
|
-
if (svg.querySelectorAll("path").length === 1 && isIcon16(svg)) return svg;
|
|
698
|
+
dirtySeatGroups(seat) {
|
|
699
|
+
if (seat === null) return;
|
|
700
|
+
for (const id of this.seatGroups.get(seat) ?? []) this.dirtyGroups.add(id);
|
|
701
|
+
if (seat.getAttribute("data-chat-flow-kind") === "turn-process") {
|
|
702
|
+
const turn = seat.getAttribute("data-chat-turn");
|
|
703
|
+
if (turn !== null) for (const id of this.turnGroups.get(turn) ?? []) this.dirtyGroups.add(id);
|
|
1835
704
|
}
|
|
1836
705
|
}
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1847
|
-
svg.setAttribute("viewBox", "0 0 16 16");
|
|
1848
|
-
svg.setAttribute("width", "14");
|
|
1849
|
-
svg.setAttribute("height", "14");
|
|
1850
|
-
svg.setAttribute("fill", "none");
|
|
1851
|
-
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1852
|
-
path.setAttribute("d", WRITE_ICON_PATH);
|
|
1853
|
-
path.setAttribute("fill", "currentColor");
|
|
1854
|
-
svg.appendChild(path);
|
|
1855
|
-
return svg;
|
|
1856
|
-
}
|
|
1857
|
-
function syncLeadingIcon(chip, kind) {
|
|
1858
|
-
const leading = chip.querySelector(".dshcf-leading");
|
|
1859
|
-
if (leading === null) return;
|
|
1860
|
-
const existing = leading.querySelector("svg");
|
|
1861
|
-
if (existing !== null && existing.getAttribute("data-dshcf-icon") === kind) return;
|
|
1862
|
-
for (const child of [...leading.childNodes]) child.remove();
|
|
1863
|
-
const svg = kind === "think" ? createThinkIcon() : kind === "context" ? createContextIcon() : kind === "write" ? createWriteIcon() : createCommandIcon();
|
|
1864
|
-
svg.setAttribute("data-dshcf-icon", kind);
|
|
1865
|
-
leading.appendChild(svg);
|
|
1866
|
-
}
|
|
1867
|
-
function findFlow() {
|
|
1868
|
-
const flows = document.querySelectorAll("[data-chat-flow]");
|
|
1869
|
-
for (const flow of flows) {
|
|
1870
|
-
if (flow.offsetParent !== null || flow.getBoundingClientRect().width > 0) return flow;
|
|
1871
|
-
}
|
|
1872
|
-
return flows[0] ?? null;
|
|
1873
|
-
}
|
|
1874
|
-
function nodeWithin(node, ancestor) {
|
|
1875
|
-
for (let current = node; current !== null; current = current.parentNode) {
|
|
1876
|
-
if (current === ancestor) return true;
|
|
1877
|
-
}
|
|
1878
|
-
return false;
|
|
1879
|
-
}
|
|
1880
|
-
function isPluginOwnedNode(node) {
|
|
1881
|
-
const element = node.nodeType === 1 ? node : node.parentElement;
|
|
1882
|
-
return element !== null && element !== void 0 && element.closest(".dshcf-chip, .dshcf-processed, .dshcf-merged-think, .dshcf-merged-body") !== null;
|
|
1883
|
-
}
|
|
1884
|
-
function flowChildOwner(node, flow) {
|
|
1885
|
-
let current = node;
|
|
1886
|
-
while (current !== null && current.parentNode !== flow) current = current.parentNode;
|
|
1887
|
-
return current instanceof HTMLElement ? current : null;
|
|
1888
|
-
}
|
|
1889
|
-
function flowItems(flow) {
|
|
1890
|
-
return [...flow.children].filter((el) => el instanceof HTMLElement && !el.classList.contains("dshcf-processed") && !el.classList.contains("dshcf-flow-chip"));
|
|
1891
|
-
}
|
|
1892
|
-
function isDisplayed(el) {
|
|
1893
|
-
if (typeof getComputedStyle === "function") return getComputedStyle(el).display !== "none";
|
|
1894
|
-
return el.style.display !== "none";
|
|
1895
|
-
}
|
|
1896
|
-
function buildNativeTurnMap(flow) {
|
|
1897
|
-
const states = /* @__PURE__ */ new Map();
|
|
1898
|
-
for (const el of flow.children) {
|
|
1899
|
-
if (!(el instanceof HTMLElement)) continue;
|
|
1900
|
-
if (el.getAttribute("data-chat-flow-kind") !== TURN_PROCESS_KIND) continue;
|
|
1901
|
-
const turn = el.getAttribute("data-chat-turn");
|
|
1902
|
-
if (turn === null || turn === "") continue;
|
|
1903
|
-
const button = el.querySelector("button[data-turn-process]");
|
|
1904
|
-
if (button === null) continue;
|
|
1905
|
-
const openAttr = button.getAttribute(NATIVE_OPEN_ATTR) !== null;
|
|
1906
|
-
const expandedAttr = button.getAttribute("aria-expanded");
|
|
1907
|
-
const open = expandedAttr === null ? openAttr : expandedAttr === "true";
|
|
1908
|
-
states.set(turn, { collapsed: !open });
|
|
1909
|
-
}
|
|
1910
|
-
return states;
|
|
1911
|
-
}
|
|
1912
|
-
function elementTurn(el) {
|
|
1913
|
-
const turn = el.getAttribute("data-chat-turn");
|
|
1914
|
-
return turn === null || turn === "" ? null : turn;
|
|
1915
|
-
}
|
|
1916
|
-
function segmentHasNativeTurn(segment, nativeTurns) {
|
|
1917
|
-
if (nativeTurns.size === 0) return false;
|
|
1918
|
-
for (const block of segment.blocks) {
|
|
1919
|
-
const turn = elementTurn(block.host);
|
|
1920
|
-
if (turn !== null && nativeTurns.has(turn)) return true;
|
|
1921
|
-
}
|
|
1922
|
-
return false;
|
|
1923
|
-
}
|
|
1924
|
-
function blockNativelyCollapsed(block, nativeTurns) {
|
|
1925
|
-
if (nativeTurns.size === 0) return false;
|
|
1926
|
-
const turn = elementTurn(block.host);
|
|
1927
|
-
return turn !== null && nativeTurns.get(turn)?.collapsed === true;
|
|
1928
|
-
}
|
|
1929
|
-
function stableElementKey(el, fallbackIndex) {
|
|
1930
|
-
const kind = el.getAttribute("data-chat-flow-kind") ?? "node";
|
|
1931
|
-
const key = el.getAttribute("data-chat-flow-key") ?? el.getAttribute("data-chat-anchor-key") ?? `${kind}:${fallbackIndex}`;
|
|
1932
|
-
return `${kind}:${key}`;
|
|
1933
|
-
}
|
|
1934
|
-
function hasLeadingTurnWork(items) {
|
|
1935
|
-
return items.some((el) => {
|
|
1936
|
-
const kind = el.getAttribute("data-chat-flow-kind");
|
|
1937
|
-
return kind === "assistant-step" || kind === "assistant" || kind === "tool-call" || kind === "command" || kind === "manual-compaction";
|
|
1938
|
-
});
|
|
1939
|
-
}
|
|
1940
|
-
function buildSegments(flow, blocks, hasBody) {
|
|
1941
|
-
const items = flowItems(flow);
|
|
1942
|
-
const itemIndex = new Map(items.map((el, index) => [el, index]));
|
|
1943
|
-
const snapshots = [];
|
|
1944
|
-
let contentStart = 0;
|
|
1945
|
-
let startMarker = null;
|
|
1946
|
-
const append = (end, boundary, closed) => {
|
|
1947
|
-
if (end < contentStart) return;
|
|
1948
|
-
const range = items.slice(contentStart, end);
|
|
1949
|
-
const inRange = new Set(range);
|
|
1950
|
-
const segmentBlocks = blocks.filter((block) => inRange.has(block.host));
|
|
1951
|
-
const bodySteps = range.filter((el) => {
|
|
1952
|
-
const kind = el.getAttribute("data-chat-flow-kind");
|
|
1953
|
-
return (kind === "assistant-step" || kind === "assistant") && hasBody(el);
|
|
1954
|
-
});
|
|
1955
|
-
const finalStep = bodySteps.length > 0 ? bodySteps[bodySteps.length - 1] : null;
|
|
1956
|
-
const middleSteps = new Set(bodySteps.slice(0, -1));
|
|
1957
|
-
const workHosts = /* @__PURE__ */ new Set([
|
|
1958
|
-
...segmentBlocks.map((block) => block.host),
|
|
1959
|
-
...middleSteps
|
|
1960
|
-
]);
|
|
1961
|
-
const firstWork = range.find((el) => workHosts.has(el)) ?? finalStep;
|
|
1962
|
-
const identity = startMarker ?? range.find((el) => hasLeadingTurnWork([el])) ?? boundary;
|
|
1963
|
-
const identityIndex = identity === null ? contentStart : itemIndex.get(identity) ?? contentStart;
|
|
1964
|
-
const prefix = startMarker === null ? "leading" : "segment";
|
|
1965
|
-
const key = `${prefix}:${identity === null ? `open:${contentStart}` : stableElementKey(identity, identityIndex)}`;
|
|
1966
|
-
snapshots.push({
|
|
1967
|
-
key,
|
|
1968
|
-
boundary,
|
|
1969
|
-
startMarker,
|
|
1970
|
-
blocks: segmentBlocks,
|
|
1971
|
-
middleSteps,
|
|
1972
|
-
finalStep,
|
|
1973
|
-
firstWork,
|
|
1974
|
-
closed,
|
|
1975
|
-
running: segmentBlocks.some((block) => block.rows.some((row) => rowState(row) === "running")),
|
|
1976
|
-
hasWork: segmentBlocks.length > 0 || middleSteps.size > 0
|
|
1977
|
-
});
|
|
1978
|
-
};
|
|
1979
|
-
for (let index = 0; index < items.length; index++) {
|
|
1980
|
-
const el = items[index];
|
|
1981
|
-
const kind = el.getAttribute("data-chat-flow-kind");
|
|
1982
|
-
if (kind === "user" || kind === "steering") {
|
|
1983
|
-
if (startMarker !== null) {
|
|
1984
|
-
append(index, el, true);
|
|
1985
|
-
contentStart = index + 1;
|
|
1986
|
-
} else {
|
|
1987
|
-
const leading = items.slice(contentStart, index);
|
|
1988
|
-
if (hasLeadingTurnWork(leading)) {
|
|
1989
|
-
append(index, el, true);
|
|
1990
|
-
contentStart = index + 1;
|
|
1991
|
-
}
|
|
706
|
+
mutations(records) {
|
|
707
|
+
if (this.disposed) return;
|
|
708
|
+
let relevant = false;
|
|
709
|
+
for (const record of records) {
|
|
710
|
+
if (pluginOwned(record.target) || this.view?.ownMutation(record)) continue;
|
|
711
|
+
const changed = [...record.addedNodes, ...record.removedNodes];
|
|
712
|
+
for (const id of this.view?.detachedGroups(changed) ?? []) {
|
|
713
|
+
this.dirtyGroups.add(id);
|
|
714
|
+
relevant = true;
|
|
1992
715
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
contentStart = index + 1;
|
|
1999
|
-
startMarker = null;
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
if (contentStart < items.length) append(items.length, null, false);
|
|
2003
|
-
return snapshots;
|
|
2004
|
-
}
|
|
2005
|
-
function hasVisibleSegmentWork(segment) {
|
|
2006
|
-
const workHosts = /* @__PURE__ */ new Set([
|
|
2007
|
-
...segment.blocks.map((block) => block.host),
|
|
2008
|
-
...segment.middleSteps
|
|
2009
|
-
]);
|
|
2010
|
-
if (segment.startMarker !== null) workHosts.add(segment.startMarker);
|
|
2011
|
-
if (segment.finalStep !== null) workHosts.add(segment.finalStep);
|
|
2012
|
-
return [...workHosts].some(isDisplayed);
|
|
2013
|
-
}
|
|
2014
|
-
function findBlocks(flow, hasBody) {
|
|
2015
|
-
const blocks = [];
|
|
2016
|
-
const children = flowItems(flow);
|
|
2017
|
-
let run = null;
|
|
2018
|
-
let carry = [];
|
|
2019
|
-
let carryHost = null;
|
|
2020
|
-
const makeBlock = (host, category) => {
|
|
2021
|
-
const block = {
|
|
2022
|
-
key: "",
|
|
2023
|
-
host,
|
|
2024
|
-
rows: [],
|
|
2025
|
-
containers: [],
|
|
2026
|
-
mount: "inside",
|
|
2027
|
-
category
|
|
2028
|
-
};
|
|
2029
|
-
blocks.push(block);
|
|
2030
|
-
return block;
|
|
2031
|
-
};
|
|
2032
|
-
const flushCarry = () => {
|
|
2033
|
-
if (carry.length === 0 || carryHost === null) return;
|
|
2034
|
-
let own = blocks.find((block) => block.host === carryHost && block.category === "work");
|
|
2035
|
-
if (own === void 0) own = makeBlock(carryHost, "work");
|
|
2036
|
-
own.rows.push(...carry);
|
|
2037
|
-
carry = [];
|
|
2038
|
-
carryHost = null;
|
|
2039
|
-
};
|
|
2040
|
-
for (const el of children) {
|
|
2041
|
-
const kind = el.getAttribute("data-chat-flow-kind");
|
|
2042
|
-
if (kind === TURN_PROCESS_KIND) continue;
|
|
2043
|
-
if (kind === "user" || kind === "steering" || kind === "turn-tail") {
|
|
2044
|
-
flushCarry();
|
|
2045
|
-
run = null;
|
|
2046
|
-
continue;
|
|
2047
|
-
}
|
|
2048
|
-
const thinkRows = thinkRowsIn(el);
|
|
2049
|
-
const workRows = [...callRowsIn(el), ...commandRowsIn(el)];
|
|
2050
|
-
const isToolPile = workRows.length > 0;
|
|
2051
|
-
const isContext = kind === "context";
|
|
2052
|
-
const msgHasBody = !isToolPile ? hasBody(el) : false;
|
|
2053
|
-
if (isContext) {
|
|
2054
|
-
flushCarry();
|
|
2055
|
-
if (run === null || run.category !== "context") run = makeBlock(el, "context");
|
|
2056
|
-
run.rows.push(el);
|
|
2057
|
-
run.mount = "before";
|
|
2058
|
-
continue;
|
|
2059
|
-
}
|
|
2060
|
-
if (isToolPile || thinkRows.length > 0 && !msgHasBody) {
|
|
2061
|
-
if (run === null || run.category !== "work") run = makeBlock(el, "work");
|
|
2062
|
-
if (carry.length > 0) {
|
|
2063
|
-
run.rows.push(...carry);
|
|
2064
|
-
carry = [];
|
|
2065
|
-
carryHost = null;
|
|
716
|
+
if (record.type === "childList" && changed.length > 0 && changed.every(pluginOwned)) continue;
|
|
717
|
+
const flow = this.flow;
|
|
718
|
+
if (flow === null || !flow.isConnected) {
|
|
719
|
+
this.discover = relevant = true;
|
|
720
|
+
continue;
|
|
2066
721
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
722
|
+
if (!flow.contains(record.target)) {
|
|
723
|
+
if (record.type === "attributes" && record.target instanceof Element && record.target.contains(flow)) {
|
|
724
|
+
this.discover = this.force = this.modelDirty = relevant = true;
|
|
725
|
+
}
|
|
726
|
+
if (record.type === "childList" && changed.some((node) => node instanceof Element && (node.matches("[data-chat-flow]") || node.querySelector("[data-chat-flow]") !== null))) this.discover = relevant = true;
|
|
727
|
+
continue;
|
|
2070
728
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
729
|
+
const seat = seatOf(record.target, flow);
|
|
730
|
+
if (seat === null && record.target !== flow) continue;
|
|
731
|
+
relevant = true;
|
|
732
|
+
const item = this.itemAt(record.target);
|
|
733
|
+
if (record.type === "attributes" && STRUCTURAL_ATTRIBUTES.has(record.attributeName ?? "")) {
|
|
734
|
+
this.modelDirty = true;
|
|
735
|
+
if (seat !== null) this.dirtySeats.add(seat);
|
|
736
|
+
} else if (item !== void 0) {
|
|
737
|
+
this.dirtyItems.add(item);
|
|
738
|
+
if (record.type === "attributes") this.dirtySeatGroups(seat);
|
|
739
|
+
} else if (record.type === "attributes") {
|
|
740
|
+
this.dirtySeatGroups(seat);
|
|
741
|
+
if (seat === null) {
|
|
742
|
+
this.force = this.modelDirty = true;
|
|
743
|
+
this.discover = true;
|
|
744
|
+
}
|
|
745
|
+
} else {
|
|
746
|
+
this.modelDirty = true;
|
|
747
|
+
if (seat !== null) this.dirtySeats.add(seat);
|
|
748
|
+
this.dirtySeatGroups(seat);
|
|
2080
749
|
}
|
|
2081
|
-
run = null;
|
|
2082
|
-
} else if (kind !== null && kind !== "assistant-step" && kind !== "assistant") {
|
|
2083
|
-
flushCarry();
|
|
2084
|
-
run = null;
|
|
2085
750
|
}
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
751
|
+
this.view?.clearWrites();
|
|
752
|
+
if (relevant) this.scheduler.schedule();
|
|
753
|
+
}
|
|
754
|
+
switchFlow(flow) {
|
|
755
|
+
if (flow === this.flow) return;
|
|
756
|
+
this.flow?.removeEventListener("beforematch", this.reveal, true);
|
|
757
|
+
this.flow?.removeEventListener("focusin", this.reveal, true);
|
|
758
|
+
this.view?.dispose();
|
|
759
|
+
this.view = null;
|
|
760
|
+
this.model.clear();
|
|
761
|
+
this.state.clear();
|
|
762
|
+
this.items.clear();
|
|
763
|
+
this.groups.clear();
|
|
764
|
+
this.infos.clear();
|
|
765
|
+
this.itemGroups.clear();
|
|
766
|
+
this.seatGroups.clear();
|
|
767
|
+
this.turnGroups.clear();
|
|
768
|
+
this.controls.clear();
|
|
769
|
+
this.rowItems = /* @__PURE__ */ new WeakMap();
|
|
770
|
+
this.dirtySeats.clear();
|
|
771
|
+
this.dirtyItems.clear();
|
|
772
|
+
this.dirtyGroups.clear();
|
|
773
|
+
this.flow = flow;
|
|
774
|
+
this.force = this.modelDirty = true;
|
|
775
|
+
this.status?.setFlow(flow);
|
|
776
|
+
if (flow !== null) {
|
|
777
|
+
this.view = new GroupView(flow, (id) => this.toggle(id));
|
|
778
|
+
flow.addEventListener("beforematch", this.reveal, true);
|
|
779
|
+
flow.addEventListener("focusin", this.reveal, true);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
rebuild(flow) {
|
|
783
|
+
const previousItems = this.items;
|
|
784
|
+
const previousGroups = this.groups;
|
|
785
|
+
const previousControls = this.controls;
|
|
786
|
+
const snapshot = this.model.read(flow, this.dirtySeats, this.force);
|
|
787
|
+
const groups = groupWork(snapshot.tokens);
|
|
788
|
+
this.state.reconcile(groups);
|
|
789
|
+
this.items = snapshot.items;
|
|
790
|
+
this.groups = new Map(groups.map((group) => [group.id, group]));
|
|
791
|
+
this.itemGroups.clear();
|
|
792
|
+
this.seatGroups.clear();
|
|
793
|
+
this.turnGroups.clear();
|
|
794
|
+
this.rowItems = /* @__PURE__ */ new WeakMap();
|
|
795
|
+
this.controls = nativeControls(flow);
|
|
796
|
+
for (const id of previousGroups.keys()) if (!this.groups.has(id)) this.view?.remove(id);
|
|
797
|
+
for (const id of this.infos.keys()) if (!this.items.has(id)) this.infos.delete(id);
|
|
798
|
+
for (const group of groups) {
|
|
799
|
+
const previous = previousGroups.get(group.id);
|
|
800
|
+
if (this.force || previous === void 0 || previous.items.length !== group.items.length || previous.items.some((id, i) => id !== group.items[i])) this.dirtyGroups.add(group.id);
|
|
801
|
+
if (group.turn !== null) {
|
|
802
|
+
if (previousControls.get(group.turn) !== this.controls.get(group.turn)) this.dirtyGroups.add(group.id);
|
|
803
|
+
const siblings = this.turnGroups.get(group.turn) ?? /* @__PURE__ */ new Set();
|
|
804
|
+
siblings.add(group.id);
|
|
805
|
+
this.turnGroups.set(group.turn, siblings);
|
|
806
|
+
}
|
|
807
|
+
for (const id of group.items) {
|
|
808
|
+
const item = this.items.get(id);
|
|
809
|
+
const previousItem = previousItems.get(id);
|
|
810
|
+
if (previousItem !== item) {
|
|
811
|
+
this.dirtyItems.add(id);
|
|
812
|
+
this.dirtyGroups.add(group.id);
|
|
813
|
+
}
|
|
814
|
+
this.itemGroups.set(id, group.id);
|
|
815
|
+
this.rowItems.set(item.row, id);
|
|
816
|
+
const owners = this.seatGroups.get(item.seat) ?? /* @__PURE__ */ new Set();
|
|
817
|
+
owners.add(group.id);
|
|
818
|
+
this.seatGroups.set(item.seat, owners);
|
|
819
|
+
}
|
|
2125
820
|
}
|
|
821
|
+
this.dirtySeats.clear();
|
|
822
|
+
this.force = this.modelDirty = false;
|
|
823
|
+
this.stats.structures++;
|
|
2126
824
|
}
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
let node;
|
|
2133
|
-
while ((node = walker.nextNode()) !== null) {
|
|
2134
|
-
if (node.data.trim() === "") continue;
|
|
2135
|
-
const parent = node.parentElement;
|
|
2136
|
-
if (parent !== null && parent.closest('[data-variant="think"], [data-chat-call-id], .dshcf-chip, .dshcf-merged-think, .dshcf-merged-body') !== null) continue;
|
|
2137
|
-
return true;
|
|
2138
|
-
}
|
|
2139
|
-
return false;
|
|
2140
|
-
}
|
|
2141
|
-
function hasBodyContent(el) {
|
|
2142
|
-
const kind = el.getAttribute("data-chat-flow-kind");
|
|
2143
|
-
if (kind === "command" || kind === "manual-compaction") return false;
|
|
2144
|
-
if (hasBodyText(el)) return true;
|
|
2145
|
-
const excluded = '[data-variant="think"], [data-chat-call-id], [data-variant="others"][data-state], .dshcf-chip, .dshcf-merged-think, .dshcf-merged-body';
|
|
2146
|
-
for (const media of el.querySelectorAll("img, video, audio, canvas")) {
|
|
2147
|
-
if (media.closest(excluded) === null) return true;
|
|
2148
|
-
}
|
|
2149
|
-
return false;
|
|
2150
|
-
}
|
|
2151
|
-
function thinkRowsIn(el) {
|
|
2152
|
-
const rows = [];
|
|
2153
|
-
for (const row of el.querySelectorAll('[data-variant="think"]:not([data-tool])')) {
|
|
2154
|
-
if (row.closest("[data-chat-call-id]") !== null) continue;
|
|
2155
|
-
if (row.closest("[data-subcalls]") !== null) continue;
|
|
2156
|
-
rows.push(row);
|
|
2157
|
-
}
|
|
2158
|
-
return rows;
|
|
2159
|
-
}
|
|
2160
|
-
function callRowsIn(el) {
|
|
2161
|
-
const rows = [];
|
|
2162
|
-
for (const row of el.querySelectorAll("[data-chat-call-id]")) {
|
|
2163
|
-
if (row.closest("[data-subcalls]") !== null) continue;
|
|
2164
|
-
if (row.closest("[data-chat-call-id]") !== row) continue;
|
|
2165
|
-
rows.push(row);
|
|
2166
|
-
}
|
|
2167
|
-
return rows;
|
|
2168
|
-
}
|
|
2169
|
-
function commandRowsIn(el) {
|
|
2170
|
-
const kind = el.getAttribute("data-chat-flow-kind");
|
|
2171
|
-
if (kind !== "command" && kind !== "manual-compaction") return [];
|
|
2172
|
-
const rows = [];
|
|
2173
|
-
for (const row of el.querySelectorAll('[data-variant="others"][data-state]')) {
|
|
2174
|
-
const parent = row.parentElement?.closest('[data-variant="others"][data-state]');
|
|
2175
|
-
if (parent !== null && parent !== void 0 && parent !== row) continue;
|
|
2176
|
-
rows.push(row);
|
|
2177
|
-
}
|
|
2178
|
-
return rows.length > 0 ? rows : [el];
|
|
2179
|
-
}
|
|
2180
|
-
function deriveRowInfo(row) {
|
|
2181
|
-
const isThink = row.matches('[data-variant="think"]') && !row.hasAttribute("data-tool");
|
|
2182
|
-
if (isThink) {
|
|
2183
|
-
return { kind: "think", label: "Think", summary: thinkSummary(row), state: row.getAttribute("data-state") ?? "ok" };
|
|
2184
|
-
}
|
|
2185
|
-
if (row.getAttribute("data-chat-flow-kind") === "context") {
|
|
2186
|
-
return { kind: "tool", label: "\u4E0A\u4E0B\u6587\u6CE8\u5165", summary: toolSummary(row), state: "ok" };
|
|
2187
|
-
}
|
|
2188
|
-
const commandSeat = row.closest('[data-chat-flow-kind="command"], [data-chat-flow-kind="manual-compaction"]');
|
|
2189
|
-
if (commandSeat !== null) {
|
|
2190
|
-
const commandKind = commandSeat.getAttribute("data-chat-flow-kind");
|
|
2191
|
-
return {
|
|
2192
|
-
kind: "tool",
|
|
2193
|
-
label: commandKind === "manual-compaction" ? "Compact" : "Command",
|
|
2194
|
-
summary: toolSummary(row),
|
|
2195
|
-
state: row.getAttribute("data-state") ?? "ok"
|
|
2196
|
-
};
|
|
2197
|
-
}
|
|
2198
|
-
const root = row.querySelector("[data-tool]") ?? row;
|
|
2199
|
-
const tool = root.getAttribute("data-tool") ?? "";
|
|
2200
|
-
const state = root.getAttribute("data-state") ?? "ok";
|
|
2201
|
-
const label = TOOL_LABELS[tool] ?? tool;
|
|
2202
|
-
return { kind: "tool", label: label !== "" ? label : "Tool", summary: toolSummary(row), state };
|
|
2203
|
-
}
|
|
2204
|
-
function thinkSummary(row) {
|
|
2205
|
-
const follow = row.querySelector("[data-follow-end]");
|
|
2206
|
-
if (follow !== null) {
|
|
2207
|
-
const text = (follow.textContent ?? "").trim();
|
|
2208
|
-
if (text !== "") return text;
|
|
2209
|
-
}
|
|
2210
|
-
return summaryFallback(row);
|
|
2211
|
-
}
|
|
2212
|
-
function toolSummary(row) {
|
|
2213
|
-
const drow = row.querySelector("[data-disclosure-row]");
|
|
2214
|
-
if (drow !== null) {
|
|
2215
|
-
const children = [...drow.children].filter((el) => el instanceof HTMLElement);
|
|
2216
|
-
for (const child of children.slice(2)) {
|
|
2217
|
-
const text = (child.textContent ?? "").trim();
|
|
2218
|
-
if (text !== "") return text;
|
|
825
|
+
prepare(group) {
|
|
826
|
+
const items = group.items.map((id) => this.items.get(id)).filter(Boolean);
|
|
827
|
+
if (!this.state.isExpanded(group.id) && needsAttention(items, this.infos)) {
|
|
828
|
+
this.state.setExpanded(group.id, true);
|
|
829
|
+
if (group.turn !== null) openNative(this.controls.get(group.turn));
|
|
2219
830
|
}
|
|
831
|
+
this.stats.groupUpdates++;
|
|
832
|
+
const turnCollapsed = group.turn !== null && nativeCollapsed(this.controls.get(group.turn));
|
|
833
|
+
return this.view?.prepare(group.id, items, this.state.isExpanded(group.id), summarize(group.items.map((id) => this.infos.get(id)).filter(Boolean)), turnCollapsed);
|
|
2220
834
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
const text = node.data.trim();
|
|
2230
|
-
if (text === "") continue;
|
|
2231
|
-
if (text.length > best.length) best = text;
|
|
2232
|
-
}
|
|
2233
|
-
return best;
|
|
2234
|
-
}
|
|
2235
|
-
function deriveBlockInfo(rows) {
|
|
2236
|
-
const infos = rows.map(deriveRowInfo);
|
|
2237
|
-
const runningTool = infos.find((i) => i.kind === "tool" && i.state === "running") ?? null;
|
|
2238
|
-
const runningThink = infos.find((i) => i.kind === "think" && i.state === "running") ?? null;
|
|
2239
|
-
const tools = [...new Set(infos.filter((i) => i.kind === "tool").map((i) => i.label))];
|
|
2240
|
-
return {
|
|
2241
|
-
runningTool,
|
|
2242
|
-
runningThink,
|
|
2243
|
-
tools,
|
|
2244
|
-
hasError: infos.some((i) => i.state === "error"),
|
|
2245
|
-
hasStopped: infos.some((i) => i.state === "stopped"),
|
|
2246
|
-
allContext: infos.length > 0 && infos.every((i) => i.label === "\u4E0A\u4E0B\u6587\u6CE8\u5165")
|
|
2247
|
-
};
|
|
2248
|
-
}
|
|
2249
|
-
function updateChip(chip, rows, expanded) {
|
|
2250
|
-
const info = deriveBlockInfo(rows);
|
|
2251
|
-
const title = chip.querySelector(".dshcf-chip-title");
|
|
2252
|
-
const summary = chip.querySelector(".dshcf-chip-summary");
|
|
2253
|
-
const sep = chip.querySelector(".dshcf-chip-sep");
|
|
2254
|
-
if (title === null || summary === null) return;
|
|
2255
|
-
const running = info.runningTool ?? info.runningThink;
|
|
2256
|
-
const collapsed = !expanded;
|
|
2257
|
-
let titleText;
|
|
2258
|
-
let summaryText;
|
|
2259
|
-
if (info.runningTool !== null) {
|
|
2260
|
-
titleText = "\u6B63\u5728\u8FD0\u884C";
|
|
2261
|
-
summaryText = collapsed ? info.runningTool.summary : "";
|
|
2262
|
-
} else if (info.runningThink !== null) {
|
|
2263
|
-
titleText = "\u6B63\u5728\u601D\u8003";
|
|
2264
|
-
summaryText = collapsed ? info.runningThink.summary : "";
|
|
2265
|
-
} else if (info.tools.length > 0) {
|
|
2266
|
-
titleText = info.allContext ? "\u4E0A\u4E0B\u6587\u6CE8\u5165" : info.tools.some((tool) => tool === "Edit" || tool === "Write") ? "\u7F16\u8F91\u4E86\u6587\u4EF6" : "\u8FD0\u884C\u4E86\u547D\u4EE4";
|
|
2267
|
-
summaryText = "";
|
|
2268
|
-
} else {
|
|
2269
|
-
titleText = "\u5DF2\u601D\u8003";
|
|
2270
|
-
summaryText = "";
|
|
2271
|
-
}
|
|
2272
|
-
let kind = running !== null ? running.kind : info.allContext ? "context" : info.tools.length > 0 ? "tool" : "think";
|
|
2273
|
-
if (running === null && info.tools.some((tool) => tool === "Edit" || tool === "Write")) kind = "write";
|
|
2274
|
-
if (title.textContent !== titleText) title.textContent = titleText;
|
|
2275
|
-
if (summary.textContent !== summaryText) summary.textContent = summaryText;
|
|
2276
|
-
if (sep !== null) {
|
|
2277
|
-
const sepDisplay = summaryText === "" ? "none" : "";
|
|
2278
|
-
if (sep.style.display !== sepDisplay) sep.style.display = sepDisplay;
|
|
2279
|
-
}
|
|
2280
|
-
if (running !== null) {
|
|
2281
|
-
summary.scrollLeft = summary.scrollWidth - summary.clientWidth;
|
|
2282
|
-
} else if (chip.classList.contains("running") && summary.scrollLeft !== 0) {
|
|
2283
|
-
summary.scrollLeft = 0;
|
|
2284
|
-
}
|
|
2285
|
-
const expandedAttr = String(expanded);
|
|
2286
|
-
if (chip.getAttribute("aria-expanded") !== expandedAttr) {
|
|
2287
|
-
chip.setAttribute("aria-expanded", expandedAttr);
|
|
2288
|
-
}
|
|
2289
|
-
if (chip.dataset.kind !== kind) {
|
|
2290
|
-
chip.dataset.kind = kind;
|
|
2291
|
-
syncLeadingIcon(chip, kind);
|
|
2292
|
-
}
|
|
2293
|
-
const tip = expanded ? "\u6536\u8D77\u8FD9\u4E9B\u5361\u7247" : "\u5C55\u5F00\u8FD9\u4E9B\u5361\u7247";
|
|
2294
|
-
if (chip.title !== tip) chip.title = tip;
|
|
2295
|
-
setClass(chip, "running", running !== null);
|
|
2296
|
-
setClass(chip, "error", !running && info.hasError);
|
|
2297
|
-
setClass(chip, "stopped", !running && info.hasStopped && !info.hasError);
|
|
2298
|
-
}
|
|
2299
|
-
function setClass(el, cls, on) {
|
|
2300
|
-
if (el.classList.contains(cls) !== on) el.classList.toggle(cls, on);
|
|
2301
|
-
}
|
|
2302
|
-
function truncateSummary(text, max) {
|
|
2303
|
-
const t = text.replace(/\s+/g, " ").trim();
|
|
2304
|
-
return t.length > max ? t.slice(0, max) + "\u2026" : t;
|
|
2305
|
-
}
|
|
2306
|
-
function stripMarkdown(text) {
|
|
2307
|
-
return text.replace(/\*\*/g, "").replace(/^#{1,3}\s+/, "").trim();
|
|
2308
|
-
}
|
|
2309
|
-
function isThinkRow(row) {
|
|
2310
|
-
return row.matches('[data-variant="think"]') && !row.hasAttribute("data-tool");
|
|
2311
|
-
}
|
|
2312
|
-
var turnDurationCache = /* @__PURE__ */ new WeakMap();
|
|
2313
|
-
function parseTurnDuration(boundary) {
|
|
2314
|
-
const text = boundary.textContent ?? "";
|
|
2315
|
-
const cached = turnDurationCache.get(boundary);
|
|
2316
|
-
if (cached !== void 0 && cached.text === text) return cached.duration;
|
|
2317
|
-
const duration = parseTurnDurationText(boundary, text);
|
|
2318
|
-
turnDurationCache.set(boundary, { text, duration });
|
|
2319
|
-
return duration;
|
|
2320
|
-
}
|
|
2321
|
-
function parseTurnDurationText(boundary, text) {
|
|
2322
|
-
const m = text.match(/用时\s*(\d+)分(\d+)秒|用时\s*(\d+)秒/);
|
|
2323
|
-
if (m !== null) {
|
|
2324
|
-
if (m[1] !== void 0 && m[2] !== void 0) return Number(m[1]) * 6e4 + Number(m[2]) * 1e3;
|
|
2325
|
-
if (m[3] !== void 0) return Number(m[3]) * 1e3;
|
|
2326
|
-
return void 0;
|
|
2327
|
-
}
|
|
2328
|
-
const end = parseTimeText(text);
|
|
2329
|
-
const start = findTurnStart(boundary);
|
|
2330
|
-
if (end !== void 0 && start !== void 0 && end > start) return end - start;
|
|
2331
|
-
return void 0;
|
|
2332
|
-
}
|
|
2333
|
-
function parseTimeText(text) {
|
|
2334
|
-
const m = text.match(/(?:(\d{4})年)?(\d{1,2})月(\d{1,2})日\s*(\d{1,2}):(\d{2})/);
|
|
2335
|
-
if (m === null) return void 0;
|
|
2336
|
-
const year = m[1] !== void 0 ? Number(m[1]) : (/* @__PURE__ */ new Date()).getFullYear();
|
|
2337
|
-
const t = new Date(year, Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5])).getTime();
|
|
2338
|
-
return Number.isNaN(t) ? void 0 : t;
|
|
2339
|
-
}
|
|
2340
|
-
function findTurnStart(boundary) {
|
|
2341
|
-
const flow = boundary.parentElement;
|
|
2342
|
-
if (flow === null) return void 0;
|
|
2343
|
-
let best = null;
|
|
2344
|
-
for (const s of flow.querySelectorAll('[class*="timeStart"]')) {
|
|
2345
|
-
const pos = s.compareDocumentPosition(boundary);
|
|
2346
|
-
if ((pos & Node.DOCUMENT_POSITION_FOLLOWING) !== 0 || (pos & Node.DOCUMENT_POSITION_CONTAINED_BY) !== 0 || s === boundary) best = s;
|
|
2347
|
-
else break;
|
|
2348
|
-
}
|
|
2349
|
-
if (best === null) return void 0;
|
|
2350
|
-
return parseTimeText(best.textContent ?? "");
|
|
2351
|
-
}
|
|
2352
|
-
function rowState(row) {
|
|
2353
|
-
if (row.matches('[data-variant="think"]') && !row.hasAttribute("data-tool")) {
|
|
2354
|
-
return row.getAttribute("data-state") ?? "ok";
|
|
2355
|
-
}
|
|
2356
|
-
const root = row.querySelector("[data-tool]") ?? row;
|
|
2357
|
-
return root.getAttribute("data-state") ?? "ok";
|
|
2358
|
-
}
|
|
2359
|
-
function createProcessedRowElement(duration) {
|
|
2360
|
-
const btn = document.createElement("button");
|
|
2361
|
-
btn.type = "button";
|
|
2362
|
-
btn.className = "dshcf-processed";
|
|
2363
|
-
btn.setAttribute("aria-expanded", "false");
|
|
2364
|
-
const text = document.createElement("span");
|
|
2365
|
-
text.textContent = duration !== void 0 ? `\u5DF2\u5904\u7406 ${formatDuration(duration)}` : "\u5DF2\u5904\u7406";
|
|
2366
|
-
const chevron = createChevronIcon("dshcf-processed-chevron");
|
|
2367
|
-
btn.append(text, chevron);
|
|
2368
|
-
btn.title = "\u5C55\u5F00\u5DE5\u4F5C\u8FC7\u7A0B";
|
|
2369
|
-
return btn;
|
|
2370
|
-
}
|
|
2371
|
-
function formatDuration(ms) {
|
|
2372
|
-
const s = Math.round(ms / 1e3);
|
|
2373
|
-
if (s < 60) return `${s}\u79D2`;
|
|
2374
|
-
const h = Math.floor(s / 3600);
|
|
2375
|
-
const m = Math.floor(s % 3600 / 60);
|
|
2376
|
-
const r = s % 60;
|
|
2377
|
-
if (h > 0) {
|
|
2378
|
-
return m > 0 ? `${h}\u5C0F\u65F6${m}\u5206` : `${h}\u5C0F\u65F6`;
|
|
2379
|
-
}
|
|
2380
|
-
if (r === 0) return `${m}\u5206`;
|
|
2381
|
-
return `${m}\u5206${String(r).padStart(2, "0")}\u79D2`;
|
|
2382
|
-
}
|
|
2383
|
-
function injectStyle() {
|
|
2384
|
-
if (document.getElementById(STYLE_ID) !== null) return;
|
|
2385
|
-
const style = document.createElement("style");
|
|
2386
|
-
style.id = STYLE_ID;
|
|
2387
|
-
style.textContent = CHIP_CSS;
|
|
2388
|
-
document.head.appendChild(style);
|
|
2389
|
-
}
|
|
2390
|
-
function replaceTurnStatus(flow, originals, statusText) {
|
|
2391
|
-
const statuses = flow.matches('[role="status"]') ? [flow, ...flow.querySelectorAll('[role="status"]')] : [...flow.querySelectorAll('[role="status"]')];
|
|
2392
|
-
for (const status of statuses) {
|
|
2393
|
-
for (const node of status.childNodes) {
|
|
2394
|
-
if (!(node instanceof Text)) continue;
|
|
2395
|
-
let record = originals.get(node);
|
|
2396
|
-
if (record !== void 0 && node.data === record.written) {
|
|
2397
|
-
const next = record.original.replace(TURN_STATUS_COPY_RE, statusText);
|
|
2398
|
-
if (node.data !== next) {
|
|
2399
|
-
node.data = next;
|
|
2400
|
-
record.written = next;
|
|
2401
|
-
}
|
|
2402
|
-
continue;
|
|
835
|
+
flush() {
|
|
836
|
+
if (this.disposed) return;
|
|
837
|
+
const start = performance.now();
|
|
838
|
+
this.stats.passes++;
|
|
839
|
+
try {
|
|
840
|
+
if (this.discover || !this.flow?.isConnected) {
|
|
841
|
+
this.discover = false;
|
|
842
|
+
this.switchFlow(findFlow());
|
|
2403
843
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
const
|
|
2411
|
-
if (
|
|
2412
|
-
|
|
2413
|
-
|
|
844
|
+
const flow = this.flow;
|
|
845
|
+
if (flow === null) return;
|
|
846
|
+
if (this.modelDirty) this.rebuild(flow);
|
|
847
|
+
this.view?.beginRead();
|
|
848
|
+
const summaries = /* @__PURE__ */ new Set();
|
|
849
|
+
for (const id of this.dirtyItems) {
|
|
850
|
+
const item = this.items.get(id);
|
|
851
|
+
if (item === void 0) continue;
|
|
852
|
+
this.infos.set(id, readWorkInfo(item));
|
|
853
|
+
this.stats.infoUpdates++;
|
|
854
|
+
const group = this.itemGroups.get(id);
|
|
855
|
+
if (group !== void 0) summaries.add(group);
|
|
856
|
+
}
|
|
857
|
+
const prepared = [];
|
|
858
|
+
for (const id of this.dirtyGroups) {
|
|
859
|
+
const group = this.groups.get(id);
|
|
860
|
+
if (group !== void 0) {
|
|
861
|
+
const update = this.prepare(group);
|
|
862
|
+
if (update !== void 0) prepared.push(update);
|
|
2414
863
|
}
|
|
864
|
+
summaries.delete(id);
|
|
2415
865
|
}
|
|
2416
|
-
|
|
866
|
+
for (const update of prepared) this.view?.apply(update);
|
|
867
|
+
for (const id of summaries) {
|
|
868
|
+
const group = this.groups.get(id);
|
|
869
|
+
if (group !== void 0) this.view?.summarize(id, summarize(group.items.map((item) => this.infos.get(item)).filter(Boolean)), this.state.isExpanded(id));
|
|
870
|
+
}
|
|
871
|
+
this.lastError = "";
|
|
872
|
+
} catch (error) {
|
|
873
|
+
this.observer?.disconnect();
|
|
874
|
+
this.switchFlow(null);
|
|
875
|
+
if (!this.disposed && document.body !== null) this.observer?.observe(document.body, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: OBSERVED_ATTRIBUTES });
|
|
876
|
+
const message = String(error);
|
|
877
|
+
if (this.lastError !== message) console.error("[dsh-auto-collapse] restored native view after an error", error);
|
|
878
|
+
this.lastError = message;
|
|
879
|
+
} finally {
|
|
880
|
+
this.dirtyGroups.clear();
|
|
881
|
+
this.dirtyItems.clear();
|
|
882
|
+
this.stats.lastDurationMs = performance.now() - start;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
toggle(id) {
|
|
886
|
+
const group = this.groups.get(id);
|
|
887
|
+
if (group === void 0 || this.disposed) return;
|
|
888
|
+
const expanded = !this.state.isExpanded(id);
|
|
889
|
+
this.state.setExpanded(id, expanded);
|
|
890
|
+
if (expanded && group.turn !== null) {
|
|
891
|
+
openNative(this.controls.get(group.turn));
|
|
892
|
+
this.view?.focus(id);
|
|
893
|
+
}
|
|
894
|
+
this.dirtyGroups.add(id);
|
|
895
|
+
this.scheduler.schedule();
|
|
896
|
+
}
|
|
897
|
+
revealAt(node, search) {
|
|
898
|
+
if (this.flow === null || pluginOwned(node)) return;
|
|
899
|
+
const item = this.itemAt(node);
|
|
900
|
+
const groups = item !== void 0 ? [this.itemGroups.get(item)] : [...this.seatGroups.get(seatOf(node, this.flow)) ?? []];
|
|
901
|
+
for (const id of groups) {
|
|
902
|
+
const group = this.groups.get(id);
|
|
903
|
+
if (group === void 0) continue;
|
|
904
|
+
if (!search && this.state.isExpanded(id)) continue;
|
|
905
|
+
this.state.setExpanded(id, true);
|
|
906
|
+
if (group.turn !== null) openNative(this.controls.get(group.turn));
|
|
907
|
+
this.dirtyGroups.add(id);
|
|
908
|
+
}
|
|
909
|
+
if (groups.length > 0) this.scheduler.schedule();
|
|
2417
910
|
}
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
911
|
+
stop() {
|
|
912
|
+
if (this.disposed) return;
|
|
913
|
+
this.disposed = true;
|
|
914
|
+
this.started = false;
|
|
915
|
+
this.observer?.disconnect();
|
|
916
|
+
this.scheduler.dispose();
|
|
917
|
+
if (typeof document !== "undefined") {
|
|
918
|
+
document.removeEventListener("DOMContentLoaded", this.ready);
|
|
919
|
+
document.removeEventListener("visibilitychange", this.visible);
|
|
920
|
+
}
|
|
921
|
+
this.switchFlow(null);
|
|
922
|
+
this.status?.dispose();
|
|
923
|
+
if (typeof document !== "undefined") {
|
|
924
|
+
const owner = document;
|
|
925
|
+
if (owner.__dshAutoCollapseV2 === this) delete owner.__dshAutoCollapseV2;
|
|
926
|
+
}
|
|
2422
927
|
}
|
|
2423
|
-
|
|
2424
|
-
}
|
|
2425
|
-
function removeStyle() {
|
|
2426
|
-
document.getElementById(STYLE_ID)?.remove();
|
|
2427
|
-
}
|
|
928
|
+
};
|
|
2428
929
|
|
|
2429
930
|
// src/settings.ts
|
|
2430
931
|
var AUTO_COLLAPSE_NS = "dsh-auto-collapse";
|
|
2431
|
-
var
|
|
932
|
+
var DEFAULT_STATUS_TEXT = "Deep sleeping...";
|
|
2432
933
|
function statusTextProvider(scope) {
|
|
2433
934
|
return () => {
|
|
2434
|
-
if (scope === void 0) return
|
|
935
|
+
if (scope === void 0) return DEFAULT_STATUS_TEXT;
|
|
2435
936
|
const snapshot = scope.getSnapshot();
|
|
2436
937
|
const value = snapshot.value;
|
|
2437
|
-
return value?.statusText ??
|
|
938
|
+
return value?.statusText ?? DEFAULT_STATUS_TEXT;
|
|
2438
939
|
};
|
|
2439
940
|
}
|
|
2440
941
|
var CARD_CSS = `
|
|
@@ -2529,12 +1030,12 @@ var __dshcfBundle = (() => {
|
|
|
2529
1030
|
.dshcf-settings-discard:focus-visible,
|
|
2530
1031
|
.dshcf-settings-save:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }
|
|
2531
1032
|
`;
|
|
2532
|
-
var
|
|
1033
|
+
var STYLE_ID = "dshcf-settings-style";
|
|
2533
1034
|
var styleOwners = 0;
|
|
2534
1035
|
function injectCardStyle() {
|
|
2535
|
-
if (typeof document === "undefined" || document.getElementById(
|
|
1036
|
+
if (typeof document === "undefined" || document.getElementById(STYLE_ID) !== null) return;
|
|
2536
1037
|
const style = document.createElement("style");
|
|
2537
|
-
style.id =
|
|
1038
|
+
style.id = STYLE_ID;
|
|
2538
1039
|
style.textContent = CARD_CSS;
|
|
2539
1040
|
document.head.appendChild(style);
|
|
2540
1041
|
}
|
|
@@ -2546,7 +1047,7 @@ var __dshcfBundle = (() => {
|
|
|
2546
1047
|
if (released) return;
|
|
2547
1048
|
released = true;
|
|
2548
1049
|
styleOwners = Math.max(0, styleOwners - 1);
|
|
2549
|
-
if (styleOwners === 0 && typeof document !== "undefined") document.getElementById(
|
|
1050
|
+
if (styleOwners === 0 && typeof document !== "undefined") document.getElementById(STYLE_ID)?.remove();
|
|
2550
1051
|
};
|
|
2551
1052
|
}
|
|
2552
1053
|
function ChevronIcon(open) {
|
|
@@ -2565,17 +1066,19 @@ var __dshcfBundle = (() => {
|
|
|
2565
1066
|
const React = __require("react");
|
|
2566
1067
|
const scope = props.scope;
|
|
2567
1068
|
const [open, setOpen] = React.useState(false);
|
|
2568
|
-
const
|
|
1069
|
+
const snapshot = React.useSyncExternalStore(
|
|
1070
|
+
React.useCallback((listener) => scope.subscribe(listener), [scope]),
|
|
1071
|
+
React.useCallback(() => scope.getSnapshot(), [scope])
|
|
1072
|
+
);
|
|
2569
1073
|
const [pending, setPending] = React.useState(null);
|
|
2570
1074
|
const [saving, setSaving] = React.useState(false);
|
|
2571
1075
|
const [failed, setFailed] = React.useState(false);
|
|
2572
|
-
React.useEffect(() => scope.subscribe(() => setSnapshot(scope.getSnapshot())), [scope]);
|
|
2573
1076
|
if (snapshot.status !== "ready") return null;
|
|
2574
1077
|
const value = snapshot.value;
|
|
2575
1078
|
const base = snapshot.base;
|
|
2576
1079
|
const user = snapshot.user;
|
|
2577
1080
|
const currentText = value?.statusText ?? "";
|
|
2578
|
-
const defaultText = base?.statusText ??
|
|
1081
|
+
const defaultText = base?.statusText ?? DEFAULT_STATUS_TEXT;
|
|
2579
1082
|
const text = pending ? pending.text : currentText;
|
|
2580
1083
|
const userHas = user !== void 0 && Object.prototype.hasOwnProperty.call(user, "statusText");
|
|
2581
1084
|
const overridden = pending ? !pending.reset : userHas;
|
|
@@ -2594,20 +1097,21 @@ var __dshcfBundle = (() => {
|
|
|
2594
1097
|
setFailed(false);
|
|
2595
1098
|
};
|
|
2596
1099
|
const save = async () => {
|
|
2597
|
-
if (pending === null) return;
|
|
1100
|
+
if (pending === null || saving || !writable) return;
|
|
1101
|
+
const submitted = pending;
|
|
2598
1102
|
setSaving(true);
|
|
2599
1103
|
setFailed(false);
|
|
2600
1104
|
try {
|
|
2601
1105
|
if (pending.reset) await scope.unset("statusText");
|
|
2602
1106
|
else await scope.set("statusText", pending.text.trim());
|
|
2603
|
-
setPending(null);
|
|
1107
|
+
setPending((current) => current === submitted ? null : current);
|
|
2604
1108
|
} catch {
|
|
2605
1109
|
setFailed(true);
|
|
2606
1110
|
} finally {
|
|
2607
1111
|
setSaving(false);
|
|
2608
1112
|
}
|
|
2609
1113
|
};
|
|
2610
|
-
const blocked = !dirty || saving;
|
|
1114
|
+
const blocked = !dirty || saving || !writable;
|
|
2611
1115
|
const cardClass = `dshcf-settings-card${open ? " dshcf-settings-cardOpen" : ""}`;
|
|
2612
1116
|
return React.createElement("li", { className: cardClass }, [
|
|
2613
1117
|
React.createElement(
|
|
@@ -2635,7 +1139,7 @@ var __dshcfBundle = (() => {
|
|
|
2635
1139
|
React.createElement("label", { className: "dshcf-settings-fieldLabel", htmlFor: "dshcf-status-text" }, "\u81EA\u5B9A\u4E49\u72B6\u6001\u63D0\u793A\u8BCD"),
|
|
2636
1140
|
overridden ? React.createElement("span", { className: "dshcf-settings-badges" }, [
|
|
2637
1141
|
React.createElement("span", { className: "dshcf-settings-badge" }, "\u5DF2\u8986\u76D6"),
|
|
2638
|
-
React.createElement("button", { type: "button", className: "dshcf-settings-reset", disabled: !writable, onClick: resetField }, "\u6062\u590D\u9ED8\u8BA4")
|
|
1142
|
+
React.createElement("button", { type: "button", className: "dshcf-settings-reset", disabled: !writable || saving, onClick: resetField }, "\u6062\u590D\u9ED8\u8BA4")
|
|
2639
1143
|
]) : null
|
|
2640
1144
|
]),
|
|
2641
1145
|
React.createElement("input", {
|
|
@@ -2644,7 +1148,7 @@ var __dshcfBundle = (() => {
|
|
|
2644
1148
|
type: "text",
|
|
2645
1149
|
value: text,
|
|
2646
1150
|
placeholder: "\u6DF1\u5EA6\u6C42\u7D22\u4E2D...",
|
|
2647
|
-
disabled: !writable,
|
|
1151
|
+
disabled: !writable || saving,
|
|
2648
1152
|
onChange: (event) => edit(event.target.value)
|
|
2649
1153
|
}),
|
|
2650
1154
|
React.createElement("p", { className: "dshcf-settings-hint" }, "\u4E3A\u7A7A\u65F6\u6062\u590D\u5B98\u65B9\u9ED8\u8BA4\u6587\u6848\uFF08\u6DF1\u5EA6\u6C42\u7D22\u4E2D...\uFF09")
|
|
@@ -2685,7 +1189,7 @@ var __dshcfBundle = (() => {
|
|
|
2685
1189
|
var name = "dsh-auto-collapse";
|
|
2686
1190
|
var inject = [];
|
|
2687
1191
|
function apply(ctx) {
|
|
2688
|
-
const fallbackStatusText = () =>
|
|
1192
|
+
const fallbackStatusText = () => DEFAULT_STATUS_TEXT;
|
|
2689
1193
|
let readStatusText = fallbackStatusText;
|
|
2690
1194
|
const controller = new FoldController(() => readStatusText());
|
|
2691
1195
|
ctx.effect(() => {
|
|
@@ -2699,7 +1203,7 @@ var __dshcfBundle = (() => {
|
|
|
2699
1203
|
if (settingsScope === void 0 || slots === void 0) return;
|
|
2700
1204
|
const scope = settingsScope.bind({ namespace: AUTO_COLLAPSE_NS });
|
|
2701
1205
|
const scopedStatusText = statusTextProvider(scope);
|
|
2702
|
-
const offScope = scope.subscribe(() => controller.
|
|
1206
|
+
const offScope = scope.subscribe(() => controller.refreshStatus());
|
|
2703
1207
|
let offSettings;
|
|
2704
1208
|
try {
|
|
2705
1209
|
offSettings = setupSettingsCard({ slots }, scope);
|
|
@@ -2708,13 +1212,13 @@ var __dshcfBundle = (() => {
|
|
|
2708
1212
|
throw error;
|
|
2709
1213
|
}
|
|
2710
1214
|
readStatusText = scopedStatusText;
|
|
2711
|
-
controller.
|
|
1215
|
+
controller.refreshStatus();
|
|
2712
1216
|
return () => {
|
|
2713
1217
|
offScope();
|
|
2714
1218
|
offSettings();
|
|
2715
1219
|
if (readStatusText === scopedStatusText) {
|
|
2716
1220
|
readStatusText = fallbackStatusText;
|
|
2717
|
-
controller.
|
|
1221
|
+
controller.refreshStatus();
|
|
2718
1222
|
}
|
|
2719
1223
|
};
|
|
2720
1224
|
}, "dsh-auto-collapse: settings scope + plugin card");
|