@chromepilot/recording-ui 0.0.0-stage → 0.1.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.
Files changed (78) hide show
  1. package/dist/api.d.ts +47 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/api.js +101 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/api.test.d.ts +2 -0
  6. package/dist/api.test.d.ts.map +1 -0
  7. package/dist/api.test.js +89 -0
  8. package/dist/api.test.js.map +1 -0
  9. package/dist/components/BatchDeleteBar.d.ts +13 -0
  10. package/dist/components/BatchDeleteBar.d.ts.map +1 -0
  11. package/dist/components/BatchDeleteBar.js +13 -0
  12. package/dist/components/BatchDeleteBar.js.map +1 -0
  13. package/dist/components/EventStreamPanel.d.ts +16 -0
  14. package/dist/components/EventStreamPanel.d.ts.map +1 -0
  15. package/dist/components/EventStreamPanel.js +106 -0
  16. package/dist/components/EventStreamPanel.js.map +1 -0
  17. package/dist/components/OtherRecordingTabs.d.ts +19 -0
  18. package/dist/components/OtherRecordingTabs.d.ts.map +1 -0
  19. package/dist/components/OtherRecordingTabs.js +82 -0
  20. package/dist/components/OtherRecordingTabs.js.map +1 -0
  21. package/dist/components/RecordingControls.d.ts +15 -0
  22. package/dist/components/RecordingControls.d.ts.map +1 -0
  23. package/dist/components/RecordingControls.js +100 -0
  24. package/dist/components/RecordingControls.js.map +1 -0
  25. package/dist/components/RecordingDetailBody.d.ts +27 -0
  26. package/dist/components/RecordingDetailBody.d.ts.map +1 -0
  27. package/dist/components/RecordingDetailBody.js +84 -0
  28. package/dist/components/RecordingDetailBody.js.map +1 -0
  29. package/dist/components/RecordingHistoryList.d.ts +28 -0
  30. package/dist/components/RecordingHistoryList.d.ts.map +1 -0
  31. package/dist/components/RecordingHistoryList.js +45 -0
  32. package/dist/components/RecordingHistoryList.js.map +1 -0
  33. package/dist/components/SyncPlayer.d.ts +20 -0
  34. package/dist/components/SyncPlayer.d.ts.map +1 -0
  35. package/dist/components/SyncPlayer.js +84 -0
  36. package/dist/components/SyncPlayer.js.map +1 -0
  37. package/dist/components/TimelineRuler.d.ts +18 -0
  38. package/dist/components/TimelineRuler.d.ts.map +1 -0
  39. package/dist/components/TimelineRuler.js +217 -0
  40. package/dist/components/TimelineRuler.js.map +1 -0
  41. package/dist/format.d.ts +13 -0
  42. package/dist/format.d.ts.map +1 -0
  43. package/dist/format.js +37 -0
  44. package/dist/format.js.map +1 -0
  45. package/dist/format.test.d.ts +2 -0
  46. package/dist/format.test.d.ts.map +1 -0
  47. package/dist/format.test.js +42 -0
  48. package/dist/format.test.js.map +1 -0
  49. package/dist/hooks.d.ts +25 -0
  50. package/dist/hooks.d.ts.map +1 -0
  51. package/dist/hooks.js +76 -0
  52. package/dist/hooks.js.map +1 -0
  53. package/dist/index.d.ts +14 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +14 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/logic.d.ts +5 -0
  58. package/dist/logic.d.ts.map +1 -0
  59. package/dist/logic.js +6 -0
  60. package/dist/logic.js.map +1 -0
  61. package/dist/player.d.ts +3 -0
  62. package/dist/player.d.ts.map +1 -0
  63. package/dist/player.js +4 -0
  64. package/dist/player.js.map +1 -0
  65. package/dist/stack.d.ts +58 -0
  66. package/dist/stack.d.ts.map +1 -0
  67. package/dist/stack.js +86 -0
  68. package/dist/stack.js.map +1 -0
  69. package/dist/stack.test.d.ts +2 -0
  70. package/dist/stack.test.d.ts.map +1 -0
  71. package/dist/stack.test.js +93 -0
  72. package/dist/stack.test.js.map +1 -0
  73. package/dist/types.d.ts +81 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/types.js +2 -0
  76. package/dist/types.js.map +1 -0
  77. package/package.json +58 -3
  78. package/README.md +0 -3
