gz-research-assistant 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1757 @@
1
+ import { a as e, d as t, f as n, i as r, m as i, n as a, o, p as s, r as c, s as l, t as u, u as d } from "./recorder-Da1VSLid.js";
2
+ import { useEffect as f, useId as p, useRef as m, useState as h, useSyncExternalStore as g } from "react";
3
+ import { Alert as _, Badge as v, Button as y, Empty as b, Input as x, InputNumber as S, Select as C, Tabs as w, Tag as T, Tooltip as ee } from "@chenhui996/gg-ui";
4
+ import { ApiOutlined as E, BulbOutlined as D, CameraOutlined as te, CheckCircleOutlined as O, CheckOutlined as ne, ClockCircleOutlined as k, CloseOutlined as re, CodeOutlined as A, DashboardOutlined as j, DeleteOutlined as ie, DesktopOutlined as ae, DownloadOutlined as oe, EditOutlined as se, ExperimentOutlined as ce, FieldTimeOutlined as le, FileAddOutlined as ue, FileTextOutlined as M, FlagOutlined as de, FolderOpenOutlined as fe, FullscreenExitOutlined as pe, FullscreenOutlined as N, HistoryOutlined as P, InfoCircleOutlined as me, LineChartOutlined as he, LinkOutlined as F, MessageOutlined as ge, MinusOutlined as I, PauseOutlined as _e, PlayCircleOutlined as L, PlusOutlined as R, ReadOutlined as ve, RobotOutlined as ye, SafetyCertificateOutlined as be, SaveOutlined as z, ScissorOutlined as xe, SearchOutlined as B, StopOutlined as V, UploadOutlined as Se, VideoCameraOutlined as H, WarningOutlined as Ce } from "@ant-design/icons";
5
+ import { Fragment as U, jsx as W, jsxs as G } from "react/jsx-runtime";
6
+ import { OverlayScrollbars as we } from "overlayscrollbars";
7
+ //#region \0rolldown/runtime.js
8
+ var K = Object.defineProperty, q = Object.getOwnPropertyDescriptor, Te = Object.getOwnPropertyNames, Ee = Object.prototype.hasOwnProperty, De = (e, t) => {
9
+ let n = {};
10
+ for (var r in e) K(n, r, {
11
+ get: e[r],
12
+ enumerable: !0
13
+ });
14
+ return t || K(n, Symbol.toStringTag, { value: "Module" }), n;
15
+ }, J = (e, t, n, r) => {
16
+ if (t && typeof t == "object" || typeof t == "function") for (var i = Te(t), a = 0, o = i.length, s; a < o; a++) s = i[a], !Ee.call(e, s) && s !== n && K(e, s, {
17
+ get: ((e) => t[e]).bind(null, s),
18
+ enumerable: !(r = q(t, s)) || r.enumerable
19
+ });
20
+ return e;
21
+ }, Oe = (e, t, n) => (J(e, t, "default"), n && J(n, t, "default")), Y = /* @__PURE__ */ De({});
22
+ import * as X from "gz-laya-pilot/schema";
23
+ Oe(Y, X);
24
+ //#endregion
25
+ //#region src/automation/AssertionEditor.tsx
26
+ function ke({ initial: e, steps: t, position: n, disabled: r, onSave: i, onCancel: a }) {
27
+ let o = p(), [s, c] = h(e ?? {
28
+ name: "",
29
+ source: "page-text",
30
+ operator: "contains",
31
+ expected: "",
32
+ timeout: 1e4
33
+ }), [l, u] = h(n), [d, f] = h(""), m = (e) => c((t) => ({
34
+ ...t,
35
+ ...e
36
+ }));
37
+ return /* @__PURE__ */ G("div", {
38
+ className: "research-assistant__assertion-editor research-assistant__form-stack",
39
+ "aria-label": e ? "编辑断言配置" : "新增断言配置",
40
+ children: [
41
+ /* @__PURE__ */ G("h3", {
42
+ className: "research-assistant__subheading",
43
+ children: [/* @__PURE__ */ W(O, {}), e ? "编辑断言" : "添加断言"]
44
+ }),
45
+ /* @__PURE__ */ W("p", {
46
+ className: "research-assistant__hint",
47
+ children: "选择断言对象、比较条件和期望值。回放时在指定步骤后自动等待并校验。"
48
+ }),
49
+ /* @__PURE__ */ G("div", {
50
+ className: "research-assistant__field",
51
+ children: [/* @__PURE__ */ W("label", {
52
+ htmlFor: `${o}-name`,
53
+ children: "断言名称"
54
+ }), /* @__PURE__ */ W(x, {
55
+ id: `${o}-name`,
56
+ placeholder: "例如:保存后显示成功提示",
57
+ value: s.name,
58
+ maxLength: 100,
59
+ disabled: r,
60
+ onChange: (e) => m({ name: e.target.value })
61
+ })]
62
+ }),
63
+ !e && /* @__PURE__ */ G("div", {
64
+ className: "research-assistant__field",
65
+ children: [/* @__PURE__ */ W("label", {
66
+ htmlFor: `${o}-position`,
67
+ children: "执行位置"
68
+ }), /* @__PURE__ */ W(C, {
69
+ id: `${o}-position`,
70
+ value: l,
71
+ disabled: r,
72
+ onChange: u,
73
+ options: [{
74
+ value: 0,
75
+ label: "所有步骤之前"
76
+ }, ...t.map((e, t) => ({
77
+ value: t + 1,
78
+ label: `第 ${t + 1} 步之后 · ${e.assertion?.name ?? e.target?.label ?? e.value ?? e.kind}`
79
+ }))]
80
+ })]
81
+ }),
82
+ /* @__PURE__ */ G("div", {
83
+ className: "research-assistant__assertion-fields",
84
+ children: [/* @__PURE__ */ G("div", {
85
+ className: "research-assistant__field",
86
+ children: [/* @__PURE__ */ W("label", {
87
+ htmlFor: `${o}-source`,
88
+ children: "断言对象"
89
+ }), /* @__PURE__ */ W(C, {
90
+ id: `${o}-source`,
91
+ value: s.source,
92
+ disabled: r,
93
+ options: Object.entries(Y.assertionSources).map(([e, t]) => ({
94
+ value: e,
95
+ label: t
96
+ })),
97
+ onChange: (e) => m({
98
+ source: e,
99
+ operator: (0, Y.operatorsFor)(e)[0],
100
+ expected: e === "element-count" ? "1" : "",
101
+ selector: s.selector ?? ""
102
+ })
103
+ })]
104
+ }), /* @__PURE__ */ G("div", {
105
+ className: "research-assistant__field",
106
+ children: [/* @__PURE__ */ W("label", {
107
+ htmlFor: `${o}-operator`,
108
+ children: "比较条件"
109
+ }), /* @__PURE__ */ W(C, {
110
+ id: `${o}-operator`,
111
+ value: s.operator,
112
+ disabled: r,
113
+ onChange: (e) => m({ operator: e }),
114
+ options: (0, Y.operatorsFor)(s.source).map((e) => ({
115
+ value: e,
116
+ label: Y.assertionOperators[e]
117
+ }))
118
+ })]
119
+ })]
120
+ }),
121
+ s.source !== "page-text" && /* @__PURE__ */ G("div", {
122
+ className: "research-assistant__field",
123
+ children: [
124
+ /* @__PURE__ */ W("label", {
125
+ htmlFor: `${o}-selector`,
126
+ children: "元素定位(CSS)"
127
+ }),
128
+ /* @__PURE__ */ W(x, {
129
+ id: `${o}-selector`,
130
+ value: s.selector,
131
+ maxLength: 2e3,
132
+ placeholder: "例如:[data-testid=\"save-result\"]",
133
+ disabled: r,
134
+ onChange: (e) => m({ selector: e.target.value })
135
+ }),
136
+ /* @__PURE__ */ W("p", {
137
+ className: "research-assistant__hint",
138
+ children: "可从步骤的“定位信息”复制。文字、值和可见性要求唯一元素;数量支持匹配多个元素。"
139
+ })
140
+ ]
141
+ }),
142
+ s.source !== "element-state" && /* @__PURE__ */ G("div", {
143
+ className: "research-assistant__field",
144
+ children: [/* @__PURE__ */ W("label", {
145
+ htmlFor: `${o}-expected`,
146
+ children: "期望值"
147
+ }), /* @__PURE__ */ W(x, {
148
+ id: `${o}-expected`,
149
+ value: s.expected,
150
+ maxLength: 2e3,
151
+ placeholder: s.source === "element-count" ? "例如:1" : "输入期望内容",
152
+ disabled: r,
153
+ onChange: (e) => m({ expected: e.target.value })
154
+ })]
155
+ }),
156
+ /* @__PURE__ */ G("div", {
157
+ className: "research-assistant__field",
158
+ children: [/* @__PURE__ */ W("label", {
159
+ htmlFor: `${o}-timeout`,
160
+ children: "最长等待时间(毫秒)"
161
+ }), /* @__PURE__ */ W(S, {
162
+ id: `${o}-timeout`,
163
+ min: 500,
164
+ max: 3e4,
165
+ step: 500,
166
+ value: s.timeout,
167
+ disabled: r,
168
+ onChange: (e) => m({ timeout: e ?? 1e4 })
169
+ })]
170
+ }),
171
+ d && /* @__PURE__ */ W(_, {
172
+ showIcon: !0,
173
+ type: "error",
174
+ message: d
175
+ }),
176
+ /* @__PURE__ */ G("div", {
177
+ className: "research-assistant__button-row",
178
+ children: [/* @__PURE__ */ W(y, {
179
+ icon: /* @__PURE__ */ W(z, {}),
180
+ type: "primary",
181
+ disabled: r,
182
+ onClick: () => {
183
+ try {
184
+ let e = (0, Y.parseAssertion)(s);
185
+ if (e.selector) try {
186
+ document.querySelector(e.selector);
187
+ } catch {
188
+ throw Error("CSS 定位语法无效,请检查后再保存");
189
+ }
190
+ i(e, Math.min(l, t.length));
191
+ } catch (e) {
192
+ f(e instanceof Error ? e.message : "断言配置无效");
193
+ }
194
+ },
195
+ children: "保存断言"
196
+ }), /* @__PURE__ */ W(y, {
197
+ onClick: a,
198
+ children: "取消"
199
+ })]
200
+ })
201
+ ]
202
+ });
203
+ }
204
+ //#endregion
205
+ //#region src/automation/StepSupplement.tsx
206
+ function Ae({ step: e, disabled: t, onApply: n }) {
207
+ let r = p(), [i, a] = h("click"), [o, s] = h(""), [c, l] = h(""), [u, d] = h(""), [f, m] = h(!0), [g, _] = h("");
208
+ return /* @__PURE__ */ G("details", {
209
+ className: "research-assistant__supplement",
210
+ children: [
211
+ /* @__PURE__ */ W("summary", { children: "补充为可执行步骤" }),
212
+ /* @__PURE__ */ W("p", {
213
+ className: "research-assistant__hint",
214
+ children: "人工补充不会修改原始事件。填写业务控件的 CSS 定位与文字;不清楚定位时,可退出选段后重新主动录制。"
215
+ }),
216
+ /* @__PURE__ */ G("div", {
217
+ className: "research-assistant__form-stack",
218
+ children: [
219
+ /* @__PURE__ */ G("div", {
220
+ className: "research-assistant__field",
221
+ children: [/* @__PURE__ */ W("label", {
222
+ htmlFor: r,
223
+ children: "操作类型"
224
+ }), /* @__PURE__ */ W(C, {
225
+ id: r,
226
+ "aria-label": "补充操作类型",
227
+ value: i,
228
+ disabled: t,
229
+ onChange: a,
230
+ style: { width: "100%" },
231
+ options: [
232
+ {
233
+ value: "click",
234
+ label: "点击"
235
+ },
236
+ {
237
+ value: "fill",
238
+ label: "输入"
239
+ },
240
+ {
241
+ value: "select",
242
+ label: "原生下拉选择"
243
+ },
244
+ {
245
+ value: "check",
246
+ label: "勾选"
247
+ },
248
+ {
249
+ value: "press",
250
+ label: "按键"
251
+ },
252
+ {
253
+ value: "assert-text",
254
+ label: "文字检查点"
255
+ }
256
+ ]
257
+ })]
258
+ }),
259
+ i !== "assert-text" && /* @__PURE__ */ G(U, { children: [/* @__PURE__ */ W(x, {
260
+ prefix: /* @__PURE__ */ W(A, {}),
261
+ "aria-label": "补充 CSS 定位",
262
+ placeholder: "例如 button[aria-label=\"展开\"]",
263
+ maxLength: 2e3,
264
+ value: o,
265
+ disabled: t,
266
+ onChange: (e) => s(e.target.value)
267
+ }), /* @__PURE__ */ W(x, {
268
+ prefix: /* @__PURE__ */ W(se, {}),
269
+ "aria-label": "补充控件文字",
270
+ placeholder: "控件文字、aria-label 或输入框 placeholder",
271
+ maxLength: 200,
272
+ value: c,
273
+ disabled: t,
274
+ onChange: (e) => l(e.target.value)
275
+ })] }),
276
+ [
277
+ "fill",
278
+ "select",
279
+ "press",
280
+ "assert-text"
281
+ ].includes(i) && /* @__PURE__ */ W(x, {
282
+ "aria-label": "补充步骤内容",
283
+ placeholder: i === "press" ? "Enter / Escape / Tab" : "输入值或预期文字",
284
+ maxLength: 2e3,
285
+ value: u,
286
+ disabled: t,
287
+ onChange: (e) => d(e.target.value)
288
+ }),
289
+ i === "check" && /* @__PURE__ */ G("label", { children: [/* @__PURE__ */ W("input", {
290
+ type: "checkbox",
291
+ checked: f,
292
+ disabled: t,
293
+ onChange: (e) => m(e.target.checked)
294
+ }), "选中"] }),
295
+ /* @__PURE__ */ W(y, {
296
+ icon: /* @__PURE__ */ W(ne, {}),
297
+ disabled: t,
298
+ onClick: () => {
299
+ try {
300
+ if (i !== "assert-text") {
301
+ if (!o.trim() || !c.trim()) throw Error("请补充定位及控件文字");
302
+ document.querySelector(o);
303
+ }
304
+ if (i === "assert-text" && !u.trim()) throw Error("检查点文字不能为空");
305
+ if (i === "press" && ![
306
+ "Enter",
307
+ "Escape",
308
+ "Tab"
309
+ ].includes(u)) throw Error("请选择支持的按键");
310
+ n({
311
+ kind: i,
312
+ supplemented: !0,
313
+ ...e.sourceEventId ? { sourceEventId: e.sourceEventId } : {},
314
+ ...i === "assert-text" ? {} : { target: {
315
+ selector: o.trim(),
316
+ label: c.trim()
317
+ } },
318
+ ...[
319
+ "fill",
320
+ "select",
321
+ "press",
322
+ "assert-text"
323
+ ].includes(i) ? { value: u } : {},
324
+ ...i === "check" ? { checked: f } : {}
325
+ }), _("");
326
+ } catch (e) {
327
+ _(e instanceof Error ? e.message : "内容无效");
328
+ }
329
+ },
330
+ children: "应用补充"
331
+ }),
332
+ g && /* @__PURE__ */ W("span", {
333
+ role: "alert",
334
+ children: g
335
+ })
336
+ ]
337
+ })
338
+ ]
339
+ });
340
+ }
341
+ //#endregion
342
+ //#region src/automation/AutomationPanel.tsx
343
+ var je = {
344
+ click: "点击",
345
+ fill: "输入",
346
+ select: "选择",
347
+ check: "勾选",
348
+ press: "按键",
349
+ "assert-text": "检查文字",
350
+ assert: "断言",
351
+ unsupported: "需要处理"
352
+ }, Me = {
353
+ state: "idle",
354
+ step: 0,
355
+ total: 0,
356
+ message: "尚未运行"
357
+ };
358
+ async function Ne(e, t, n) {
359
+ let r = await fetch(`${e.replace(/\/$/, "")}/${t}`, n === void 0 ? {} : {
360
+ method: "POST",
361
+ headers: {
362
+ "Content-Type": "application/json",
363
+ "X-Laya-Pilot": "recorded-case-v1"
364
+ },
365
+ body: JSON.stringify(n)
366
+ });
367
+ if (!r.headers.get("content-type")?.includes("application/json")) throw Error("回放服务未连接,请检查宿主配置的服务地址");
368
+ let i = await r.json();
369
+ if (!r.ok) throw Error(i.message || "本地回放服务不可用");
370
+ return i;
371
+ }
372
+ function Pe({ close: e, bridgeUrl: t }) {
373
+ let n = g(u.subscribe, u.getState), [r, i] = h(""), [a, o] = h(null), s = m(null), c = (e, t) => {
374
+ o({
375
+ position: e,
376
+ edit: t
377
+ }), requestAnimationFrame(() => s.current?.scrollIntoView({
378
+ behavior: "smooth",
379
+ block: "nearest"
380
+ }));
381
+ }, [l, d] = h(Me), [p, v] = h(!1), [b, S] = h(!1), [C, w] = h(2e3), ee = m(null);
382
+ f(() => {
383
+ let e = !1, n, r = async () => {
384
+ try {
385
+ let n = await Ne(t, "status");
386
+ e || (d(n), v(n.available !== !1));
387
+ } catch {
388
+ e || v(!1);
389
+ } finally {
390
+ e || (n = setTimeout(r, 1500));
391
+ }
392
+ };
393
+ return r(), () => {
394
+ e = !0, clearTimeout(n);
395
+ };
396
+ }, [t]);
397
+ let E = b || l.state === "running", D = async (e) => {
398
+ try {
399
+ i(""), await e();
400
+ } catch (e) {
401
+ i(e instanceof Error ? e.message : "操作失败");
402
+ }
403
+ }, te = (e, t) => u.edit({
404
+ ...n.draft,
405
+ steps: n.draft.steps.map((n, r) => r === e ? {
406
+ ...n,
407
+ ...t,
408
+ supplemented: !0
409
+ } : n)
410
+ });
411
+ return /* @__PURE__ */ G("div", {
412
+ className: "research-assistant__section research-assistant__automation",
413
+ children: [
414
+ /* @__PURE__ */ G("div", {
415
+ className: "research-assistant__section-heading",
416
+ children: [/* @__PURE__ */ W("span", {
417
+ className: "research-assistant__heading-icon",
418
+ children: /* @__PURE__ */ W(H, {})
419
+ }), /* @__PURE__ */ G("div", { children: [/* @__PURE__ */ W("h3", { children: "把操作变成可复现的用例" }), /* @__PURE__ */ W("p", { children: "录制操作,配置断言,再验证结果。" })] })]
420
+ }),
421
+ !p && /* @__PURE__ */ W(_, {
422
+ type: "warning",
423
+ message: "回放服务未连接。仍可录制、编辑断言和导出用例。"
424
+ }),
425
+ r && /* @__PURE__ */ W(_, {
426
+ closable: !0,
427
+ onClose: () => i(""),
428
+ type: "info",
429
+ message: r
430
+ }),
431
+ /* @__PURE__ */ G("div", {
432
+ className: "research-assistant__automation-layout",
433
+ children: [
434
+ /* @__PURE__ */ G("section", {
435
+ className: "research-assistant__card research-assistant__record-card",
436
+ "aria-label": "录制操作",
437
+ children: [
438
+ /* @__PURE__ */ G("div", {
439
+ className: "research-assistant__card-heading",
440
+ children: [/* @__PURE__ */ G("h3", { children: [/* @__PURE__ */ W(H, {}), "录制操作"] }), /* @__PURE__ */ G(T, {
441
+ color: n.recording ? "red" : "default",
442
+ children: [
443
+ n.recording ? "录制中" : "待录制",
444
+ " · ",
445
+ n.draft.steps.length,
446
+ "/100 步"
447
+ ]
448
+ })]
449
+ }),
450
+ /* @__PURE__ */ G("div", {
451
+ className: "research-assistant__button-row",
452
+ children: [/* @__PURE__ */ W(y, {
453
+ icon: /* @__PURE__ */ W(H, {}),
454
+ type: "primary",
455
+ disabled: E || n.recording,
456
+ onClick: () => void D(() => {
457
+ o(null), u.start(), e();
458
+ }),
459
+ children: "开始新录制"
460
+ }), /* @__PURE__ */ W(y, {
461
+ icon: /* @__PURE__ */ W(V, {}),
462
+ disabled: !n.recording,
463
+ onClick: () => u.stop(),
464
+ children: "结束录制"
465
+ })]
466
+ }),
467
+ /* @__PURE__ */ G("details", {
468
+ className: "research-assistant__help",
469
+ children: [
470
+ /* @__PURE__ */ G("summary", { children: [/* @__PURE__ */ W(me, {}), "录制范围与使用说明"] }),
471
+ /* @__PURE__ */ W("p", {
472
+ className: "research-assistant__hint",
473
+ children: "先将业务页面恢复到初始状态,再开始录制;回放目标由宿主配置。需保留用例时请导出 JSON。"
474
+ }),
475
+ /* @__PURE__ */ W("p", {
476
+ className: "research-assistant__hint",
477
+ children: "录制会保存普通字段输入值。密码等敏感输入不记录;上传、跨域 iframe、拖拽和刷新暂不支持。保存前请检查步骤。回放会真实操作模拟数据,不会自动恢复录制前的数据。"
478
+ })
479
+ ]
480
+ })
481
+ ]
482
+ }),
483
+ /* @__PURE__ */ G("section", {
484
+ className: "research-assistant__card research-assistant__editor-card",
485
+ "aria-label": "编辑用例",
486
+ children: [
487
+ /* @__PURE__ */ G("div", {
488
+ className: "research-assistant__card-heading",
489
+ children: [/* @__PURE__ */ G("h3", { children: [/* @__PURE__ */ W(se, {}), "编辑用例"] }), /* @__PURE__ */ G("span", { children: [n.draft.steps.length, " 个步骤"] })]
490
+ }),
491
+ /* @__PURE__ */ G("div", {
492
+ className: "research-assistant__field",
493
+ children: [/* @__PURE__ */ W("label", {
494
+ htmlFor: "assistant-case-name",
495
+ children: "用例名称"
496
+ }), /* @__PURE__ */ W(x, {
497
+ id: "assistant-case-name",
498
+ prefix: /* @__PURE__ */ W(M, {}),
499
+ "aria-label": "测试用例名称",
500
+ value: n.draft.name,
501
+ maxLength: 100,
502
+ disabled: n.recording || E,
503
+ onChange: (e) => u.edit({
504
+ ...n.draft,
505
+ name: e.target.value
506
+ })
507
+ })]
508
+ }),
509
+ !n.draft.steps.length && /* @__PURE__ */ G("div", {
510
+ className: "research-assistant__empty-steps",
511
+ children: [
512
+ /* @__PURE__ */ W(M, {}),
513
+ /* @__PURE__ */ W("span", { children: "还没有操作步骤" }),
514
+ /* @__PURE__ */ W("p", { children: "开始录制,或导入已有用例。" })
515
+ ]
516
+ }),
517
+ /* @__PURE__ */ W("ol", {
518
+ className: "research-assistant__case-steps",
519
+ children: n.draft.steps.map((e, t) => /* @__PURE__ */ G("li", { children: [
520
+ /* @__PURE__ */ G("div", {
521
+ className: "research-assistant__step-heading",
522
+ children: [
523
+ /* @__PURE__ */ W("span", {
524
+ className: "research-assistant__step-number",
525
+ children: String(t + 1).padStart(2, "0")
526
+ }),
527
+ /* @__PURE__ */ W(T, {
528
+ color: e.kind === "unsupported" ? "red" : "blue",
529
+ children: je[e.kind]
530
+ }),
531
+ /* @__PURE__ */ W("strong", { children: e.assertion?.name ?? e.target?.label ?? e.value }),
532
+ e.sourceEventId && /* @__PURE__ */ G(T, { children: ["源事件 #", e.sourceEventId] }),
533
+ e.supplemented && /* @__PURE__ */ W(T, {
534
+ color: "orange",
535
+ children: "人工补充"
536
+ })
537
+ ]
538
+ }),
539
+ e.assertion && /* @__PURE__ */ G("div", {
540
+ className: "research-assistant__assertion-summary",
541
+ children: [
542
+ /* @__PURE__ */ W(T, {
543
+ color: "green",
544
+ children: Y.assertionSources[e.assertion.source]
545
+ }),
546
+ /* @__PURE__ */ G("span", { children: [
547
+ Y.assertionOperators[e.assertion.operator],
548
+ " ",
549
+ e.assertion.expected === "" ? "空字符串" : e.assertion.expected
550
+ ] }),
551
+ /* @__PURE__ */ G("span", {
552
+ className: "research-assistant__hint",
553
+ children: [
554
+ "最多等待 ",
555
+ e.assertion.timeout / 1e3,
556
+ " 秒"
557
+ ]
558
+ }),
559
+ e.assertion.selector && /* @__PURE__ */ W("code", { children: e.assertion.selector })
560
+ ]
561
+ }),
562
+ e.kind === "unsupported" && /* @__PURE__ */ W(Ae, {
563
+ step: e,
564
+ disabled: n.recording || E,
565
+ onApply: (e) => u.edit({
566
+ ...n.draft,
567
+ steps: n.draft.steps.map((n, r) => r === t ? e : n)
568
+ })
569
+ }, JSON.stringify(e)),
570
+ e.value !== void 0 && e.kind !== "unsupported" && /* @__PURE__ */ W(x, {
571
+ "aria-label": `第 ${t + 1} 步内容`,
572
+ value: e.value,
573
+ maxLength: 2e3,
574
+ disabled: n.recording || E,
575
+ onChange: (e) => te(t, { value: e.target.value })
576
+ }),
577
+ e.kind === "check" && /* @__PURE__ */ W("span", { children: e.checked ? "选中" : "取消选中" }),
578
+ e.target && /* @__PURE__ */ G("details", { children: [/* @__PURE__ */ W("summary", { children: "定位信息" }), /* @__PURE__ */ W("code", { children: e.target.selector })] }),
579
+ /* @__PURE__ */ G("div", {
580
+ className: "research-assistant__button-row research-assistant__step-actions",
581
+ children: [
582
+ /* @__PURE__ */ W(y, {
583
+ size: "small",
584
+ icon: /* @__PURE__ */ W(O, {}),
585
+ disabled: n.recording || E || !e.assertion && n.draft.steps.length >= 100,
586
+ onClick: () => c(t + 1, e.assertion ? t : void 0),
587
+ children: e.assertion ? "编辑断言" : "在此步后添加断言"
588
+ }),
589
+ /* @__PURE__ */ G(y, {
590
+ icon: /* @__PURE__ */ W(R, {}),
591
+ size: "small",
592
+ disabled: n.recording || E || n.draft.steps.length >= 100,
593
+ onClick: () => {
594
+ o(null), u.edit({
595
+ ...n.draft,
596
+ steps: [
597
+ ...n.draft.steps.slice(0, t),
598
+ {
599
+ kind: "unsupported",
600
+ value: "人工前置步骤待补充"
601
+ },
602
+ ...n.draft.steps.slice(t)
603
+ ]
604
+ });
605
+ },
606
+ children: [
607
+ "在第 ",
608
+ t + 1,
609
+ " 步前插入"
610
+ ]
611
+ }),
612
+ /* @__PURE__ */ G(y, {
613
+ icon: /* @__PURE__ */ W(ie, {}),
614
+ danger: !0,
615
+ size: "small",
616
+ disabled: n.recording || E,
617
+ onClick: () => {
618
+ o(null), u.edit({
619
+ ...n.draft,
620
+ steps: n.draft.steps.filter((e, n) => n !== t)
621
+ });
622
+ },
623
+ children: [
624
+ "移除第 ",
625
+ t + 1,
626
+ " 步"
627
+ ]
628
+ })
629
+ ]
630
+ })
631
+ ] }, t))
632
+ }),
633
+ n.draft.timeline && /* @__PURE__ */ G("details", { children: [
634
+ /* @__PURE__ */ G("summary", { children: [
635
+ "选段原始证据(",
636
+ n.draft.timeline.events.length,
637
+ " 条,只读)"
638
+ ] }),
639
+ /* @__PURE__ */ W("p", { children: "接口、异常和性能事件不执行;人工补充与移除步骤不会改写这里的来源记录。" }),
640
+ /* @__PURE__ */ W("pre", {
641
+ className: "research-assistant__json",
642
+ children: JSON.stringify(n.draft.timeline, null, 2)
643
+ })
644
+ ] }),
645
+ /* @__PURE__ */ G("div", {
646
+ ref: s,
647
+ className: "research-assistant__assertion-area",
648
+ children: [
649
+ /* @__PURE__ */ G("div", {
650
+ className: "research-assistant__card-heading",
651
+ children: [/* @__PURE__ */ G("h3", { children: [/* @__PURE__ */ W(O, {}), " 结果断言"] }), /* @__PURE__ */ G(T, { children: [
652
+ n.draft.steps.filter((e) => e.kind === "assert" || e.kind === "assert-text").length,
653
+ " ",
654
+ "条"
655
+ ] })]
656
+ }),
657
+ /* @__PURE__ */ W("p", {
658
+ className: "research-assistant__hint",
659
+ children: "断言按步骤顺序执行,可检查页面文字、元素文字、输入值、可见性和数量。"
660
+ }),
661
+ a ? /* @__PURE__ */ W(ke, {
662
+ initial: a.edit === void 0 ? void 0 : n.draft.steps[a.edit]?.assertion,
663
+ steps: n.draft.steps,
664
+ position: a.position,
665
+ disabled: E || n.recording,
666
+ onCancel: () => o(null),
667
+ onSave: (e, t) => {
668
+ let r = [...n.draft.steps];
669
+ a.edit === void 0 ? r.splice(t, 0, {
670
+ kind: "assert",
671
+ assertion: e,
672
+ supplemented: !0
673
+ }) : r[a.edit] = {
674
+ ...r[a.edit],
675
+ kind: "assert",
676
+ assertion: e,
677
+ supplemented: !0
678
+ }, u.edit((0, Y.parseCase)({
679
+ ...n.draft,
680
+ steps: r
681
+ })), o(null);
682
+ }
683
+ }, `${a.position}-${a.edit ?? "new"}`) : /* @__PURE__ */ W(y, {
684
+ icon: /* @__PURE__ */ W(R, {}),
685
+ disabled: E || n.recording || n.draft.steps.length >= 100,
686
+ onClick: () => c(n.draft.steps.length),
687
+ children: "添加断言"
688
+ })
689
+ ]
690
+ }),
691
+ /* @__PURE__ */ G("div", {
692
+ className: "research-assistant__button-row research-assistant__card-actions",
693
+ children: [
694
+ /* @__PURE__ */ W(y, {
695
+ icon: /* @__PURE__ */ W(z, {}),
696
+ disabled: n.recording || E || !n.draft.steps.length,
697
+ onClick: () => void D(() => {
698
+ let e = (0, Y.parseCase)(n.draft);
699
+ if (e.steps.some((e) => e.kind === "unsupported")) throw Error("请处理“需要处理”的步骤,再保存用例");
700
+ u.save(e), i("已保存到当前页面内存,可以开始回放或导出");
701
+ }),
702
+ children: "保存用例"
703
+ }),
704
+ /* @__PURE__ */ W(y, {
705
+ icon: /* @__PURE__ */ W(oe, {}),
706
+ disabled: !n.saved,
707
+ onClick: () => {
708
+ let e = URL.createObjectURL(new Blob([JSON.stringify(n.saved, null, 2)], { type: "application/json" })), t = document.createElement("a");
709
+ t.href = e, t.download = "页面录制用例.json", document.body.append(t), t.click(), t.remove(), setTimeout(() => URL.revokeObjectURL(e), 3e4);
710
+ },
711
+ children: "导出用例 JSON"
712
+ }),
713
+ /* @__PURE__ */ W(y, {
714
+ icon: /* @__PURE__ */ W(Se, {}),
715
+ disabled: n.recording || E,
716
+ onClick: () => ee.current?.click(),
717
+ children: "导入用例"
718
+ }),
719
+ /* @__PURE__ */ W("input", {
720
+ ref: ee,
721
+ hidden: !0,
722
+ type: "file",
723
+ accept: ".json,application/json",
724
+ onChange: (e) => {
725
+ let t = e.target.files?.[0];
726
+ e.target.value = "", t && D(async () => {
727
+ if (t.size > 512 * 1024) throw Error("用例不能超过 512 KB");
728
+ u.save((0, Y.parseCase)(JSON.parse(await t.text()))), o(null), i("已导入,请检查步骤后开始回放");
729
+ });
730
+ }
731
+ })
732
+ ]
733
+ }),
734
+ n.saved && /* @__PURE__ */ G("p", {
735
+ className: "research-assistant__saved-case",
736
+ children: [
737
+ /* @__PURE__ */ W(O, {}),
738
+ "已保存:",
739
+ n.saved.name,
740
+ "(",
741
+ n.saved.steps.length,
742
+ " 步)",
743
+ JSON.stringify(n.saved) !== JSON.stringify(n.draft) && /* @__PURE__ */ W(T, {
744
+ color: "orange",
745
+ children: "草稿有未保存修改"
746
+ })
747
+ ]
748
+ })
749
+ ]
750
+ }),
751
+ /* @__PURE__ */ G("section", {
752
+ className: "research-assistant__card research-assistant__replay-card",
753
+ "aria-label": "执行回放",
754
+ children: [
755
+ /* @__PURE__ */ W("div", {
756
+ className: "research-assistant__card-heading",
757
+ children: /* @__PURE__ */ G("h3", { children: [/* @__PURE__ */ W(L, {}), "执行回放"] })
758
+ }),
759
+ /* @__PURE__ */ W("p", {
760
+ className: "research-assistant__hint",
761
+ children: "保存后在独立浏览器中执行,按断言验证结果。"
762
+ }),
763
+ /* @__PURE__ */ G("div", {
764
+ className: "research-assistant__field",
765
+ children: [/* @__PURE__ */ G("label", {
766
+ htmlFor: "assistant-step-delay",
767
+ children: [/* @__PURE__ */ W(k, {}), "每步间隔"]
768
+ }), /* @__PURE__ */ G("div", {
769
+ className: "research-assistant__delay-row",
770
+ children: [/* @__PURE__ */ W("input", {
771
+ id: "assistant-step-delay",
772
+ "aria-label": "回放步骤间隔",
773
+ type: "number",
774
+ min: 0,
775
+ max: 1e4,
776
+ step: 500,
777
+ value: C,
778
+ disabled: E,
779
+ onChange: (e) => w(Number(e.target.value))
780
+ }), /* @__PURE__ */ W("span", { children: "毫秒" })]
781
+ })]
782
+ }),
783
+ /* @__PURE__ */ G("div", {
784
+ className: "research-assistant__button-row",
785
+ children: [/* @__PURE__ */ W(y, {
786
+ icon: /* @__PURE__ */ W(L, {}),
787
+ type: "primary",
788
+ disabled: !p || !n.saved || n.saved.steps.some((e) => e.kind === "unsupported") || n.recording || E || JSON.stringify(n.saved) !== JSON.stringify(n.draft),
789
+ onClick: () => void D(async () => {
790
+ S(!0);
791
+ try {
792
+ d(await Ne(t, "run", {
793
+ testCase: n.saved,
794
+ delay: C,
795
+ headed: !0
796
+ }));
797
+ } finally {
798
+ S(!1);
799
+ }
800
+ }),
801
+ children: "开始回放(本地 Chrome)"
802
+ }), /* @__PURE__ */ W(y, {
803
+ icon: /* @__PURE__ */ W(V, {}),
804
+ disabled: l.state !== "running",
805
+ onClick: () => void D(async () => {
806
+ await Ne(t, "stop", {});
807
+ }),
808
+ children: "停止回放"
809
+ })]
810
+ }),
811
+ /* @__PURE__ */ W(_, {
812
+ showIcon: !0,
813
+ type: l.state === "failed" ? "error" : l.state === "passed" ? "success" : "info",
814
+ message: `${l.step}/${l.total} · ${l.message}`
815
+ }),
816
+ !!l.assertions?.length && /* @__PURE__ */ G("div", {
817
+ className: "research-assistant__assertion-results",
818
+ "aria-label": "断言结果",
819
+ children: [/* @__PURE__ */ G("h3", {
820
+ className: "research-assistant__subheading",
821
+ children: [/* @__PURE__ */ W(O, {}), " 断言结果"]
822
+ }), l.assertions.map((e) => /* @__PURE__ */ G("div", {
823
+ className: "research-assistant__assertion-result",
824
+ children: [
825
+ /* @__PURE__ */ G("div", {
826
+ className: "research-assistant__card-heading",
827
+ children: [/* @__PURE__ */ G("strong", { children: [
828
+ "第 ",
829
+ e.step,
830
+ " 步 · ",
831
+ e.name
832
+ ] }), /* @__PURE__ */ W(T, {
833
+ color: e.passed ? "green" : "red",
834
+ children: e.passed ? "通过" : "失败"
835
+ })]
836
+ }),
837
+ /* @__PURE__ */ G("dl", { children: [
838
+ /* @__PURE__ */ W("dt", { children: "期望" }),
839
+ /* @__PURE__ */ W("dd", { children: e.expected }),
840
+ /* @__PURE__ */ W("dt", { children: "实际" }),
841
+ /* @__PURE__ */ W("dd", { children: e.actual || "空字符串" })
842
+ ] }),
843
+ /* @__PURE__ */ G("span", {
844
+ className: "research-assistant__hint",
845
+ children: [
846
+ "耗时 ",
847
+ e.duration,
848
+ " ms"
849
+ ]
850
+ })
851
+ ]
852
+ }, e.step))]
853
+ })
854
+ ]
855
+ })
856
+ ]
857
+ })
858
+ ]
859
+ });
860
+ }
861
+ //#endregion
862
+ //#region src/automation/timeline-case.ts
863
+ function Fe(e) {
864
+ let t = /* @__PURE__ */ new Set();
865
+ return e.map((e) => {
866
+ if (!Number.isSafeInteger(e.id) || e.id <= 0 || t.has(e.id)) throw Error("事件编号重复或无效,无法选段");
867
+ return t.add(e.id), {
868
+ id: e.id,
869
+ time: e.time,
870
+ kind: e.kind,
871
+ title: s(e.title, 180),
872
+ page: i(e.page),
873
+ ...e.actionId ? { actionId: e.actionId } : {}
874
+ };
875
+ }).sort((e, t) => e.time - t.time || e.id - t.id);
876
+ }
877
+ function Ie(e, t, n) {
878
+ let r = e.findIndex((e) => e.id === t), i = e.findIndex((e) => e.id === n);
879
+ return r < 0 || i < r ? [] : e.slice(r, i + 1);
880
+ }
881
+ function Le(e, t, n) {
882
+ let r = Ie(e, t, n);
883
+ if (!r.length) throw Error("请选择按时间先后排列的起点和终点");
884
+ let i = r.filter((e) => [
885
+ "route",
886
+ "click",
887
+ "change"
888
+ ].includes(e.kind));
889
+ if (!i.length) throw Error("该片段只有排查证据,没有页面操作,不能生成用例");
890
+ if (i.length >= 100) throw Error("操作过多,请缩小选段范围(需为前置步骤预留一个位置)");
891
+ let a = new URL(i[0].page);
892
+ if (!["http:", "https:"].includes(a.protocol) || i.some((e) => new URL(e.page).origin !== a.origin)) throw Error("请选择同一站点内的片段");
893
+ return (0, Y.parseCase)({
894
+ schema: "zhidao-recorded-case",
895
+ version: 1,
896
+ name: `时间线片段 #${t}–#${n}`,
897
+ path: a.pathname,
898
+ timeline: {
899
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
900
+ events: structuredClone(r)
901
+ },
902
+ steps: [{
903
+ kind: "unsupported",
904
+ value: "前置条件待确认:回放从片段的入口页面开始。请补充登录、筛选、弹窗等必要步骤;无需前置操作时可移除此项。"
905
+ }, ...i.map((e) => ({
906
+ kind: "unsupported",
907
+ sourceEventId: e.id,
908
+ value: e.kind === "change" ? `待补充字段定位、操作类型和输入值:${e.title}(历史输入值未采集)` : e.kind === "route" ? `待确认页面前置状态:${e.title}(不会自动恢复原现场)` : `待补充点击目标:${e.title}(历史记录没有可回放定位,不能按文字猜测)`
909
+ }))]
910
+ });
911
+ }
912
+ //#endregion
913
+ //#region src/TimelinePanel.tsx
914
+ var Re = {
915
+ route: "页面",
916
+ click: "点击",
917
+ change: "字段",
918
+ request: "接口",
919
+ error: "异常",
920
+ performance: "性能"
921
+ };
922
+ function ze({ events: e, onDraft: t }) {
923
+ let n = g(u.subscribe, u.getState), [r, i] = h(null), [a, o] = h(), [s, c] = h(), [l, d] = h("all"), [f, p] = h(""), [m, v] = h(""), S = r ? Ie(r, a, s) : [], C = new Set(S.map((e) => e.id)), w = r ?? e;
924
+ return /* @__PURE__ */ G("div", {
925
+ className: "research-assistant__section",
926
+ children: [
927
+ /* @__PURE__ */ G("div", {
928
+ className: "research-assistant__section-heading",
929
+ children: [/* @__PURE__ */ W("span", {
930
+ className: "research-assistant__heading-icon",
931
+ children: /* @__PURE__ */ W(P, {})
932
+ }), /* @__PURE__ */ G("div", { children: [/* @__PURE__ */ W("h3", { children: "操作时间线" }), /* @__PURE__ */ W("p", { children: "沿着操作轨迹,找到问题发生的时刻。" })] })]
933
+ }),
934
+ /* @__PURE__ */ G("div", {
935
+ className: "research-assistant__button-row",
936
+ children: [/* @__PURE__ */ W(y, {
937
+ icon: /* @__PURE__ */ W(xe, {}),
938
+ disabled: n.recording || !e.length || !!r,
939
+ onClick: () => {
940
+ try {
941
+ i(Fe(e)), o(void 0), c(void 0), v("");
942
+ } catch (e) {
943
+ v(e.message);
944
+ }
945
+ },
946
+ children: "开始选段"
947
+ }), r && /* @__PURE__ */ G(U, { children: [/* @__PURE__ */ W(y, {
948
+ icon: /* @__PURE__ */ W(re, {}),
949
+ onClick: () => {
950
+ i(null), o(void 0), c(void 0), v("");
951
+ },
952
+ children: "退出选段"
953
+ }), /* @__PURE__ */ W(y, {
954
+ icon: /* @__PURE__ */ W(ue, {}),
955
+ type: "primary",
956
+ disabled: !S.length || n.recording,
957
+ onClick: () => {
958
+ try {
959
+ u.loadDraft(Le(r, a, s)), t();
960
+ } catch (e) {
961
+ v(e.message);
962
+ }
963
+ },
964
+ children: "生成用例草稿"
965
+ })] })]
966
+ }),
967
+ r && /* @__PURE__ */ W(_, {
968
+ type: "info",
969
+ message: `已固定 ${r.length} 条事件;起点 ${a ? "#" + a : "未选"} → 终点 ${s ? "#" + s : "未选"},选中 ${S.length} 条。`,
970
+ description: "列表仍按最新在上显示,生成步骤按实际时间正序。选段包含中间全部事件;接口、异常、性能只保留为证据。生成草稿会替换当前未保存草稿,保留已保存用例。"
971
+ }),
972
+ m && /* @__PURE__ */ W(_, {
973
+ type: "warning",
974
+ message: m
975
+ }),
976
+ !r && /* @__PURE__ */ G("div", {
977
+ className: "research-assistant__filters",
978
+ children: [/* @__PURE__ */ G("select", {
979
+ "aria-label": "事件类型",
980
+ value: l,
981
+ onChange: (e) => d(e.target.value),
982
+ children: [/* @__PURE__ */ W("option", {
983
+ value: "all",
984
+ children: "全部事件"
985
+ }), Object.entries(Re).map(([e, t]) => /* @__PURE__ */ W("option", {
986
+ value: e,
987
+ children: t
988
+ }, e))]
989
+ }), /* @__PURE__ */ W(x, {
990
+ prefix: /* @__PURE__ */ W(B, {}),
991
+ "aria-label": "搜索时间线",
992
+ placeholder: "搜索操作或页面",
993
+ value: f,
994
+ onChange: (e) => p(e.target.value),
995
+ allowClear: !0
996
+ })]
997
+ }),
998
+ /* @__PURE__ */ W("p", {
999
+ className: "research-assistant__hint",
1000
+ children: "最新记录在最上方;接口详情中的 actionId 对应触发操作编号。历史记录缺少输入值及定位信息,生成后需要补充。"
1001
+ }),
1002
+ !w.length && /* @__PURE__ */ W(b, { description: "等待页面操作" }),
1003
+ /* @__PURE__ */ W("ol", {
1004
+ className: "research-assistant__timeline",
1005
+ children: w.filter((e) => !!r || (l === "all" || e.kind === l) && (e.title + e.page).includes(f)).slice().sort((e, t) => t.time - e.time || t.id - e.id).map((e) => /* @__PURE__ */ G("li", {
1006
+ className: e.kind === "error" ? "is-error" : "",
1007
+ children: [
1008
+ /* @__PURE__ */ G("div", {
1009
+ className: "research-assistant__event-meta",
1010
+ children: [
1011
+ /* @__PURE__ */ W(T, {
1012
+ color: e.kind === "error" ? "red" : "blue",
1013
+ children: Re[e.kind]
1014
+ }),
1015
+ /* @__PURE__ */ W("time", { children: new Date(e.time).toLocaleTimeString("zh-CN", { hour12: !1 }) }),
1016
+ /* @__PURE__ */ G("small", { children: ["#", e.id] }),
1017
+ C.has(e.id) && /* @__PURE__ */ W(T, {
1018
+ color: "gold",
1019
+ children: "已选"
1020
+ })
1021
+ ]
1022
+ }),
1023
+ /* @__PURE__ */ W("strong", { children: e.title }),
1024
+ r && /* @__PURE__ */ G("div", {
1025
+ className: "research-assistant__button-row",
1026
+ children: [/* @__PURE__ */ W(y, {
1027
+ icon: /* @__PURE__ */ W(de, {}),
1028
+ size: "small",
1029
+ "aria-label": `从事件 ${e.id} 开始`,
1030
+ onClick: () => {
1031
+ o(e.id), c(void 0);
1032
+ },
1033
+ children: "设为起点"
1034
+ }), /* @__PURE__ */ W(y, {
1035
+ icon: /* @__PURE__ */ W(O, {}),
1036
+ size: "small",
1037
+ "aria-label": `到事件 ${e.id} 结束`,
1038
+ disabled: a === void 0 || r.findIndex((t) => t.id === e.id) < r.findIndex((e) => e.id === a),
1039
+ onClick: () => c(e.id),
1040
+ children: "设为终点"
1041
+ })]
1042
+ }),
1043
+ /* @__PURE__ */ G("details", { children: [/* @__PURE__ */ G("summary", { children: ["详情", e.actionId ? ` · 关联操作 #${e.actionId}` : ""] }), /* @__PURE__ */ W("pre", {
1044
+ className: "research-assistant__json",
1045
+ children: JSON.stringify({
1046
+ page: e.page,
1047
+ detail: "detail" in e ? e.detail : void 0
1048
+ }, null, 2)
1049
+ })] })
1050
+ ]
1051
+ }, e.id))
1052
+ })
1053
+ ]
1054
+ });
1055
+ }
1056
+ //#endregion
1057
+ //#region src/AssistantAvatar.tsx
1058
+ function Be() {
1059
+ return /* @__PURE__ */ W("span", {
1060
+ className: "research-assistant__avatar",
1061
+ "aria-hidden": "true",
1062
+ children: /* @__PURE__ */ W(ye, {})
1063
+ });
1064
+ }
1065
+ //#endregion
1066
+ //#region src/useOverlayScrollbars.ts
1067
+ function Ve(e, t) {
1068
+ f(() => {
1069
+ let n = e.current;
1070
+ if (!n || !t) return;
1071
+ let r = /* @__PURE__ */ new Map(), i = 0, a = () => {
1072
+ for (let [e, t] of r) n.contains(e) || (t.destroy(), r.delete(e));
1073
+ for (let e of n.querySelectorAll(".research-assistant__body,.research-assistant__json,.research-assistant__steps,.ant-tabs-content-holder,.ant-tabs-body-holder")) r.has(e) || r.set(e, we({
1074
+ target: e,
1075
+ elements: { viewport: e }
1076
+ }, {
1077
+ overflow: {
1078
+ x: "scroll",
1079
+ y: "scroll"
1080
+ },
1081
+ scrollbars: {
1082
+ theme: "os-theme-dark",
1083
+ autoHide: "scroll",
1084
+ autoHideDelay: 1200,
1085
+ dragScroll: !0
1086
+ }
1087
+ }));
1088
+ }, o = new MutationObserver(() => {
1089
+ cancelAnimationFrame(i), i = requestAnimationFrame(a);
1090
+ });
1091
+ return o.observe(n, {
1092
+ childList: !0,
1093
+ subtree: !0
1094
+ }), a(), () => {
1095
+ o.disconnect(), cancelAnimationFrame(i), r.forEach((e) => e.destroy());
1096
+ };
1097
+ }, [e, t]);
1098
+ }
1099
+ //#endregion
1100
+ //#region src/FloatingPanel.tsx
1101
+ var Z = 12, Q = (e) => {
1102
+ let t = Math.min(Math.max(360, e.width), window.innerWidth - Z * 2), n = Math.min(Math.max(380, e.height), window.innerHeight - Z * 2);
1103
+ return {
1104
+ width: t,
1105
+ height: n,
1106
+ x: Math.max(Z, Math.min(e.x, window.innerWidth - t - Z)),
1107
+ y: Math.max(Z, Math.min(e.y, window.innerHeight - n - Z))
1108
+ };
1109
+ }, He = (e) => {
1110
+ let { open: t, onMinimize: n, children: r, developer: i } = e, [a, o] = h(() => Q({
1111
+ x: window.innerWidth - 564,
1112
+ y: window.innerHeight - 744,
1113
+ width: 540,
1114
+ height: 720
1115
+ })), [s, c] = h(!1), [l, u] = h(!1), d = m(null);
1116
+ Ve(d, t);
1117
+ let p = m(null);
1118
+ f(() => {
1119
+ let e = () => o((e) => Q(e));
1120
+ return window.addEventListener("resize", e), () => window.removeEventListener("resize", e);
1121
+ }, []), f(() => {
1122
+ t && d.current?.focus({ preventScroll: !0 });
1123
+ }, [t]);
1124
+ let g = (e, t = !1) => {
1125
+ s || e.button !== 0 || e.target.closest("button") || (e.preventDefault(), e.currentTarget.setPointerCapture(e.pointerId), p.current = {
1126
+ x: e.clientX,
1127
+ y: e.clientY,
1128
+ rect: a,
1129
+ resize: t
1130
+ }, u(!0));
1131
+ }, _ = (e) => {
1132
+ let t = p.current;
1133
+ if (!t) return;
1134
+ let n = e.clientX - t.x, r = e.clientY - t.y;
1135
+ o(Q(t.resize ? {
1136
+ ...t.rect,
1137
+ width: Math.min(t.rect.width + n, window.innerWidth - t.rect.x - Z),
1138
+ height: Math.min(t.rect.height + r, window.innerHeight - t.rect.y - Z)
1139
+ } : {
1140
+ ...t.rect,
1141
+ x: t.rect.x + n,
1142
+ y: t.rect.y + r
1143
+ }));
1144
+ }, v = () => {
1145
+ p.current = null, u(!1);
1146
+ };
1147
+ return /* @__PURE__ */ G("section", {
1148
+ ref: d,
1149
+ id: "research-assistant-window",
1150
+ role: "dialog",
1151
+ "aria-modal": "false",
1152
+ "aria-labelledby": "research-assistant-title",
1153
+ tabIndex: -1,
1154
+ hidden: !t,
1155
+ "data-research-assistant": !0,
1156
+ className: `research-assistant research-assistant__window${s ? " is-expanded" : ""}${l ? " is-moving" : ""}`,
1157
+ style: s ? void 0 : {
1158
+ left: a.x,
1159
+ top: a.y,
1160
+ width: a.width,
1161
+ height: a.height
1162
+ },
1163
+ onKeyDown: (e) => {
1164
+ e.key === "Escape" && e.target === e.currentTarget && n();
1165
+ },
1166
+ children: [
1167
+ /* @__PURE__ */ G("header", {
1168
+ className: "research-assistant__header",
1169
+ children: [/* @__PURE__ */ G("div", {
1170
+ className: "research-assistant__drag",
1171
+ tabIndex: 0,
1172
+ role: "group",
1173
+ "aria-label": "移动小蜜窗口,使用方向键调整位置",
1174
+ onPointerDown: g,
1175
+ onPointerMove: _,
1176
+ onPointerUp: v,
1177
+ onPointerCancel: v,
1178
+ onLostPointerCapture: v,
1179
+ onDoubleClick: () => c((e) => !e),
1180
+ onKeyDown: (e) => {
1181
+ if (s || ![
1182
+ "ArrowLeft",
1183
+ "ArrowRight",
1184
+ "ArrowUp",
1185
+ "ArrowDown"
1186
+ ].includes(e.key)) return;
1187
+ e.preventDefault();
1188
+ let t = e.shiftKey ? 40 : 10;
1189
+ o((n) => Q({
1190
+ ...n,
1191
+ x: n.x + (e.key === "ArrowLeft" ? -t : e.key === "ArrowRight" ? t : 0),
1192
+ y: n.y + (e.key === "ArrowUp" ? -t : e.key === "ArrowDown" ? t : 0)
1193
+ }));
1194
+ },
1195
+ children: [/* @__PURE__ */ W(Be, {}), /* @__PURE__ */ G("div", { children: [/* @__PURE__ */ G("h2", {
1196
+ id: "research-assistant-title",
1197
+ children: ["研发小蜜 ", /* @__PURE__ */ W("span", { children: i ? "开发助手" : "问题反馈" })]
1198
+ }), /* @__PURE__ */ W("p", { children: s ? "详细分析 · 专注每一处线索" : "陪你定位问题,留住每一处线索" })] })]
1199
+ }), /* @__PURE__ */ G("div", {
1200
+ className: "research-assistant__window-actions",
1201
+ children: [/* @__PURE__ */ W(y, {
1202
+ type: "text",
1203
+ "aria-label": s ? "还原小窗" : "放大分析",
1204
+ title: s ? "还原小窗" : "放大分析",
1205
+ onClick: () => c((e) => !e),
1206
+ children: W(s ? pe : N, {})
1207
+ }), /* @__PURE__ */ W(y, {
1208
+ type: "text",
1209
+ "aria-label": "最小化研发小蜜",
1210
+ title: "最小化",
1211
+ onClick: n,
1212
+ children: /* @__PURE__ */ W(I, {})
1213
+ })]
1214
+ })]
1215
+ }),
1216
+ r,
1217
+ /* @__PURE__ */ G("footer", {
1218
+ className: "research-assistant__footer",
1219
+ children: [/* @__PURE__ */ G("span", { children: [/* @__PURE__ */ W("i", {}), "仅在本地内存中记录"] }), /* @__PURE__ */ W("span", { children: s ? "详细分析模式" : "拖动顶部移动 · 右下角调整大小" })]
1220
+ }),
1221
+ !s && /* @__PURE__ */ W("div", {
1222
+ className: "research-assistant__resize",
1223
+ role: "group",
1224
+ tabIndex: 0,
1225
+ "aria-label": "调整小蜜窗口大小,使用方向键",
1226
+ onPointerDown: (e) => g(e, !0),
1227
+ onPointerMove: _,
1228
+ onPointerUp: v,
1229
+ onPointerCancel: v,
1230
+ onLostPointerCapture: v,
1231
+ onKeyDown: (e) => {
1232
+ [
1233
+ "ArrowLeft",
1234
+ "ArrowRight",
1235
+ "ArrowUp",
1236
+ "ArrowDown"
1237
+ ].includes(e.key) && (e.preventDefault(), o((t) => Q({
1238
+ ...t,
1239
+ width: Math.min(t.width + (e.key === "ArrowLeft" ? -20 : e.key === "ArrowRight" ? 20 : 0), window.innerWidth - t.x - Z),
1240
+ height: Math.min(t.height + (e.key === "ArrowUp" ? -20 : e.key === "ArrowDown" ? 20 : 0), window.innerHeight - t.y - Z)
1241
+ })));
1242
+ }
1243
+ })
1244
+ ]
1245
+ });
1246
+ };
1247
+ //#endregion
1248
+ //#region src/PerformancePanel.tsx
1249
+ function Ue({ metrics: e, data: t, now: r, paused: i, offline: a }) {
1250
+ let o = d(e, t, r), s = t.heapSamples, c = s.at(-1), l = Math.min(...s.map((e) => e.used)), u = Math.max(...s.map((e) => e.used)), f = s[0]?.time ?? r, p = s.map((e) => `${4 + (e.time - f) / Math.max(1, (c?.time ?? r) - f) * 592},${76 - (e.used - l) / Math.max(1048576, u - l) * 60}`).join(" ");
1251
+ return /* @__PURE__ */ G("div", {
1252
+ className: "research-assistant__section",
1253
+ children: [
1254
+ /* @__PURE__ */ G("div", {
1255
+ className: "research-assistant__section-heading",
1256
+ children: [
1257
+ /* @__PURE__ */ W("span", {
1258
+ className: "research-assistant__heading-icon",
1259
+ children: /* @__PURE__ */ W(j, {})
1260
+ }),
1261
+ /* @__PURE__ */ G("div", { children: [/* @__PURE__ */ W("h3", { children: "性能诊断" }), /* @__PURE__ */ W("p", { children: "加载体验、主线程卡顿与内存增长风险" })] }),
1262
+ /* @__PURE__ */ W(T, { children: a ? "快照分析" : i ? "采集已暂停" : "自动监测中" })
1263
+ ]
1264
+ }),
1265
+ /* @__PURE__ */ W(_, {
1266
+ showIcon: !0,
1267
+ type: o.length ? "warning" : "info",
1268
+ message: o.length ? `${o.length} 项需要关注` : "当前采样未触发告警",
1269
+ description: a ? "仅分析这份快照中的数据;旧快照可能没有连续内存采样。" : "小蜜收起时也会监测;告警显示在入口,并记录到时间线。未触发规则不代表不存在性能问题。"
1270
+ }),
1271
+ /* @__PURE__ */ W("div", {
1272
+ className: "research-assistant__issue-list",
1273
+ children: o.map((e) => /* @__PURE__ */ G("section", {
1274
+ className: "research-assistant__card research-assistant__performance-issue",
1275
+ children: [
1276
+ /* @__PURE__ */ W("div", {
1277
+ className: "research-assistant__card-heading",
1278
+ children: /* @__PURE__ */ G("h3", { children: [/* @__PURE__ */ W(T, {
1279
+ color: e.severity === "error" ? "red" : "orange",
1280
+ children: e.severity === "error" ? "较高风险" : "需关注"
1281
+ }), e.title] })
1282
+ }),
1283
+ /* @__PURE__ */ W("p", { children: e.evidence }),
1284
+ /* @__PURE__ */ G("details", { children: [
1285
+ /* @__PURE__ */ G("summary", { children: [/* @__PURE__ */ W(D, {}), " 排查建议与触发依据"] }),
1286
+ /* @__PURE__ */ W("ol", { children: e.suggestions.map((e) => /* @__PURE__ */ W("li", { children: e }, e)) }),
1287
+ /* @__PURE__ */ W("p", {
1288
+ className: "research-assistant__hint",
1289
+ children: e.rule
1290
+ }),
1291
+ /* @__PURE__ */ G("a", {
1292
+ href: e.url,
1293
+ target: "_blank",
1294
+ rel: "noreferrer",
1295
+ children: [/* @__PURE__ */ W(F, {}), " 查看官方排查文档"]
1296
+ })
1297
+ ] })
1298
+ ]
1299
+ }, e.id))
1300
+ }),
1301
+ /* @__PURE__ */ G("section", {
1302
+ className: "research-assistant__card",
1303
+ children: [
1304
+ /* @__PURE__ */ G("div", {
1305
+ className: "research-assistant__card-heading",
1306
+ children: [/* @__PURE__ */ W("h3", { children: "JS 堆内存趋势" }), /* @__PURE__ */ W("span", { children: c ? n(c.used) : "暂无采样" })]
1307
+ }),
1308
+ s.length > 1 ? /* @__PURE__ */ G(U, { children: [/* @__PURE__ */ G("svg", {
1309
+ className: "research-assistant__memory-chart",
1310
+ viewBox: "0 0 600 90",
1311
+ role: "img",
1312
+ "aria-label": `内存趋势,最低 ${n(l)},最高 ${n(u)},当前 ${n(c.used)}`,
1313
+ children: [/* @__PURE__ */ W("path", {
1314
+ d: "M4 76 H596",
1315
+ stroke: "currentColor",
1316
+ opacity: ".2"
1317
+ }), /* @__PURE__ */ W("polyline", {
1318
+ points: p,
1319
+ fill: "none",
1320
+ stroke: "currentColor",
1321
+ strokeWidth: "2",
1322
+ vectorEffect: "non-scaling-stroke"
1323
+ })]
1324
+ }), /* @__PURE__ */ G("div", {
1325
+ className: "research-assistant__card-heading",
1326
+ children: [/* @__PURE__ */ G("span", { children: [
1327
+ new Date(f).toLocaleTimeString(),
1328
+ " · ",
1329
+ n(l)
1330
+ ] }), /* @__PURE__ */ G("span", { children: [
1331
+ new Date(c.time).toLocaleTimeString(),
1332
+ " · 峰值 ",
1333
+ n(u)
1334
+ ] })]
1335
+ })] }) : /* @__PURE__ */ W("p", {
1336
+ className: "research-assistant__hint",
1337
+ children: c ? "正在积累连续采样,持续增长风险至少需要观察 60 秒。" : "当前没有可用的连续内存数据。浏览器不支持 performance.memory、采集暂停或旧快照均可能导致此状态。"
1338
+ }),
1339
+ /* @__PURE__ */ W("p", {
1340
+ className: "research-assistant__hint",
1341
+ children: "前台每 5 秒采样,最多保留 15 分钟。JS 堆统计是近似值,不含全部页面内存;增长告警仅提供线索,需通过 DevTools 堆快照确认泄漏。"
1342
+ })
1343
+ ]
1344
+ }),
1345
+ /* @__PURE__ */ G("h3", {
1346
+ className: "research-assistant__subheading",
1347
+ children: [/* @__PURE__ */ W(j, {}), " 页面指标"]
1348
+ }),
1349
+ /* @__PURE__ */ W("div", {
1350
+ className: "research-assistant__metrics",
1351
+ children: [
1352
+ ["TTFB", "首字节"],
1353
+ ["FCP", "首次内容绘制"],
1354
+ ["LCP", "最大内容绘制"],
1355
+ ["CLS", "布局偏移"],
1356
+ ["DOMContentLoaded", "DOM 就绪"],
1357
+ ["load", "页面加载"],
1358
+ ["longTasks", "长任务次数"],
1359
+ ["longTaskDuration", "长任务总耗时"]
1360
+ ].map(([t, n]) => /* @__PURE__ */ G("div", { children: [/* @__PURE__ */ G("span", { children: [
1361
+ n,
1362
+ " · ",
1363
+ t
1364
+ ] }), /* @__PURE__ */ W("strong", { children: e[t] == null ? "不可用" : t === "CLS" ? e[t].toFixed(4) : `${Math.round(e[t])}${t === "longTasks" ? " 次" : " ms"}` })] }, t))
1365
+ }),
1366
+ /* @__PURE__ */ W("p", {
1367
+ className: "research-assistant__hint",
1368
+ children: "LCP / CLS 是当前文档生命周期内的观测,不随业务路由切换重置;浏览器不支持的指标显示“不可用”,尚未采集完整 INP。"
1369
+ })
1370
+ ]
1371
+ });
1372
+ }
1373
+ //#endregion
1374
+ //#region src/ResearchAssistant.tsx
1375
+ function We({ value: e }) {
1376
+ return /* @__PURE__ */ W("pre", {
1377
+ className: "research-assistant__json",
1378
+ children: JSON.stringify(e, null, 2)
1379
+ });
1380
+ }
1381
+ var $ = (e) => new Date(e).toLocaleTimeString("zh-CN", { hour12: !1 });
1382
+ function Ge({ developer: n = !0, bridgeUrl: i = "/__laya-pilot" } = {}) {
1383
+ let s = g(r.subscribe, r.getState), p = g(u.subscribe, u.getState), [S, C] = h(!1), [D, O] = h("feedback"), [ne, k] = h("timeline"), A = m(null), ue = () => {
1384
+ C(!1), requestAnimationFrame(() => A.current?.focus({ preventScroll: !0 }));
1385
+ }, [M, de] = h(""), [pe, N] = h(""), [me, F] = h(null), [I, R] = h("live"), [ye, z] = h(o), xe = m(null);
1386
+ f(() => {
1387
+ if (!S) return;
1388
+ let e = setInterval(() => z(o()), 2e3);
1389
+ return () => clearInterval(e);
1390
+ }, [S]);
1391
+ let B = I === "imported" ? me : I === "frozen" ? s.frozen : null, V = B?.events ?? s.events, H = B?.requests ?? s.requests, we = B?.metrics ?? s.metrics, K = B?.environment ?? ye, q = s.frozen, Te = /* @__PURE__ */ G("div", {
1392
+ className: "research-assistant__section",
1393
+ children: [
1394
+ /* @__PURE__ */ G("div", {
1395
+ className: "research-assistant__intro",
1396
+ children: [
1397
+ /* @__PURE__ */ G("div", {
1398
+ className: "research-assistant__eyebrow",
1399
+ children: [/* @__PURE__ */ W(be, {}), "你的问题排查工作台"]
1400
+ }),
1401
+ /* @__PURE__ */ W("span", { children: "问题现场,一键留存" }),
1402
+ /* @__PURE__ */ W("p", { children: "操作轨迹、接口与异常,在这里串成线索。" })
1403
+ ]
1404
+ }),
1405
+ /* @__PURE__ */ G("div", {
1406
+ className: "research-assistant__stats",
1407
+ children: [
1408
+ /* @__PURE__ */ G("div", { children: [/* @__PURE__ */ G("strong", { children: [V.length, /* @__PURE__ */ W("small", { children: "条" })] }), /* @__PURE__ */ G("span", {
1409
+ className: "research-assistant__stat-label",
1410
+ children: [/* @__PURE__ */ W(P, {}), "操作与事件"]
1411
+ })] }),
1412
+ /* @__PURE__ */ G("div", { children: [/* @__PURE__ */ G("strong", { children: [H.length, /* @__PURE__ */ W("small", { children: "条" })] }), /* @__PURE__ */ G("span", {
1413
+ className: "research-assistant__stat-label",
1414
+ children: [/* @__PURE__ */ W(E, {}), "接口记录"]
1415
+ })] }),
1416
+ /* @__PURE__ */ G("div", {
1417
+ className: H.some((e) => e.failed) ? "has-errors" : "",
1418
+ children: [/* @__PURE__ */ G("strong", { children: [H.filter((e) => e.failed).length, /* @__PURE__ */ W("small", { children: "条" })] }), /* @__PURE__ */ G("span", {
1419
+ className: "research-assistant__stat-label",
1420
+ children: [/* @__PURE__ */ W(Ce, {}), "失败接口"]
1421
+ })]
1422
+ })
1423
+ ]
1424
+ }),
1425
+ /* @__PURE__ */ G("div", {
1426
+ className: "research-assistant__button-row research-assistant__actions",
1427
+ children: [/* @__PURE__ */ W(y, {
1428
+ type: "primary",
1429
+ icon: /* @__PURE__ */ W(te, {}),
1430
+ onClick: () => {
1431
+ l("用户手动截取现场"), R("frozen"), N("已冻结现场。后续操作不会改变这份快照。");
1432
+ },
1433
+ children: "截取当前现场"
1434
+ }), /* @__PURE__ */ W(y, {
1435
+ icon: /* @__PURE__ */ W(oe, {}),
1436
+ onClick: () => {
1437
+ let t = B ?? s.frozen ?? e();
1438
+ a(t, M), N(`已导出 ${$(t.capturedAt)} 的快照,请将 JSON 文件发送给研发人员。`);
1439
+ },
1440
+ children: "导出快照 JSON"
1441
+ })]
1442
+ }),
1443
+ /* @__PURE__ */ W(_, {
1444
+ type: q ? "warning" : "info",
1445
+ showIcon: !0,
1446
+ message: q ? `已保留现场 · ${$(q.capturedAt)} · ${q.reason}` : "异常时自动保留现场,刷新或关闭前请导出。"
1447
+ }),
1448
+ /* @__PURE__ */ G("div", {
1449
+ className: "research-assistant__field",
1450
+ children: [/* @__PURE__ */ G("label", {
1451
+ htmlFor: "research-feedback",
1452
+ children: [/* @__PURE__ */ W(se, {}), "问题描述"]
1453
+ }), /* @__PURE__ */ W(x.TextArea, {
1454
+ id: "research-feedback",
1455
+ value: M,
1456
+ onChange: (e) => de(e.target.value),
1457
+ maxLength: 1e3,
1458
+ showCount: !0,
1459
+ rows: 3,
1460
+ placeholder: "例如:修改表单后点击保存,出现错误提示。"
1461
+ })]
1462
+ }),
1463
+ /* @__PURE__ */ W("p", {
1464
+ className: "research-assistant__hint",
1465
+ children: B ? `当前查看:${B.reason} · ${$(B.capturedAt)}` : q ? "直接导出会使用最近保留的现场。需要最新状态时,请先手动截取。" : "当前没有冻结现场,直接导出会截取此刻状态。"
1466
+ }),
1467
+ /* @__PURE__ */ G("details", { children: [
1468
+ /* @__PURE__ */ W("summary", { children: "快照包含哪些内容" }),
1469
+ /* @__PURE__ */ W("p", { children: "最近 15 分钟内最多 300 条事件、60 条接口记录,页面地址与布局信息、浏览器环境、性能指标及已脱敏的 JSON 正文。页面状态快照不包含屏幕截图或输入框值。" }),
1470
+ /* @__PURE__ */ W("p", { children: "数据只在当前页面内存中保存,不自动上传。字段脱敏无法识别所有业务敏感内容,分享前可先查看导出的 JSON。" })
1471
+ ] })
1472
+ ]
1473
+ }), Ee = /* @__PURE__ */ W(ze, {
1474
+ events: V,
1475
+ onDraft: () => O("automation")
1476
+ }, 0 + I + (B?.capturedAt ?? "live")), De = /* @__PURE__ */ G("div", {
1477
+ className: "research-assistant__section",
1478
+ children: [
1479
+ /* @__PURE__ */ G("div", {
1480
+ className: "research-assistant__section-heading",
1481
+ children: [
1482
+ /* @__PURE__ */ W("span", {
1483
+ className: "research-assistant__heading-icon",
1484
+ children: /* @__PURE__ */ W(E, {})
1485
+ }),
1486
+ /* @__PURE__ */ G("div", { children: [/* @__PURE__ */ W("h3", { children: "接口记录" }), /* @__PURE__ */ W("p", { children: "查看请求状态、耗时与响应详情。" })] }),
1487
+ /* @__PURE__ */ G(T, { children: [H.length, " 条"] })
1488
+ ]
1489
+ }),
1490
+ /* @__PURE__ */ W("p", {
1491
+ className: "research-assistant__hint",
1492
+ children: "Fetch / XHR · 正文最多读取 16 KB · 认证接口不采集正文 · 耗时为响应头到达(Fetch)或请求结束(XHR)"
1493
+ }),
1494
+ !H.length && /* @__PURE__ */ W(b, { description: "暂无接口记录" }),
1495
+ H.slice().sort((e, t) => e.time - t.time).map((e) => /* @__PURE__ */ G("details", {
1496
+ className: "research-assistant__request",
1497
+ children: [/* @__PURE__ */ G("summary", { children: [/* @__PURE__ */ G("span", {
1498
+ className: "research-assistant__request-meta",
1499
+ children: [
1500
+ /* @__PURE__ */ W(T, {
1501
+ color: e.failed ? "red" : "green",
1502
+ children: e.status || "失败"
1503
+ }),
1504
+ /* @__PURE__ */ W("b", { children: e.method }),
1505
+ " ",
1506
+ /* @__PURE__ */ G("span", { children: [
1507
+ /* @__PURE__ */ W(le, {}),
1508
+ Math.round(e.duration),
1509
+ " ms"
1510
+ ] })
1511
+ ]
1512
+ }), /* @__PURE__ */ W("span", {
1513
+ className: "research-assistant__request-url",
1514
+ children: e.url
1515
+ })] }), /* @__PURE__ */ W(We, { value: e })]
1516
+ }, e.id))
1517
+ ]
1518
+ }), J = B ? B.performance ?? t() : s.performance, Oe = B ? Date.parse(B.capturedAt) : s.performanceUpdatedAt, Y = d(we, J, Oe), X = d(s.metrics, s.performance, s.performanceUpdatedAt).length, ke = /* @__PURE__ */ W(Ue, {
1519
+ metrics: we,
1520
+ data: J,
1521
+ now: Oe,
1522
+ paused: s.paused,
1523
+ offline: !!B
1524
+ }), Ae = /* @__PURE__ */ G("div", {
1525
+ className: "research-assistant__section",
1526
+ children: [
1527
+ /* @__PURE__ */ G("div", {
1528
+ className: "research-assistant__section-heading",
1529
+ children: [/* @__PURE__ */ W("span", {
1530
+ className: "research-assistant__heading-icon",
1531
+ children: /* @__PURE__ */ W(ae, {})
1532
+ }), /* @__PURE__ */ G("div", { children: [/* @__PURE__ */ W("h3", { children: "环境与数据" }), /* @__PURE__ */ W("p", { children: "浏览器环境和现场信息,集中查看。" })] })]
1533
+ }),
1534
+ /* @__PURE__ */ W(_, {
1535
+ showIcon: !0,
1536
+ type: "info",
1537
+ message: "只读浏览器信息;不采集 Cookie、localStorage、sessionStorage 的值。"
1538
+ }),
1539
+ /* @__PURE__ */ W(We, { value: K }),
1540
+ B && /* @__PURE__ */ G(U, { children: [/* @__PURE__ */ W("h3", { children: "现场页面状态" }), /* @__PURE__ */ W(We, { value: B.page })] })
1541
+ ]
1542
+ }), je = /* @__PURE__ */ G("div", {
1543
+ className: "research-assistant__section",
1544
+ children: [/* @__PURE__ */ W(b, { description: "知识库问答待接入" }), /* @__PURE__ */ W("p", {
1545
+ className: "research-assistant__hint",
1546
+ children: "已预留问答适配接口。后续可接入使用手册、常见问题和研发文档;当前不会发送问题或快照到任何服务。"
1547
+ })]
1548
+ });
1549
+ return /* @__PURE__ */ G("div", {
1550
+ "data-research-assistant": !0,
1551
+ children: [/* @__PURE__ */ G("div", {
1552
+ className: `research-assistant__dock${p.recording ? " is-recording" : ""}`,
1553
+ hidden: S,
1554
+ children: [/* @__PURE__ */ W(ee, {
1555
+ title: p.recording ? `研发小蜜 · 录制中 ${p.draft.steps.length} 步` : X ? `研发小蜜 · ${X} 项性能告警` : s.frozen ? "研发小蜜 · 已保留异常现场" : "研发小蜜",
1556
+ children: /* @__PURE__ */ W(v, {
1557
+ dot: p.recording || !!s.frozen || X > 0,
1558
+ color: p.recording ? "red" : "orange",
1559
+ offset: [-5, 5],
1560
+ children: /* @__PURE__ */ W(y, {
1561
+ ref: A,
1562
+ type: "primary",
1563
+ shape: "circle",
1564
+ className: "research-assistant__launcher",
1565
+ "aria-label": "打开研发小蜜",
1566
+ "aria-expanded": S,
1567
+ "aria-controls": "research-assistant-window",
1568
+ onClick: () => {
1569
+ C(!0), z(o());
1570
+ },
1571
+ icon: /* @__PURE__ */ W(Be, {})
1572
+ })
1573
+ })
1574
+ }), /* @__PURE__ */ W("span", {
1575
+ className: "research-assistant__sr-only",
1576
+ role: "status",
1577
+ children: p.recording ? `录制中 ${p.draft.steps.length} 步` : X ? `${X} 项性能告警` : s.frozen ? "已保留异常现场" : ""
1578
+ })]
1579
+ }), /* @__PURE__ */ W(He, {
1580
+ open: S,
1581
+ onMinimize: ue,
1582
+ developer: n,
1583
+ children: /* @__PURE__ */ G("div", {
1584
+ "data-research-assistant": !0,
1585
+ className: "research-assistant__body",
1586
+ children: [
1587
+ /* @__PURE__ */ G("div", {
1588
+ className: "research-assistant__toolbar",
1589
+ children: [/* @__PURE__ */ G("span", { children: [/* @__PURE__ */ W("i", { className: s.paused ? "paused" : "" }), B ? "查看离线现场" : s.paused ? "采集已暂停" : "正在采集 · 仅内存"] }), /* @__PURE__ */ G("div", {
1590
+ className: "research-assistant__button-row research-assistant__toolbar-actions",
1591
+ children: [/* @__PURE__ */ W(y, {
1592
+ type: "text",
1593
+ size: "small",
1594
+ icon: s.paused ? /* @__PURE__ */ W(L, {}) : /* @__PURE__ */ W(_e, {}),
1595
+ onClick: () => r.pause(!s.paused),
1596
+ children: s.paused ? "继续采集" : "暂停采集"
1597
+ }), /* @__PURE__ */ W(y, {
1598
+ size: "small",
1599
+ type: "text",
1600
+ icon: /* @__PURE__ */ W(ie, {}),
1601
+ onClick: () => {
1602
+ r.clear(), R("live"), F(null), N("内存记录已清空");
1603
+ },
1604
+ children: "清空记录"
1605
+ })]
1606
+ })]
1607
+ }),
1608
+ pe && /* @__PURE__ */ W(_, {
1609
+ closable: !0,
1610
+ message: pe,
1611
+ type: "info",
1612
+ onClose: () => N("")
1613
+ }),
1614
+ B?.feedback && /* @__PURE__ */ W(_, {
1615
+ message: "快照中的问题描述",
1616
+ description: B.feedback,
1617
+ type: "info"
1618
+ }),
1619
+ n && /* @__PURE__ */ G("div", {
1620
+ className: "research-assistant__sources",
1621
+ children: [
1622
+ /* @__PURE__ */ W(y, {
1623
+ size: "small",
1624
+ icon: /* @__PURE__ */ W(P, {}),
1625
+ type: I === "live" ? "primary" : "default",
1626
+ onClick: () => R("live"),
1627
+ children: "实时记录"
1628
+ }),
1629
+ /* @__PURE__ */ W(y, {
1630
+ size: "small",
1631
+ icon: /* @__PURE__ */ W(te, {}),
1632
+ disabled: !s.frozen,
1633
+ type: I === "frozen" ? "primary" : "default",
1634
+ onClick: () => R("frozen"),
1635
+ children: "已冻结现场"
1636
+ }),
1637
+ /* @__PURE__ */ W(y, {
1638
+ size: "small",
1639
+ icon: /* @__PURE__ */ W(Se, {}),
1640
+ onClick: () => xe.current?.click(),
1641
+ children: "导入 JSON"
1642
+ }),
1643
+ me && /* @__PURE__ */ G(U, { children: [/* @__PURE__ */ W(y, {
1644
+ size: "small",
1645
+ icon: /* @__PURE__ */ W(fe, {}),
1646
+ type: I === "imported" ? "primary" : "default",
1647
+ onClick: () => R("imported"),
1648
+ children: "导入的现场"
1649
+ }), /* @__PURE__ */ W(y, {
1650
+ "aria-label": "关闭导入现场",
1651
+ size: "small",
1652
+ icon: /* @__PURE__ */ W(re, {}),
1653
+ onClick: () => {
1654
+ F(null), R("live");
1655
+ }
1656
+ })] }),
1657
+ /* @__PURE__ */ W("input", {
1658
+ ref: xe,
1659
+ hidden: !0,
1660
+ type: "file",
1661
+ accept: ".json,application/json",
1662
+ onChange: async (e) => {
1663
+ let t = e.target.files?.[0];
1664
+ if (e.target.value = "", t) try {
1665
+ if (t.size > 2 * 1024 * 1024) throw Error("快照不能超过 2 MB");
1666
+ F(c(await t.text())), R("imported"), O("diagnostics"), k("timeline"), N("已载入本地快照,未上传文件");
1667
+ } catch (e) {
1668
+ N(e instanceof Error ? e.message : "快照读取失败");
1669
+ }
1670
+ }
1671
+ })
1672
+ ]
1673
+ }),
1674
+ n && !B && X > 0 && !(D === "diagnostics" && ne === "performance") && /* @__PURE__ */ W(_, {
1675
+ showIcon: !0,
1676
+ type: "warning",
1677
+ message: `${X} 项性能告警`,
1678
+ action: /* @__PURE__ */ W(y, {
1679
+ size: "small",
1680
+ onClick: () => {
1681
+ O("diagnostics"), k("performance");
1682
+ },
1683
+ children: "查看诊断"
1684
+ })
1685
+ }),
1686
+ /* @__PURE__ */ W(w, {
1687
+ className: "research-assistant__main-tabs",
1688
+ activeKey: D,
1689
+ onChange: O,
1690
+ items: [
1691
+ {
1692
+ key: "feedback",
1693
+ label: "问题反馈",
1694
+ icon: /* @__PURE__ */ W(ge, { "aria-hidden": !0 }),
1695
+ children: Te
1696
+ },
1697
+ ...n ? [{
1698
+ key: "automation",
1699
+ label: "自动化测试",
1700
+ icon: /* @__PURE__ */ W(ce, { "aria-hidden": !0 }),
1701
+ children: /* @__PURE__ */ W(Pe, {
1702
+ close: ue,
1703
+ bridgeUrl: i
1704
+ })
1705
+ }, {
1706
+ key: "diagnostics",
1707
+ label: "诊断",
1708
+ icon: /* @__PURE__ */ W(he, { "aria-hidden": !0 }),
1709
+ children: /* @__PURE__ */ W(w, {
1710
+ className: "research-assistant__diagnostic-tabs",
1711
+ activeKey: ne,
1712
+ onChange: k,
1713
+ items: [
1714
+ {
1715
+ key: "timeline",
1716
+ label: "时间线",
1717
+ icon: /* @__PURE__ */ W(P, { "aria-hidden": !0 }),
1718
+ children: Ee
1719
+ },
1720
+ {
1721
+ key: "network",
1722
+ label: "接口",
1723
+ icon: /* @__PURE__ */ W(E, { "aria-hidden": !0 }),
1724
+ children: De
1725
+ },
1726
+ {
1727
+ key: "performance",
1728
+ label: Y.length ? `性能 (${Y.length})` : "性能",
1729
+ icon: /* @__PURE__ */ W(j, { "aria-hidden": !0 }),
1730
+ children: ke
1731
+ },
1732
+ {
1733
+ key: "data",
1734
+ label: "环境与数据",
1735
+ icon: /* @__PURE__ */ W(ae, { "aria-hidden": !0 }),
1736
+ children: Ae
1737
+ }
1738
+ ]
1739
+ })
1740
+ }] : [],
1741
+ {
1742
+ key: "knowledge",
1743
+ label: "知识问答",
1744
+ icon: /* @__PURE__ */ W(ve, { "aria-hidden": !0 }),
1745
+ children: je
1746
+ }
1747
+ ]
1748
+ })
1749
+ ]
1750
+ })
1751
+ })]
1752
+ });
1753
+ }
1754
+ //#endregion
1755
+ export { Ge as t };
1756
+
1757
+ //# sourceMappingURL=ResearchAssistant-DAsgcwcG.js.map