package/dist/api.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * FID-133:chromepilot server 录制 API 客户端。
3
+ * baseUrl 可配:sidepanel 传 `http://localhost:<port>/api`,server web 传 `/api`。
4
+ * 非 2xx 一律抛 RecordingApiError(carry server error code),由调用方映射用户文案。
5
+ */
6
+ import type { ActiveRecordingInfo, BatchDeleteResponse, CpuProfile, RecordingEvents, RecordingListResponse, RecordingMeta } from "./types.js";
7
+ export declare class RecordingApiError extends Error {
8
+ code: string;
9
+ status: number;
10
+ constructor(status: number, code: string, message?: string);
11
+ }
12
+ export interface StartRecordingResult {
13
+ sessionId: string;
14
+ tabId: number | null;
15
+ }
16
+ export declare class RecordingApi {
17
+ private readonly baseUrl;
18
+ constructor(baseUrl: string);
19
+ private request;
20
+ /** 历史录制;urlKey 过滤 + cursor 滚动分页(不传 = 全量,兼容旧调用) */
21
+ list(opts?: {
22
+ urlKey?: string;
23
+ cursor?: string;
24
+ limit?: number;
25
+ }): Promise<RecordingListResponse>;
26
+ /** 活跃录制清单(sidepanel"正在录制"区块数据源) */
27
+ active(): Promise<ActiveRecordingInfo[]>;
28
+ /** 开始录制当前 tab(tabId+url 必传;同 tab 重复 → 409 tab_already_recording) */
29
+ start(input: {
30
+ tabId: number;
31
+ url: string;
32
+ title?: string;
33
+ }): Promise<StartRecordingResult>;
34
+ /** 停止录制;零事件 → 422 recording_no_events(服务端标记 failed) */
35
+ stop(sessionId: string): Promise<{
36
+ sessionId: string;
37
+ }>;
38
+ /** 批量删除;active 项被拒删,由响应分流 */
39
+ batchDelete(ids: string[]): Promise<BatchDeleteResponse>;
40
+ /** 录制事件(rrweb + 网络 + 控制台 + 异常) */
41
+ events(sessionId: string): Promise<RecordingEvents>;
42
+ /** 单条元数据 */
43
+ meta(sessionId: string): Promise<RecordingMeta>;
44
+ /** FID-133 iter6 P3:CPU profile sidecar 懒加载;无 sidecar 返回 null(不抛) */
45
+ cpuProfile(sessionId: string): Promise<CpuProfile | null>;
46
+ }
47
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,qBAAqB,EACrB,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;gBACH,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAM3D;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,MAAM;YAE9B,OAAO;IAuBrB,kDAAkD;IAClD,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC;IASjG,mCAAmC;IAC7B,MAAM,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAK9C,oEAAoE;IACpE,KAAK,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAO3F,uDAAuD;IACvD,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAOvD,6BAA6B;IAC7B,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAOxD,kCAAkC;IAClC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAInD,YAAY;IACZ,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAI/C,qEAAqE;IAC/D,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CAahE"}
package/dist/api.js ADDED
@@ -0,0 +1,101 @@
1
+ export class RecordingApiError extends Error {
2
+ code;
3
+ status;
4
+ constructor(status, code, message) {
5
+ super(message || code);
6
+ this.name = "RecordingApiError";
7
+ this.status = status;
8
+ this.code = code;
9
+ }
10
+ }
11
+ export class RecordingApi {
12
+ baseUrl;
13
+ constructor(baseUrl) {
14
+ this.baseUrl = baseUrl;
15
+ }
16
+ async request(path, init) {
17
+ let res;
18
+ try {
19
+ res = await fetch(`${this.baseUrl}${path}`, {
20
+ headers: { "Content-Type": "application/json" },
21
+ ...init,
22
+ });
23
+ }
24
+ catch {
25
+ throw new RecordingApiError(0, "network_error", "network_error");
26
+ }
27
+ if (!res.ok) {
28
+ let code = `http_${res.status}`;
29
+ try {
30
+ const body = (await res.json());
31
+ if (typeof body.error === "string")
32
+ code = body.error;
33
+ }
34
+ catch {
35
+ // 保留 http_<status> 兜底
36
+ }
37
+ throw new RecordingApiError(res.status, code);
38
+ }
39
+ return (await res.json());
40
+ }
41
+ /** 历史录制;urlKey 过滤 + cursor 滚动分页(不传 = 全量,兼容旧调用) */
42
+ list(opts) {
43
+ const params = new URLSearchParams();
44
+ if (opts?.urlKey)
45
+ params.set("urlKey", opts.urlKey);
46
+ if (opts?.cursor)
47
+ params.set("cursor", opts.cursor);
48
+ if (opts?.limit)
49
+ params.set("limit", String(opts.limit));
50
+ const qs = params.toString();
51
+ return this.request(`/recordings${qs ? `?${qs}` : ""}`);
52
+ }
53
+ /** 活跃录制清单(sidepanel"正在录制"区块数据源) */
54
+ async active() {
55
+ const data = await this.request("/recordings/active");
56
+ return data.recordings ?? [];
57
+ }
58
+ /** 开始录制当前 tab(tabId+url 必传;同 tab 重复 → 409 tab_already_recording) */
59
+ start(input) {
60
+ return this.request("/recordings/start", {
61
+ method: "POST",
62
+ body: JSON.stringify({ tabId: input.tabId, url: input.url, title: input.title ?? "" }),
63
+ });
64
+ }
65
+ /** 停止录制;零事件 → 422 recording_no_events(服务端标记 failed) */
66
+ stop(sessionId) {
67
+ return this.request("/recordings/stop", {
68
+ method: "POST",
69
+ body: JSON.stringify({ sessionId }),
70
+ });
71
+ }
72
+ /** 批量删除;active 项被拒删,由响应分流 */
73
+ batchDelete(ids) {
74
+ return this.request("/recordings/batch-delete", {
75
+ method: "POST",
76
+ body: JSON.stringify({ ids }),
77
+ });
78
+ }
79
+ /** 录制事件(rrweb + 网络 + 控制台 + 异常) */
80
+ events(sessionId) {
81
+ return this.request(`/recordings/${encodeURIComponent(sessionId)}/events`);
82
+ }
83
+ /** 单条元数据 */
84
+ meta(sessionId) {
85
+ return this.request(`/recordings/${encodeURIComponent(sessionId)}`);
86
+ }
87
+ /** FID-133 iter6 P3:CPU profile sidecar 懒加载;无 sidecar 返回 null(不抛) */
88
+ async cpuProfile(sessionId) {
89
+ try {
90
+ const data = await this.request(`/recordings/${encodeURIComponent(sessionId)}/cpu-profile`);
91
+ return data.profile ?? null;
92
+ }
93
+ catch (e) {
94
+ if (e instanceof RecordingApiError && (e.code === "no_cpu_profile" || e.status === 404)) {
95
+ return null;
96
+ }
97
+ throw e;
98
+ }
99
+ }
100
+ }
101
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=api.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.test.d.ts","sourceRoot":"","sources":["../src/api.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { RecordingApi, RecordingApiError } from "./api.js";
3
+ function mockFetchOnce(status, body) {
4
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValueOnce({
5
+ ok: status >= 200 && status < 300,
6
+ status,
7
+ json: async () => body,
8
+ }));
9
+ }
10
+ describe("RecordingApi(FID-133)", () => {
11
+ beforeEach(() => {
12
+ vi.unstubAllGlobals();
13
+ });
14
+ it("list 拼装 urlKey/cursor/limit 查询串", async () => {
15
+ const fetchMock = vi.fn().mockResolvedValue({
16
+ ok: true,
17
+ status: 200,
18
+ json: async () => ({ recordings: [], nextCursor: null }),
19
+ });
20
+ vi.stubGlobal("fetch", fetchMock);
21
+ const api = new RecordingApi("http://localhost:34568/api");
22
+ await api.list({ urlKey: "https://a.com/x", cursor: "abc_1", limit: 20 });
23
+ const url = fetchMock.mock.calls[0][0];
24
+ expect(url).toContain("/recordings?");
25
+ expect(url).toContain("urlKey=https%3A%2F%2Fa.com%2Fx");
26
+ expect(url).toContain("cursor=abc_1");
27
+ expect(url).toContain("limit=20");
28
+ });
29
+ it("active 解包 recordings 数组", async () => {
30
+ mockFetchOnce(200, { recordings: [{ sessionId: "rec-1", tabId: 7 }] });
31
+ const api = new RecordingApi("/api");
32
+ const list = await api.active();
33
+ expect(list).toHaveLength(1);
34
+ expect(list[0]).toMatchObject({ sessionId: "rec-1", tabId: 7 });
35
+ });
36
+ it("start 透传 tabId/url/title", async () => {
37
+ const fetchMock = vi.fn().mockResolvedValue({
38
+ ok: true,
39
+ status: 200,
40
+ json: async () => ({ sessionId: "rec-new", tabId: 7 }),
41
+ });
42
+ vi.stubGlobal("fetch", fetchMock);
43
+ const api = new RecordingApi("/api");
44
+ const res = await api.start({ tabId: 7, url: "https://a.com/x", title: "页面A" });
45
+ expect(res.sessionId).toBe("rec-new");
46
+ const init = fetchMock.mock.calls[0][1];
47
+ expect(JSON.parse(init.body)).toMatchObject({ tabId: 7, url: "https://a.com/x", title: "页面A" });
48
+ });
49
+ it("非 2xx 抛 RecordingApiError 并携带 server error code", async () => {
50
+ mockFetchOnce(409, { error: "tab_already_recording", sessionId: "rec-x" });
51
+ const api = new RecordingApi("/api");
52
+ await expect(api.start({ tabId: 7, url: "https://a.com" })).rejects.toMatchObject({
53
+ name: "RecordingApiError",
54
+ status: 409,
55
+ code: "tab_already_recording",
56
+ });
57
+ });
58
+ it("网络失败抛 network_error", async () => {
59
+ vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("conn refused")));
60
+ const api = new RecordingApi("/api");
61
+ await expect(api.active()).rejects.toMatchObject({ code: "network_error" });
62
+ });
63
+ it("非 JSON 错误体回退 http_<status>", async () => {
64
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
65
+ ok: false,
66
+ status: 503,
67
+ json: async () => { throw new Error("not json"); },
68
+ }));
69
+ const api = new RecordingApi("/api");
70
+ try {
71
+ await api.active();
72
+ expect.unreachable();
73
+ }
74
+ catch (e) {
75
+ expect(e).toBeInstanceOf(RecordingApiError);
76
+ expect(e.code).toBe("http_503");
77
+ }
78
+ });
79
+ it("batchDelete 透传 ids", async () => {
80
+ mockFetchOnce(200, { ok: true, deleted: ["a"], missing: [], active: [], deletedCount: 1 });
81
+ const fetchMock = vi.mocked(fetch);
82
+ const api = new RecordingApi("/api");
83
+ const res = await api.batchDelete(["a", "b"]);
84
+ expect(res.deleted).toEqual(["a"]);
85
+ const init = fetchMock.mock.calls[0][1];
86
+ expect(JSON.parse(init.body)).toEqual({ ids: ["a", "b"] });
87
+ });
88
+ });
89
+ //# sourceMappingURL=api.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.test.js","sourceRoot":"","sources":["../src/api.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * FID-133:批量删除内联确认条(方案检裁决:窄面板不用弹窗,内联常驻可取消)。
3
+ */
4
+ export interface BatchDeleteBarProps {
5
+ count: number;
6
+ busy?: boolean;
7
+ /** 额外不可删数(选中项里含录制中,已由服务端拒绝) */
8
+ skippedActive?: number;
9
+ onCancel: () => void;
10
+ onConfirm: () => void;
11
+ }
12
+ export declare function BatchDeleteBar({ count, busy, skippedActive, onCancel, onConfirm }: BatchDeleteBarProps): import("react").JSX.Element;
13
+ //# sourceMappingURL=BatchDeleteBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BatchDeleteBar.d.ts","sourceRoot":"","sources":["../../src/components/BatchDeleteBar.tsx"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+BAA+B;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,IAAY,EAAE,aAAiB,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,mBAAmB,+BAiClH"}
@@ -0,0 +1,13 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ export function BatchDeleteBar({ count, busy = false, skippedActive = 0, onCancel, onConfirm }) {
3
+ return (_jsxs("div", { style: {
4
+ display: "flex",
5
+ alignItems: "center",
6
+ gap: 8,
7
+ padding: "6px 8px",
8
+ borderRadius: 6,
9
+ background: "#fff7ed",
10
+ border: "1px solid #fed7aa",
11
+ }, children: [_jsxs("span", { style: { flex: 1, fontSize: 11, color: "#9a3412", minWidth: 0 }, children: ["\u5DF2\u9009 ", count, " \u6761\u5F55\u5236\uFF0C\u5220\u9664\u540E\u65E0\u6CD5\u6062\u590D", skippedActive > 0 ? `(其中 ${skippedActive} 条录制中,将被跳过)` : ""] }), _jsx("button", { onClick: onCancel, disabled: busy, style: { flexShrink: 0, fontSize: 11, padding: "3px 8px", borderRadius: 4, border: "1px solid #d1d5db", background: "#fff", color: "#374151", cursor: "pointer" }, children: "\u53D6\u6D88" }), _jsx("button", { onClick: onConfirm, disabled: busy, style: { flexShrink: 0, fontSize: 11, padding: "3px 8px", borderRadius: 4, border: "none", background: busy ? "#fca5a5" : "#ea580c", color: "#fff", cursor: busy ? "wait" : "pointer" }, children: busy ? "删除中…" : "确认删除" })] }));
12
+ }
13
+ //# sourceMappingURL=BatchDeleteBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BatchDeleteBar.js","sourceRoot":"","sources":["../../src/components/BatchDeleteBar.tsx"],"names":[],"mappings":";AAYA,MAAM,UAAU,cAAc,CAAC,EAAE,KAAK,EAAE,IAAI,GAAG,KAAK,EAAE,aAAa,GAAG,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAuB;IACjH,OAAO,CACL,eACE,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,UAAU,EAAE,QAAQ;YACpB,GAAG,EAAE,CAAC;YACN,OAAO,EAAE,SAAS;YAClB,YAAY,EAAE,CAAC;YACf,UAAU,EAAE,SAAS;YACrB,MAAM,EAAE,mBAAmB;SAC5B,aAED,gBAAM,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE,8BAC/D,KAAK,yEACR,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,aAAa,aAAa,CAAC,CAAC,CAAC,EAAE,IACtD,EACP,iBACE,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,IAAI,EACd,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,6BAG1J,EACT,iBACE,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,IAAI,EACd,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,YAEtL,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,GAChB,IACL,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { TimelineEventItem } from "../types.js";
2
+ export type EventStreamTab = "network" | "console" | "exceptions";
3
+ export interface EventStreamPanelProps {
4
+ network: TimelineEventItem[];
5
+ console: TimelineEventItem[];
6
+ exceptions: TimelineEventItem[];
7
+ /** 首个 rrweb 事件 timestamp(偏移基准);null 时联动禁用 */
8
+ baseTimestamp: number | null;
9
+ /** 播放器当前偏移 ms(调用方轮询 getCurrentTime 传入) */
10
+ currentOffsetMs: number | null;
11
+ /** 点击事件行 → seek 播放器 */
12
+ onSeek: (offsetMs: number) => void;
13
+ height?: number;
14
+ }
15
+ export declare function EventStreamPanel({ network, console: consoleEvents, exceptions, baseTimestamp, currentOffsetMs, onSeek, height, }: EventStreamPanelProps): import("react").JSX.Element;
16
+ //# sourceMappingURL=EventStreamPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventStreamPanel.d.ts","sourceRoot":"","sources":["../../src/components/EventStreamPanel.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC;AAElE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,6CAA6C;IAC7C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0CAA0C;IAC1C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,uBAAuB;IACvB,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAoCD,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,OAAO,EAAE,aAAa,EACtB,UAAU,EACV,aAAa,EACb,eAAe,EACf,MAAM,EACN,MAAY,GACb,EAAE,qBAAqB,+BAiHvB"}
@@ -0,0 +1,106 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * FID-133:事件流面板——网络请求 / 控制台 / 异常(JS 栈)三页签,
4
+ * 与回放播放器时间轴联动:播放进度高亮"当前事件"(最后一个已越过的),
5
+ * 点击事件行 seek 播放器到对应时间点。
6
+ *
7
+ * 时间口径:事件 timestamp 是 epoch ms;播放器 getCurrentTime 是回放偏移 ms。
8
+ * 换算基准 = 首个 rrweb 事件 timestamp(调用方传入 baseTimestamp)。
9
+ */
10
+ import { useEffect, useMemo, useRef, useState } from "react";
11
+ function formatClock(ts) {
12
+ const d = new Date(ts);
13
+ const pad = (n, l = 2) => n.toString().padStart(l, "0");
14
+ return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`;
15
+ }
16
+ function rowText(kind, ev) {
17
+ const p = ev.payload;
18
+ if (kind === "network") {
19
+ const status = p.status;
20
+ return {
21
+ main: `${p.method || "GET"} ${p.url || ""}`,
22
+ sub: p.phase === "request" ? "请求发出" : p.phase === "response" ? "响应返回" : undefined,
23
+ badge: status != null ? String(status) : p.phase,
24
+ badgeColor: status != null ? (status >= 400 ? "#ef4444" : status >= 300 ? "#f59e0b" : "#22c55e") : "#9ca3af",
25
+ };
26
+ }
27
+ if (kind === "console") {
28
+ const level = p.level || "log";
29
+ return {
30
+ main: p.message || (p.args ?? []).map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" "),
31
+ sub: p.stack ? p.stack.split("\n").slice(0, 2).join(" ") : undefined,
32
+ badge: level,
33
+ badgeColor: level === "error" ? "#ef4444" : level === "warn" ? "#f59e0b" : "#3b82f6",
34
+ };
35
+ }
36
+ return {
37
+ main: p.message || "(未知异常)",
38
+ sub: p.stack ? p.stack.split("\n").slice(0, 2).join(" ") : undefined,
39
+ badge: "异常",
40
+ badgeColor: "#ef4444",
41
+ };
42
+ }
43
+ export function EventStreamPanel({ network, console: consoleEvents, exceptions, baseTimestamp, currentOffsetMs, onSeek, height = 260, }) {
44
+ const [tab, setTab] = useState("network");
45
+ const listRef = useRef(null);
46
+ const tabs = [
47
+ { key: "network", label: "网络请求", items: network },
48
+ { key: "console", label: "控制台", items: consoleEvents },
49
+ { key: "exceptions", label: "异常", items: exceptions },
50
+ ];
51
+ const active = tabs.find((t) => t.key === tab);
52
+ // 当前高亮:偏移 <= 当前播放偏移的最后一个事件
53
+ const highlightedIdx = useMemo(() => {
54
+ if (baseTimestamp == null || currentOffsetMs == null)
55
+ return -1;
56
+ const nowTs = baseTimestamp + currentOffsetMs;
57
+ let best = -1;
58
+ for (let i = 0; i < active.items.length; i++) {
59
+ if (active.items[i].timestamp <= nowTs)
60
+ best = i;
61
+ else
62
+ break;
63
+ }
64
+ return best;
65
+ }, [active.items, baseTimestamp, currentOffsetMs]);
66
+ // 高亮行自动滚入可视区
67
+ useEffect(() => {
68
+ if (highlightedIdx < 0 || !listRef.current)
69
+ return;
70
+ const el = listRef.current.children[highlightedIdx];
71
+ el?.scrollIntoView?.({ block: "nearest" });
72
+ }, [highlightedIdx]);
73
+ return (_jsxs("div", { style: { border: "1px solid #e5e7eb", borderRadius: 6, background: "#fff" }, children: [_jsx("div", { style: { display: "flex", gap: 4, padding: "6px 8px 0", borderBottom: "1px solid #f3f4f6" }, children: tabs.map((t) => (_jsxs("button", { onClick: () => setTab(t.key), style: {
74
+ fontSize: 12,
75
+ padding: "4px 8px",
76
+ border: "none",
77
+ borderBottom: tab === t.key ? "2px solid #2563eb" : "2px solid transparent",
78
+ background: "transparent",
79
+ color: tab === t.key ? "#2563eb" : "#6b7280",
80
+ cursor: "pointer",
81
+ fontWeight: tab === t.key ? 600 : 400,
82
+ }, children: [t.label, " (", t.items.length, ")"] }, t.key))) }), _jsx("div", { ref: listRef, style: { height, overflowY: "auto", padding: "4px 0" }, children: active.items.length === 0 ? (_jsxs("div", { style: { padding: "20px 0", textAlign: "center", fontSize: 12, color: "#9ca3af" }, children: ["\u672C\u6B21\u5F55\u5236\u6CA1\u6709\u6355\u6349\u5230", active.label] })) : (active.items.map((ev, idx) => {
83
+ const { main, sub, badge, badgeColor } = rowText(tab, ev);
84
+ const highlighted = idx === highlightedIdx;
85
+ const offsetMs = baseTimestamp != null ? ev.timestamp - baseTimestamp : null;
86
+ return (_jsxs("div", { onClick: () => offsetMs != null && offsetMs >= 0 && onSeek(offsetMs), title: offsetMs != null ? "点击跳转到此时间点" : undefined, style: {
87
+ display: "flex",
88
+ alignItems: "flex-start",
89
+ gap: 8,
90
+ padding: "4px 10px",
91
+ fontSize: 12,
92
+ background: highlighted ? "#dbeafe" : idx % 2 === 0 ? "#fafafa" : "#fff",
93
+ cursor: offsetMs != null ? "pointer" : "default",
94
+ borderLeft: highlighted ? "3px solid #2563eb" : "3px solid transparent",
95
+ }, children: [_jsx("span", { style: { fontFamily: "monospace", fontSize: 11, color: "#9ca3af", flexShrink: 0 }, children: formatClock(ev.timestamp) }), _jsx("span", { style: {
96
+ fontSize: 10,
97
+ padding: "1px 5px",
98
+ borderRadius: 4,
99
+ color: "#fff",
100
+ background: badgeColor ?? "#9ca3af",
101
+ flexShrink: 0,
102
+ marginTop: 1,
103
+ }, children: badge ?? "-" }), _jsxs("span", { style: { flex: 1, minWidth: 0 }, children: [_jsx("span", { style: { display: "block", color: "#111827", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontFamily: tab === "console" || tab === "exceptions" ? "monospace" : undefined }, children: main }), sub && (_jsx("span", { style: { display: "block", color: "#9ca3af", fontSize: 10, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontFamily: "monospace" }, children: sub }))] })] }, `${ev.timestamp}-${idx}`));
104
+ })) })] }));
105
+ }
106
+ //# sourceMappingURL=EventStreamPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventStreamPanel.js","sourceRoot":"","sources":["../../src/components/EventStreamPanel.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ActiveRecordingInfo } from "../types.js";
2
+ export interface RecordingTabInfo {
3
+ title?: string;
4
+ url?: string;
5
+ groupColor?: string;
6
+ groupTitle?: string;
7
+ }
8
+ export interface OtherRecordingTabsProps {
9
+ recordings: ActiveRecordingInfo[];
10
+ currentTabId: number;
11
+ /** tabId → 面板 Tabs 列表里的展示信息(标题/组色),缺省退化 URL 显示 */
12
+ tabInfo?: Map<number, RecordingTabInfo>;
13
+ /** 点击「跳转」→ 激活对应 tab(chrome.tabs 由消费方实现) */
14
+ onJump?: (tabId: number) => void;
15
+ /** 两步确认后的真正停止;不传则不渲染停止按钮 */
16
+ onStop?: (sessionId: string) => void;
17
+ }
18
+ export declare function OtherRecordingTabs({ recordings, currentTabId, tabInfo, onJump, onStop }: OtherRecordingTabsProps): import("react").JSX.Element | null;
19
+ //# sourceMappingURL=OtherRecordingTabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtherRecordingTabs.d.ts","sourceRoot":"","sources":["../../src/components/OtherRecordingTabs.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAIvD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAyBD,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACxC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,4BAA4B;IAC5B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAiCD,wBAAgB,kBAAkB,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,uBAAuB,sCA2DhH"}
@@ -0,0 +1,82 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * FID-133 iter6:"其他标签页也在录制"区块可辨识化改造(用户裁定 P1)。
4
+ * 同标题 tab 无法区分 → 每条必须显示 tabId 徽标 + 完整 title;
5
+ * 提供「跳转」聚焦对应 tab、「停止」两步内联确认(3s 自动复位)。
6
+ * onJump/onStop 为可选回调:不传则对应按钮不渲染(只读消费方如 hmh 向后兼容)。
7
+ */
8
+ import { useEffect, useState } from "react";
9
+ import { useNow } from "../hooks.js";
10
+ import { formatDuration } from "../format.js";
11
+ const COLOR_MAP = {
12
+ grey: "#9ca3af",
13
+ blue: "#60a5fa",
14
+ red: "#f87171",
15
+ yellow: "#facc15",
16
+ green: "#4ade80",
17
+ pink: "#f472b6",
18
+ purple: "#c084fc",
19
+ cyan: "#22d3ee",
20
+ orange: "#fb923c",
21
+ };
22
+ const BTN_STYLE = {
23
+ fontSize: 10,
24
+ padding: "1px 6px",
25
+ borderRadius: 4,
26
+ border: "1px solid #d1d5db",
27
+ background: "#fff",
28
+ color: "#374151",
29
+ cursor: "pointer",
30
+ flexShrink: 0,
31
+ };
32
+ /** 两步确认按钮:首次点击变「确认?」,3s 内再点才真正执行,超时自动复位 */
33
+ function ConfirmStopButton({ onConfirm }) {
34
+ const [arming, setArming] = useState(false);
35
+ useEffect(() => {
36
+ if (!arming)
37
+ return undefined;
38
+ const t = setTimeout(() => setArming(false), 3000);
39
+ return () => clearTimeout(t);
40
+ }, [arming]);
41
+ return (_jsx("button", { onClick: (e) => {
42
+ e.stopPropagation();
43
+ if (arming) {
44
+ setArming(false);
45
+ onConfirm();
46
+ }
47
+ else {
48
+ setArming(true);
49
+ }
50
+ }, style: {
51
+ ...BTN_STYLE,
52
+ ...(arming
53
+ ? { background: "#fef2f2", borderColor: "#ef4444", color: "#b91c1c", fontWeight: 600 }
54
+ : {}),
55
+ }, children: arming ? "确认停止?" : "停止" }));
56
+ }
57
+ export function OtherRecordingTabs({ recordings, currentTabId, tabInfo, onJump, onStop }) {
58
+ const now = useNow(1000);
59
+ const others = recordings.filter((r) => r.tabId !== currentTabId);
60
+ if (others.length === 0)
61
+ return null;
62
+ return (_jsxs("div", { style: { borderRadius: 6, border: "1px solid #fde68a", background: "#fffbeb", padding: "6px 8px" }, children: [_jsxs("div", { style: { fontSize: 11, fontWeight: 600, color: "#92400e", marginBottom: 4 }, children: ["\u5176\u4ED6\u6807\u7B7E\u9875\u4E5F\u5728\u5F55\u5236 (", others.length, ")"] }), _jsx("ul", { style: { margin: 0, padding: 0, listStyle: "none" }, children: others.map((r) => {
63
+ const info = r.tabId != null ? tabInfo?.get(r.tabId) : undefined;
64
+ const label = info?.title || r.title || r.url || "未知页面";
65
+ const color = COLOR_MAP[info?.groupColor ?? ""] ?? "#9ca3af";
66
+ const elapsed = formatDuration(Math.max(0, now - r.startTime));
67
+ const jumpable = r.tabId != null && onJump;
68
+ return (_jsxs("li", { style: { display: "flex", alignItems: "center", gap: 6, padding: "3px 0", minWidth: 0 }, children: [_jsx("span", { style: { width: 7, height: 7, borderRadius: "50%", background: color, flexShrink: 0 } }), _jsxs("span", { style: {
69
+ fontSize: 10,
70
+ fontFamily: "monospace",
71
+ padding: "0 4px",
72
+ borderRadius: 3,
73
+ background: "#fef3c7",
74
+ color: "#92400e",
75
+ flexShrink: 0,
76
+ }, title: "\u6807\u7B7E\u9875 ID", children: ["tab ", r.tabId ?? "?"] }), _jsx("span", { style: { fontSize: 11, color: "#374151", flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: label, children: label }), _jsxs("span", { style: { fontSize: 10, color: "#b45309", flexShrink: 0 }, children: ["\u5DF2\u5F55 ", elapsed] }), jumpable && (_jsx("button", { onClick: (e) => {
77
+ e.stopPropagation();
78
+ onJump(r.tabId);
79
+ }, style: BTN_STYLE, children: "\u8DF3\u8F6C" })), onStop && _jsx(ConfirmStopButton, { onConfirm: () => onStop(r.sessionId) })] }, r.sessionId));
80
+ }) })] }));
81
+ }
82
+ //# sourceMappingURL=OtherRecordingTabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtherRecordingTabs.js","sourceRoot":"","sources":["../../src/components/OtherRecordingTabs.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { RecordingApi } from "../api.js";
2
+ import type { ActiveRecordingInfo } from "../types.js";
3
+ /** server 错误码 → 用户文案(方案检文案终稿) */
4
+ export declare function recordingErrorText(e: unknown): string;
5
+ export interface RecordingControlsProps {
6
+ api: RecordingApi;
7
+ tabId: number;
8
+ tabUrl: string;
9
+ tabTitle: string;
10
+ /** server 活跃录制清单(含其他 agent 的);本 tab 那条即"正在录制"状态 */
11
+ activeRecordings: ActiveRecordingInfo[];
12
+ onChanged?: () => void;
13
+ }
14
+ export declare function RecordingControls({ api, tabId, tabUrl, tabTitle, activeRecordings, onChanged, }: RecordingControlsProps): import("react").JSX.Element;
15
+ //# sourceMappingURL=RecordingControls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecordingControls.d.ts","sourceRoot":"","sources":["../../src/components/RecordingControls.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAIvD,iCAAiC;AACjC,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAiBrD;AAED,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,YAAY,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,GAAG,EACH,KAAK,EACL,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,SAAS,GACV,EAAE,sBAAsB,+BAsGxB"}