opendevbrowser 0.0.16 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -27
- package/dist/browser/annotation-manager.d.ts +3 -0
- package/dist/browser/annotation-manager.d.ts.map +1 -1
- package/dist/browser/browser-manager.d.ts +6 -1
- package/dist/browser/browser-manager.d.ts.map +1 -1
- package/dist/browser/canvas-client.d.ts +53 -0
- package/dist/browser/canvas-client.d.ts.map +1 -0
- package/dist/browser/canvas-code-sync-manager.d.ts +79 -0
- package/dist/browser/canvas-code-sync-manager.d.ts.map +1 -0
- package/dist/browser/canvas-manager.d.ts +94 -0
- package/dist/browser/canvas-manager.d.ts.map +1 -0
- package/dist/browser/canvas-runtime-preview-bridge.d.ts +20 -0
- package/dist/browser/canvas-runtime-preview-bridge.d.ts.map +1 -0
- package/dist/browser/canvas-session-sync-manager.d.ts +21 -0
- package/dist/browser/canvas-session-sync-manager.d.ts.map +1 -0
- package/dist/browser/manager-types.d.ts +13 -1
- package/dist/browser/manager-types.d.ts.map +1 -1
- package/dist/browser/ops-browser-manager.d.ts +11 -1
- package/dist/browser/ops-browser-manager.d.ts.map +1 -1
- package/dist/canvas/code-sync/apply-tsx.d.ts +23 -0
- package/dist/canvas/code-sync/apply-tsx.d.ts.map +1 -0
- package/dist/canvas/code-sync/graph.d.ts +5 -0
- package/dist/canvas/code-sync/graph.d.ts.map +1 -0
- package/dist/canvas/code-sync/hash.d.ts +3 -0
- package/dist/canvas/code-sync/hash.d.ts.map +1 -0
- package/dist/canvas/code-sync/import.d.ts +18 -0
- package/dist/canvas/code-sync/import.d.ts.map +1 -0
- package/dist/canvas/code-sync/manifest.d.ts +5 -0
- package/dist/canvas/code-sync/manifest.d.ts.map +1 -0
- package/dist/canvas/code-sync/tsx-adapter.d.ts +8 -0
- package/dist/canvas/code-sync/tsx-adapter.d.ts.map +1 -0
- package/dist/canvas/code-sync/types.d.ts +152 -0
- package/dist/canvas/code-sync/types.d.ts.map +1 -0
- package/dist/canvas/code-sync/write.d.ts +9 -0
- package/dist/canvas/code-sync/write.d.ts.map +1 -0
- package/dist/canvas/document-store.d.ts +81 -0
- package/dist/canvas/document-store.d.ts.map +1 -0
- package/dist/canvas/export.d.ts +12 -0
- package/dist/canvas/export.d.ts.map +1 -0
- package/dist/canvas/repo-store.d.ts +10 -0
- package/dist/canvas/repo-store.d.ts.map +1 -0
- package/dist/canvas/surface-palette.d.ts +15 -0
- package/dist/canvas/surface-palette.d.ts.map +1 -0
- package/dist/canvas/types.d.ts +255 -0
- package/dist/canvas/types.d.ts.map +1 -0
- package/dist/canvas-runtime-preview-bridge-HBEHXM4T.js +7 -0
- package/dist/canvas-runtime-preview-bridge-HBEHXM4T.js.map +1 -0
- package/dist/{chunk-ST7CO5FA.js → chunk-5J3IFL3X.js} +11577 -13539
- package/dist/chunk-5J3IFL3X.js.map +1 -0
- package/dist/chunk-D633UO34.js +8149 -0
- package/dist/chunk-D633UO34.js.map +1 -0
- package/dist/{chunk-7W3SPXIB.js → chunk-FUSXMW3G.js} +4 -1
- package/dist/chunk-TBUCZX4A.js +34 -0
- package/dist/chunk-TBUCZX4A.js.map +1 -0
- package/dist/chunk-V7KUDHDG.js +276 -0
- package/dist/chunk-V7KUDHDG.js.map +1 -0
- package/dist/chunk-Y2KL55OG.js +59 -0
- package/dist/chunk-Y2KL55OG.js.map +1 -0
- package/dist/cli/args.d.ts +3 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/commands/annotate.d.ts +11 -0
- package/dist/cli/commands/annotate.d.ts.map +1 -1
- package/dist/cli/commands/canvas.d.ts +45 -0
- package/dist/cli/commands/canvas.d.ts.map +1 -0
- package/dist/cli/commands/devtools/perf.d.ts.map +1 -1
- package/dist/cli/commands/devtools/screenshot.d.ts +1 -0
- package/dist/cli/commands/devtools/screenshot.d.ts.map +1 -1
- package/dist/cli/commands/dom/attr.d.ts.map +1 -1
- package/dist/cli/commands/dom/checked.d.ts.map +1 -1
- package/dist/cli/commands/dom/enabled.d.ts.map +1 -1
- package/dist/cli/commands/dom/html.d.ts.map +1 -1
- package/dist/cli/commands/dom/text.d.ts.map +1 -1
- package/dist/cli/commands/dom/value.d.ts.map +1 -1
- package/dist/cli/commands/dom/visible.d.ts.map +1 -1
- package/dist/cli/commands/export/clone-component.d.ts +9 -0
- package/dist/cli/commands/export/clone-component.d.ts.map +1 -1
- package/dist/cli/commands/export/clone-page.d.ts +8 -0
- package/dist/cli/commands/export/clone-page.d.ts.map +1 -1
- package/dist/cli/commands/interact/check.d.ts.map +1 -1
- package/dist/cli/commands/interact/click.d.ts.map +1 -1
- package/dist/cli/commands/interact/hover.d.ts.map +1 -1
- package/dist/cli/commands/interact/press.d.ts.map +1 -1
- package/dist/cli/commands/interact/scroll-into-view.d.ts.map +1 -1
- package/dist/cli/commands/interact/scroll.d.ts.map +1 -1
- package/dist/cli/commands/interact/select.d.ts.map +1 -1
- package/dist/cli/commands/interact/type.d.ts.map +1 -1
- package/dist/cli/commands/interact/uncheck.d.ts.map +1 -1
- package/dist/cli/commands/native.d.ts +12 -1
- package/dist/cli/commands/native.d.ts.map +1 -1
- package/dist/cli/commands/nav/goto.d.ts.map +1 -1
- package/dist/cli/commands/nav/snapshot.d.ts.map +1 -1
- package/dist/cli/commands/nav/wait.d.ts.map +1 -1
- package/dist/cli/commands/serve.d.ts +5 -0
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/session/connect.d.ts.map +1 -1
- package/dist/cli/commands/status.d.ts +5 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/daemon-commands.d.ts.map +1 -1
- package/dist/cli/help.d.ts +5 -0
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/index.js +724 -163
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/remote-canvas-manager.d.ts +8 -0
- package/dist/cli/remote-canvas-manager.d.ts.map +1 -0
- package/dist/cli/remote-manager.d.ts +3 -1
- package/dist/cli/remote-manager.d.ts.map +1 -1
- package/dist/cli/remote-relay.d.ts +2 -0
- package/dist/cli/remote-relay.d.ts.map +1 -1
- package/dist/cli/utils/parse.d.ts +1 -0
- package/dist/cli/utils/parse.d.ts.map +1 -1
- package/dist/core/bootstrap.d.ts.map +1 -1
- package/dist/core/types.d.ts +2 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/fs-UMRKOBNN.js +7 -0
- package/dist/fs-UMRKOBNN.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +192 -67
- package/dist/index.js.map +1 -1
- package/dist/{macros-NUBRM44Y.js → macros-ND2M7LWU.js} +2 -2
- package/dist/opendevbrowser.d.ts.map +1 -1
- package/dist/opendevbrowser.js +192 -67
- package/dist/opendevbrowser.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/shopping/index.d.ts.map +1 -1
- package/dist/providers-G3LRHQXX.js +121 -0
- package/dist/providers-G3LRHQXX.js.map +1 -0
- package/dist/relay/protocol.d.ts +85 -3
- package/dist/relay/protocol.d.ts.map +1 -1
- package/dist/relay/relay-server.d.ts +14 -1
- package/dist/relay/relay-server.d.ts.map +1 -1
- package/dist/relay/relay-types.d.ts +3 -0
- package/dist/relay/relay-types.d.ts.map +1 -1
- package/dist/runtime-factory-BICHDPE7.js +13 -0
- package/dist/runtime-factory-BICHDPE7.js.map +1 -0
- package/dist/tools/annotate.d.ts.map +1 -1
- package/dist/tools/canvas.d.ts +4 -0
- package/dist/tools/canvas.d.ts.map +1 -0
- package/dist/tools/check.d.ts.map +1 -1
- package/dist/tools/click.d.ts.map +1 -1
- package/dist/tools/clone_component.d.ts.map +1 -1
- package/dist/tools/clone_page.d.ts.map +1 -1
- package/dist/tools/connect.d.ts.map +1 -1
- package/dist/tools/deps.d.ts +2 -0
- package/dist/tools/deps.d.ts.map +1 -1
- package/dist/tools/dom_get_html.d.ts.map +1 -1
- package/dist/tools/dom_get_text.d.ts.map +1 -1
- package/dist/tools/get_attr.d.ts.map +1 -1
- package/dist/tools/get_value.d.ts.map +1 -1
- package/dist/tools/goto.d.ts.map +1 -1
- package/dist/tools/hover.d.ts.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/is_checked.d.ts.map +1 -1
- package/dist/tools/is_enabled.d.ts.map +1 -1
- package/dist/tools/is_visible.d.ts.map +1 -1
- package/dist/tools/launch.d.ts.map +1 -1
- package/dist/tools/macro_resolve.d.ts.map +1 -1
- package/dist/tools/perf.d.ts.map +1 -1
- package/dist/tools/press.d.ts.map +1 -1
- package/dist/tools/product_video_run.d.ts.map +1 -1
- package/dist/tools/research_run.d.ts.map +1 -1
- package/dist/tools/response.d.ts +4 -1
- package/dist/tools/response.d.ts.map +1 -1
- package/dist/tools/screenshot.d.ts.map +1 -1
- package/dist/tools/scroll.d.ts.map +1 -1
- package/dist/tools/scroll_into_view.d.ts.map +1 -1
- package/dist/tools/select.d.ts.map +1 -1
- package/dist/tools/shopping_run.d.ts.map +1 -1
- package/dist/tools/snapshot.d.ts.map +1 -1
- package/dist/tools/type.d.ts.map +1 -1
- package/dist/tools/uncheck.d.ts.map +1 -1
- package/dist/tools/wait.d.ts.map +1 -1
- package/dist/tools/workflow-runtime.d.ts +1 -2
- package/dist/tools/workflow-runtime.d.ts.map +1 -1
- package/extension/canvas.html +636 -0
- package/extension/dist/annotate-content.css +15 -6
- package/extension/dist/annotate-content.js +119 -9
- package/extension/dist/annotation-payload.js +163 -0
- package/extension/dist/background.js +148 -18
- package/extension/dist/canvas/canvas-runtime.js +1061 -0
- package/extension/dist/canvas/model.js +213 -0
- package/extension/dist/canvas/viewport-fit.js +67 -0
- package/extension/dist/canvas-page.js +1801 -0
- package/extension/dist/ops/dom-bridge.js +116 -3
- package/extension/dist/ops/ops-runtime.js +508 -44
- package/extension/dist/ops/ops-session-store.js +21 -114
- package/extension/dist/ops/target-session-coordinator.js +157 -0
- package/extension/dist/popup.js +155 -31
- package/extension/dist/services/ConnectionManager.js +17 -0
- package/extension/dist/services/RelayClient.js +9 -0
- package/extension/dist/services/TabManager.js +35 -12
- package/extension/dist/types.js +2 -0
- package/extension/manifest.json +1 -1
- package/extension/popup.html +52 -0
- package/package.json +6 -4
- package/skills/AGENTS.md +5 -2
- package/skills/opendevbrowser-best-practices/SKILL.md +71 -3
- package/skills/opendevbrowser-best-practices/artifacts/canvas-governance-playbook.md +141 -0
- package/skills/opendevbrowser-best-practices/artifacts/command-channel-reference.md +113 -17
- package/skills/opendevbrowser-best-practices/assets/templates/canvas-blocker-checklist.json +70 -0
- package/skills/opendevbrowser-best-practices/assets/templates/canvas-feedback-eval.json +73 -0
- package/skills/opendevbrowser-best-practices/assets/templates/canvas-generation-plan.v1.json +67 -0
- package/skills/opendevbrowser-best-practices/assets/templates/canvas-handshake-example.json +126 -0
- package/skills/opendevbrowser-best-practices/assets/templates/robustness-checklist.json +57 -0
- package/skills/opendevbrowser-best-practices/assets/templates/surface-audit-checklist.json +7 -3
- package/skills/opendevbrowser-best-practices/scripts/odb-workflow.sh +26 -0
- package/skills/opendevbrowser-best-practices/scripts/run-robustness-audit.sh +82 -1
- package/skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh +225 -84
- package/dist/chunk-ST7CO5FA.js.map +0 -1
- /package/dist/{chunk-7W3SPXIB.js.map → chunk-FUSXMW3G.js.map} +0 -0
- /package/dist/{macros-NUBRM44Y.js.map → macros-ND2M7LWU.js.map} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_OPS_PARALLELISM_POLICY, createOpsGovernorState } from "./parallelism-governor.js";
|
|
2
|
+
import { createCoordinatorId, TargetSessionCoordinator } from "./target-session-coordinator.js";
|
|
2
3
|
export class OpsRefStore {
|
|
3
4
|
refsByTarget = new Map();
|
|
4
5
|
snapshotByTarget = new Map();
|
|
@@ -7,7 +8,7 @@ export class OpsRefStore {
|
|
|
7
8
|
for (const entry of entries) {
|
|
8
9
|
map.set(entry.ref, entry);
|
|
9
10
|
}
|
|
10
|
-
const snapshotId =
|
|
11
|
+
const snapshotId = createCoordinatorId();
|
|
11
12
|
this.refsByTarget.set(targetId, map);
|
|
12
13
|
this.snapshotByTarget.set(targetId, snapshotId);
|
|
13
14
|
return { snapshotId, targetId, count: entries.length };
|
|
@@ -31,32 +32,12 @@ export class OpsRefStore {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
export class OpsSessionStore {
|
|
34
|
-
|
|
35
|
-
tabToSession = new Map();
|
|
35
|
+
coordinator = new TargetSessionCoordinator();
|
|
36
36
|
createSession(ownerClientId, tabId, leaseId, info, options) {
|
|
37
|
-
const id = createId();
|
|
38
|
-
const targetId = `tab-${tabId}`;
|
|
39
37
|
const parallelismPolicy = options?.parallelismPolicy ?? DEFAULT_OPS_PARALLELISM_POLICY;
|
|
40
|
-
|
|
41
|
-
targetId,
|
|
42
|
-
tabId,
|
|
43
|
-
url: info?.url,
|
|
44
|
-
title: info?.title
|
|
45
|
-
};
|
|
46
|
-
const session = {
|
|
47
|
-
id,
|
|
48
|
-
ownerClientId,
|
|
49
|
-
leaseId,
|
|
50
|
-
state: "active",
|
|
51
|
-
tabId,
|
|
52
|
-
targetId,
|
|
53
|
-
activeTargetId: targetId,
|
|
54
|
-
createdAt: Date.now(),
|
|
55
|
-
lastUsedAt: Date.now(),
|
|
56
|
-
targets: new Map([[targetId, target]]),
|
|
57
|
-
nameToTarget: new Map(),
|
|
58
|
-
targetToName: new Map(),
|
|
38
|
+
return this.coordinator.createSession(ownerClientId, tabId, leaseId, info, {
|
|
59
39
|
refStore: new OpsRefStore(),
|
|
40
|
+
syntheticTargets: new Map(),
|
|
60
41
|
consoleEvents: [],
|
|
61
42
|
networkEvents: [],
|
|
62
43
|
networkRequests: new Map(),
|
|
@@ -72,132 +53,58 @@ export class OpsSessionStore {
|
|
|
72
53
|
frozenSignals: 0,
|
|
73
54
|
parallelismPolicy,
|
|
74
55
|
parallelismState: createOpsGovernorState(parallelismPolicy, "extensionOpsHeaded")
|
|
75
|
-
};
|
|
76
|
-
this.sessions.set(id, session);
|
|
77
|
-
this.tabToSession.set(tabId, id);
|
|
78
|
-
return session;
|
|
56
|
+
});
|
|
79
57
|
}
|
|
80
58
|
get(sessionId) {
|
|
81
|
-
return this.
|
|
59
|
+
return this.coordinator.get(sessionId);
|
|
82
60
|
}
|
|
83
61
|
getByTabId(tabId) {
|
|
84
|
-
|
|
85
|
-
if (!id)
|
|
86
|
-
return null;
|
|
87
|
-
return this.sessions.get(id) ?? null;
|
|
62
|
+
return this.coordinator.getByTabId(tabId);
|
|
88
63
|
}
|
|
89
64
|
listOwnedBy(clientId) {
|
|
90
|
-
return
|
|
65
|
+
return this.coordinator.listOwnedBy(clientId);
|
|
91
66
|
}
|
|
92
67
|
delete(sessionId) {
|
|
93
|
-
|
|
94
|
-
if (!session)
|
|
95
|
-
return null;
|
|
96
|
-
this.sessions.delete(sessionId);
|
|
97
|
-
for (const target of session.targets.values()) {
|
|
98
|
-
this.tabToSession.delete(target.tabId);
|
|
99
|
-
}
|
|
100
|
-
return session;
|
|
68
|
+
return this.coordinator.delete(sessionId);
|
|
101
69
|
}
|
|
102
70
|
addTarget(sessionId, tabId, info) {
|
|
103
|
-
|
|
104
|
-
const targetId = `tab-${tabId}`;
|
|
105
|
-
const target = {
|
|
106
|
-
targetId,
|
|
107
|
-
tabId,
|
|
108
|
-
url: info?.url,
|
|
109
|
-
title: info?.title
|
|
110
|
-
};
|
|
111
|
-
session.targets.set(targetId, target);
|
|
112
|
-
this.tabToSession.set(tabId, sessionId);
|
|
113
|
-
if (!session.activeTargetId) {
|
|
114
|
-
session.activeTargetId = targetId;
|
|
115
|
-
}
|
|
116
|
-
return target;
|
|
71
|
+
return this.coordinator.addTarget(sessionId, tabId, info);
|
|
117
72
|
}
|
|
118
73
|
removeTarget(sessionId, targetId) {
|
|
74
|
+
const target = this.coordinator.removeTarget(sessionId, targetId);
|
|
119
75
|
const session = this.requireSession(sessionId);
|
|
120
|
-
const target = session.targets.get(targetId) ?? null;
|
|
121
76
|
if (!target)
|
|
122
77
|
return null;
|
|
123
|
-
session.targets.delete(targetId);
|
|
124
|
-
this.tabToSession.delete(target.tabId);
|
|
125
|
-
const name = session.targetToName.get(targetId);
|
|
126
|
-
if (name) {
|
|
127
|
-
session.targetToName.delete(targetId);
|
|
128
|
-
session.nameToTarget.delete(name);
|
|
129
|
-
}
|
|
130
|
-
if (session.activeTargetId === targetId) {
|
|
131
|
-
const [first] = session.targets.keys();
|
|
132
|
-
session.activeTargetId = first ?? "";
|
|
133
|
-
}
|
|
134
78
|
session.targetQueues.delete(targetId);
|
|
135
79
|
session.targetQueueDepth.delete(targetId);
|
|
136
80
|
session.targetQueueOldestAt.delete(targetId);
|
|
137
81
|
session.refStore.clearTarget(targetId);
|
|
82
|
+
session.syntheticTargets.delete(targetId);
|
|
138
83
|
return target;
|
|
139
84
|
}
|
|
140
85
|
getTargetIdByTabId(sessionId, tabId) {
|
|
141
|
-
|
|
142
|
-
for (const target of session.targets.values()) {
|
|
143
|
-
if (target.tabId === tabId) {
|
|
144
|
-
return target.targetId;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return null;
|
|
86
|
+
return this.coordinator.getTargetIdByTabId(sessionId, tabId);
|
|
148
87
|
}
|
|
149
88
|
removeTargetByTabId(sessionId, tabId) {
|
|
150
|
-
const targetId = this.getTargetIdByTabId(sessionId, tabId);
|
|
89
|
+
const targetId = this.coordinator.getTargetIdByTabId(sessionId, tabId);
|
|
151
90
|
if (!targetId)
|
|
152
91
|
return null;
|
|
153
92
|
return this.removeTarget(sessionId, targetId);
|
|
154
93
|
}
|
|
155
94
|
setActiveTarget(sessionId, targetId) {
|
|
156
|
-
|
|
157
|
-
if (!session.targets.has(targetId)) {
|
|
158
|
-
throw new Error(`Unknown targetId: ${targetId}`);
|
|
159
|
-
}
|
|
160
|
-
session.activeTargetId = targetId;
|
|
95
|
+
this.coordinator.setActiveTarget(sessionId, targetId);
|
|
161
96
|
}
|
|
162
97
|
setName(sessionId, targetId, name) {
|
|
163
|
-
|
|
164
|
-
const trimmed = name.trim();
|
|
165
|
-
if (!trimmed) {
|
|
166
|
-
throw new Error("Name must be non-empty");
|
|
167
|
-
}
|
|
168
|
-
if (!session.targets.has(targetId)) {
|
|
169
|
-
throw new Error(`Unknown targetId: ${targetId}`);
|
|
170
|
-
}
|
|
171
|
-
const existing = session.nameToTarget.get(trimmed);
|
|
172
|
-
if (existing && existing !== targetId) {
|
|
173
|
-
throw new Error(`Name already in use: ${trimmed}`);
|
|
174
|
-
}
|
|
175
|
-
const previousName = session.targetToName.get(targetId);
|
|
176
|
-
if (previousName && previousName !== trimmed) {
|
|
177
|
-
session.nameToTarget.delete(previousName);
|
|
178
|
-
}
|
|
179
|
-
session.nameToTarget.set(trimmed, targetId);
|
|
180
|
-
session.targetToName.set(targetId, trimmed);
|
|
98
|
+
this.coordinator.setName(sessionId, targetId, name);
|
|
181
99
|
}
|
|
182
100
|
getTargetIdByName(sessionId, name) {
|
|
183
|
-
|
|
184
|
-
return session.nameToTarget.get(name.trim()) ?? null;
|
|
101
|
+
return this.coordinator.getTargetIdByName(sessionId, name);
|
|
185
102
|
}
|
|
186
103
|
listNamedTargets(sessionId) {
|
|
187
|
-
|
|
188
|
-
return Array.from(session.nameToTarget.entries()).map(([name, targetId]) => ({ name, targetId }));
|
|
104
|
+
return this.coordinator.listNamedTargets(sessionId);
|
|
189
105
|
}
|
|
190
106
|
requireSession(sessionId) {
|
|
191
|
-
|
|
192
|
-
if (!session) {
|
|
193
|
-
throw new Error(`Unknown sessionId: ${sessionId}`);
|
|
194
|
-
}
|
|
195
|
-
return session;
|
|
107
|
+
return this.coordinator.requireSession(sessionId);
|
|
196
108
|
}
|
|
197
109
|
}
|
|
198
|
-
const
|
|
199
|
-
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
200
|
-
return crypto.randomUUID();
|
|
201
|
-
}
|
|
202
|
-
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
203
|
-
};
|
|
110
|
+
export const createOpsSessionId = () => createCoordinatorId();
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
export class TargetSessionCoordinator {
|
|
2
|
+
sessions = new Map();
|
|
3
|
+
tabToSession = new Map();
|
|
4
|
+
createSession(ownerClientId, tabId, leaseId, info, extra, sessionId) {
|
|
5
|
+
const id = sessionId ?? createCoordinatorId();
|
|
6
|
+
const targetId = `tab-${tabId}`;
|
|
7
|
+
const target = {
|
|
8
|
+
targetId,
|
|
9
|
+
tabId,
|
|
10
|
+
url: info?.url,
|
|
11
|
+
title: info?.title
|
|
12
|
+
};
|
|
13
|
+
const createdAt = Date.now();
|
|
14
|
+
const session = {
|
|
15
|
+
id,
|
|
16
|
+
ownerClientId,
|
|
17
|
+
leaseId,
|
|
18
|
+
state: "active",
|
|
19
|
+
tabId,
|
|
20
|
+
targetId,
|
|
21
|
+
activeTargetId: targetId,
|
|
22
|
+
createdAt,
|
|
23
|
+
lastUsedAt: createdAt,
|
|
24
|
+
targets: new Map([[targetId, target]]),
|
|
25
|
+
nameToTarget: new Map(),
|
|
26
|
+
targetToName: new Map(),
|
|
27
|
+
...extra
|
|
28
|
+
};
|
|
29
|
+
this.sessions.set(id, session);
|
|
30
|
+
this.tabToSession.set(tabId, id);
|
|
31
|
+
return session;
|
|
32
|
+
}
|
|
33
|
+
get(sessionId) {
|
|
34
|
+
return this.sessions.get(sessionId) ?? null;
|
|
35
|
+
}
|
|
36
|
+
getByTabId(tabId) {
|
|
37
|
+
const sessionId = this.tabToSession.get(tabId);
|
|
38
|
+
if (!sessionId) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return this.sessions.get(sessionId) ?? null;
|
|
42
|
+
}
|
|
43
|
+
listOwnedBy(clientId) {
|
|
44
|
+
return Array.from(this.sessions.values()).filter((session) => session.ownerClientId === clientId);
|
|
45
|
+
}
|
|
46
|
+
delete(sessionId) {
|
|
47
|
+
const session = this.sessions.get(sessionId) ?? null;
|
|
48
|
+
if (!session) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
this.sessions.delete(sessionId);
|
|
52
|
+
for (const target of session.targets.values()) {
|
|
53
|
+
this.tabToSession.delete(target.tabId);
|
|
54
|
+
}
|
|
55
|
+
return session;
|
|
56
|
+
}
|
|
57
|
+
addTarget(sessionId, tabId, info) {
|
|
58
|
+
const session = this.requireSession(sessionId);
|
|
59
|
+
const targetId = `tab-${tabId}`;
|
|
60
|
+
const target = {
|
|
61
|
+
targetId,
|
|
62
|
+
tabId,
|
|
63
|
+
url: info?.url,
|
|
64
|
+
title: info?.title
|
|
65
|
+
};
|
|
66
|
+
session.targets.set(targetId, target);
|
|
67
|
+
this.tabToSession.set(tabId, sessionId);
|
|
68
|
+
if (!session.activeTargetId) {
|
|
69
|
+
session.activeTargetId = targetId;
|
|
70
|
+
}
|
|
71
|
+
return target;
|
|
72
|
+
}
|
|
73
|
+
removeTarget(sessionId, targetId) {
|
|
74
|
+
const session = this.requireSession(sessionId);
|
|
75
|
+
const target = session.targets.get(targetId) ?? null;
|
|
76
|
+
if (!target) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
session.targets.delete(targetId);
|
|
80
|
+
this.tabToSession.delete(target.tabId);
|
|
81
|
+
const name = session.targetToName.get(targetId);
|
|
82
|
+
if (name) {
|
|
83
|
+
session.targetToName.delete(targetId);
|
|
84
|
+
session.nameToTarget.delete(name);
|
|
85
|
+
}
|
|
86
|
+
if (session.activeTargetId === targetId) {
|
|
87
|
+
const [first] = session.targets.keys();
|
|
88
|
+
session.activeTargetId = first ?? "";
|
|
89
|
+
}
|
|
90
|
+
return target;
|
|
91
|
+
}
|
|
92
|
+
getTargetIdByTabId(sessionId, tabId) {
|
|
93
|
+
const session = this.requireSession(sessionId);
|
|
94
|
+
for (const target of session.targets.values()) {
|
|
95
|
+
if (target.tabId === tabId) {
|
|
96
|
+
return target.targetId;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
removeTargetByTabId(sessionId, tabId) {
|
|
102
|
+
const targetId = this.getTargetIdByTabId(sessionId, tabId);
|
|
103
|
+
if (!targetId) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return this.removeTarget(sessionId, targetId);
|
|
107
|
+
}
|
|
108
|
+
setActiveTarget(sessionId, targetId) {
|
|
109
|
+
const session = this.requireSession(sessionId);
|
|
110
|
+
if (!session.targets.has(targetId)) {
|
|
111
|
+
throw new Error(`Unknown targetId: ${targetId}`);
|
|
112
|
+
}
|
|
113
|
+
session.activeTargetId = targetId;
|
|
114
|
+
}
|
|
115
|
+
setName(sessionId, targetId, name) {
|
|
116
|
+
const session = this.requireSession(sessionId);
|
|
117
|
+
const trimmed = name.trim();
|
|
118
|
+
if (!trimmed) {
|
|
119
|
+
throw new Error("Name must be non-empty");
|
|
120
|
+
}
|
|
121
|
+
if (!session.targets.has(targetId)) {
|
|
122
|
+
throw new Error(`Unknown targetId: ${targetId}`);
|
|
123
|
+
}
|
|
124
|
+
const existing = session.nameToTarget.get(trimmed);
|
|
125
|
+
if (existing && existing !== targetId) {
|
|
126
|
+
throw new Error(`Name already in use: ${trimmed}`);
|
|
127
|
+
}
|
|
128
|
+
const previousName = session.targetToName.get(targetId);
|
|
129
|
+
if (previousName && previousName !== trimmed) {
|
|
130
|
+
session.nameToTarget.delete(previousName);
|
|
131
|
+
}
|
|
132
|
+
session.nameToTarget.set(trimmed, targetId);
|
|
133
|
+
session.targetToName.set(targetId, trimmed);
|
|
134
|
+
}
|
|
135
|
+
getTargetIdByName(sessionId, name) {
|
|
136
|
+
const session = this.requireSession(sessionId);
|
|
137
|
+
return session.nameToTarget.get(name.trim()) ?? null;
|
|
138
|
+
}
|
|
139
|
+
listNamedTargets(sessionId) {
|
|
140
|
+
const session = this.requireSession(sessionId);
|
|
141
|
+
return Array.from(session.nameToTarget.entries()).map(([name, targetId]) => ({ name, targetId }));
|
|
142
|
+
}
|
|
143
|
+
requireSession(sessionId) {
|
|
144
|
+
const session = this.sessions.get(sessionId);
|
|
145
|
+
if (!session) {
|
|
146
|
+
throw new Error(`Unknown sessionId: ${sessionId}`);
|
|
147
|
+
}
|
|
148
|
+
session.lastUsedAt = Date.now();
|
|
149
|
+
return session;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export const createCoordinatorId = () => {
|
|
153
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
154
|
+
return crypto.randomUUID();
|
|
155
|
+
}
|
|
156
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
157
|
+
};
|
package/extension/dist/popup.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_AUTO_CONNECT, DEFAULT_AUTO_PAIR, DEFAULT_DISCOVERY_PORT, DEFAULT_NATIVE_ENABLED, DEFAULT_PAIRING_ENABLED, DEFAULT_PAIRING_TOKEN, DEFAULT_RELAY_PORT } from "./relay-settings.js";
|
|
2
2
|
import { logError } from "./logging.js";
|
|
3
|
+
import { describeAnnotationItem, filterAnnotationPayload } from "./annotation-payload.js";
|
|
3
4
|
const statusEl = document.getElementById("status");
|
|
4
5
|
const statusIndicator = document.getElementById("statusIndicator");
|
|
5
6
|
const statusPill = document.getElementById("statusPill");
|
|
@@ -22,6 +23,9 @@ const nativeEnabledInput = document.getElementById("nativeEnabled");
|
|
|
22
23
|
const annotationContextInput = document.getElementById("annotationContext");
|
|
23
24
|
const annotationStartButton = document.getElementById("annotationStart");
|
|
24
25
|
const annotationCopyButton = document.getElementById("annotationCopy");
|
|
26
|
+
const annotationSendButton = document.getElementById("annotationSend");
|
|
27
|
+
const annotationRefreshButton = document.getElementById("annotationRefresh");
|
|
28
|
+
const annotationItems = document.getElementById("annotationItems");
|
|
25
29
|
const annotationNote = document.getElementById("annotationNote");
|
|
26
30
|
if (!statusEl
|
|
27
31
|
|| !statusIndicator
|
|
@@ -45,12 +49,16 @@ if (!statusEl
|
|
|
45
49
|
|| !annotationContextInput
|
|
46
50
|
|| !annotationStartButton
|
|
47
51
|
|| !annotationCopyButton
|
|
52
|
+
|| !annotationSendButton
|
|
53
|
+
|| !annotationRefreshButton
|
|
54
|
+
|| !annotationItems
|
|
48
55
|
|| !annotationNote) {
|
|
49
56
|
throw new Error("Popup DOM missing required elements");
|
|
50
57
|
}
|
|
51
58
|
const defaultNote = "Local relay only. Tokens stay on-device.";
|
|
52
59
|
const defaultAnnotationNote = "No annotations captured yet.";
|
|
53
60
|
const LAST_ANNOTATION_META_KEY = "annotationLastMeta";
|
|
61
|
+
let lastAnnotationPayload = null;
|
|
54
62
|
const setNote = (message) => {
|
|
55
63
|
const next = message && message.trim() ? message : defaultNote;
|
|
56
64
|
statusNote.textContent = next;
|
|
@@ -207,13 +215,24 @@ const refreshStatus = async () => {
|
|
|
207
215
|
setInjectionStatus(null);
|
|
208
216
|
}
|
|
209
217
|
};
|
|
210
|
-
const
|
|
218
|
+
const setAnnotationActionsEnabled = (enabled) => {
|
|
211
219
|
annotationCopyButton.disabled = !enabled;
|
|
220
|
+
annotationSendButton.disabled = !enabled;
|
|
212
221
|
};
|
|
213
222
|
const buildPopupAnnotationOptions = () => {
|
|
214
223
|
const context = annotationContextInput.value.trim();
|
|
215
224
|
return context ? { context } : undefined;
|
|
216
225
|
};
|
|
226
|
+
const withPopupContext = (payload) => {
|
|
227
|
+
const context = annotationContextInput.value.trim();
|
|
228
|
+
if (!context) {
|
|
229
|
+
return payload;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
...payload,
|
|
233
|
+
context
|
|
234
|
+
};
|
|
235
|
+
};
|
|
217
236
|
const fetchTokenFromPlugin = async (port) => {
|
|
218
237
|
try {
|
|
219
238
|
const response = await fetch(`http://127.0.0.1:${port}/pair`, {
|
|
@@ -332,16 +351,76 @@ const formatAnnotationSummary = (meta) => {
|
|
|
332
351
|
const target = meta.url ? ` on ${meta.url}` : "";
|
|
333
352
|
return `Last annotation: ${count} item${count === 1 ? "" : "s"}${target}.`;
|
|
334
353
|
};
|
|
354
|
+
const renderAnnotationItems = (payload) => {
|
|
355
|
+
annotationItems.innerHTML = "";
|
|
356
|
+
if (!payload || payload.annotations.length === 0) {
|
|
357
|
+
const empty = document.createElement("div");
|
|
358
|
+
empty.className = "annotation-empty";
|
|
359
|
+
empty.textContent = "No annotation items available.";
|
|
360
|
+
annotationItems.append(empty);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
for (const item of payload.annotations) {
|
|
364
|
+
const row = document.createElement("div");
|
|
365
|
+
row.className = "annotation-item";
|
|
366
|
+
const summary = document.createElement("div");
|
|
367
|
+
summary.className = "annotation-item-summary";
|
|
368
|
+
summary.textContent = describeAnnotationItem(item);
|
|
369
|
+
const meta = document.createElement("div");
|
|
370
|
+
meta.className = "annotation-item-meta";
|
|
371
|
+
meta.textContent = `${item.tag} • ${Math.round(item.rect.width)}×${Math.round(item.rect.height)}`;
|
|
372
|
+
const actions = document.createElement("div");
|
|
373
|
+
actions.className = "annotation-item-actions";
|
|
374
|
+
const copyButton = document.createElement("button");
|
|
375
|
+
copyButton.className = "secondary";
|
|
376
|
+
copyButton.type = "button";
|
|
377
|
+
copyButton.textContent = "Copy item";
|
|
378
|
+
copyButton.addEventListener("click", () => {
|
|
379
|
+
void copySelectedAnnotation([item.id], "Copied annotation item payload.");
|
|
380
|
+
});
|
|
381
|
+
const sendButton = document.createElement("button");
|
|
382
|
+
sendButton.className = "secondary";
|
|
383
|
+
sendButton.type = "button";
|
|
384
|
+
sendButton.textContent = "Send item";
|
|
385
|
+
sendButton.addEventListener("click", () => {
|
|
386
|
+
void sendSelectedAnnotation([item.id], "popup_item", describeAnnotationItem(item), "Sent annotation item to agent inbox.");
|
|
387
|
+
});
|
|
388
|
+
actions.append(copyButton, sendButton);
|
|
389
|
+
row.append(summary, meta, actions);
|
|
390
|
+
annotationItems.append(row);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
const loadAnnotationPayload = async (preferScreenshots) => {
|
|
394
|
+
let response = await sendMessage({
|
|
395
|
+
type: "annotation:getPayload",
|
|
396
|
+
includeScreenshots: preferScreenshots
|
|
397
|
+
});
|
|
398
|
+
if (!response.payload && preferScreenshots) {
|
|
399
|
+
response = await sendMessage({
|
|
400
|
+
type: "annotation:getPayload",
|
|
401
|
+
includeScreenshots: false
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
return response;
|
|
405
|
+
};
|
|
406
|
+
const refreshAnnotationPayload = async () => {
|
|
407
|
+
const response = await loadAnnotationPayload(false);
|
|
408
|
+
lastAnnotationPayload = response.payload ? withPopupContext(response.payload) : null;
|
|
409
|
+
renderAnnotationItems(lastAnnotationPayload);
|
|
410
|
+
};
|
|
335
411
|
const refreshLastAnnotationMeta = async () => {
|
|
336
412
|
try {
|
|
337
413
|
const response = await sendMessage({ type: "annotation:lastMeta" });
|
|
338
414
|
const meta = response.meta;
|
|
339
415
|
if (meta && meta.status === "ok") {
|
|
340
|
-
|
|
416
|
+
setAnnotationActionsEnabled(true);
|
|
341
417
|
setAnnotationNote(formatAnnotationSummary(meta));
|
|
418
|
+
await refreshAnnotationPayload();
|
|
342
419
|
return;
|
|
343
420
|
}
|
|
344
|
-
|
|
421
|
+
setAnnotationActionsEnabled(false);
|
|
422
|
+
lastAnnotationPayload = null;
|
|
423
|
+
renderAnnotationItems(null);
|
|
345
424
|
if (meta) {
|
|
346
425
|
setAnnotationNote(formatAnnotationSummary(meta));
|
|
347
426
|
}
|
|
@@ -351,7 +430,9 @@ const refreshLastAnnotationMeta = async () => {
|
|
|
351
430
|
}
|
|
352
431
|
catch (error) {
|
|
353
432
|
logError("popup.annotation_meta", error, { code: "annotation_meta_failed" });
|
|
354
|
-
|
|
433
|
+
setAnnotationActionsEnabled(false);
|
|
434
|
+
lastAnnotationPayload = null;
|
|
435
|
+
renderAnnotationItems(null);
|
|
355
436
|
setAnnotationNote("Annotation status unavailable.");
|
|
356
437
|
}
|
|
357
438
|
};
|
|
@@ -377,6 +458,49 @@ const copyTextToClipboard = async (text) => {
|
|
|
377
458
|
throw new Error("Clipboard copy failed");
|
|
378
459
|
}
|
|
379
460
|
};
|
|
461
|
+
const copyPayloadToClipboard = async (payload, message) => {
|
|
462
|
+
await copyTextToClipboard(JSON.stringify(payload, null, 2));
|
|
463
|
+
setAnnotationNote(message);
|
|
464
|
+
};
|
|
465
|
+
const sendPayloadToAgent = async (payload, source, label, successMessage) => {
|
|
466
|
+
const response = await sendMessage({
|
|
467
|
+
type: "annotation:sendPayload",
|
|
468
|
+
payload,
|
|
469
|
+
source,
|
|
470
|
+
label
|
|
471
|
+
});
|
|
472
|
+
if (!response.ok) {
|
|
473
|
+
throw new Error(response.error?.message ?? "Agent dispatch failed.");
|
|
474
|
+
}
|
|
475
|
+
setAnnotationNote(successMessage);
|
|
476
|
+
};
|
|
477
|
+
const copySelectedAnnotation = async (annotationIds, successMessage = "Copied annotation payload to clipboard.") => {
|
|
478
|
+
const response = await loadAnnotationPayload(true);
|
|
479
|
+
if (!response.payload) {
|
|
480
|
+
setAnnotationActionsEnabled(false);
|
|
481
|
+
setAnnotationNote("No completed annotation payload available.");
|
|
482
|
+
renderAnnotationItems(null);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const payload = withPopupContext(annotationIds && annotationIds.length > 0
|
|
486
|
+
? filterAnnotationPayload(response.payload, annotationIds, { includeScreenshots: Boolean(response.payload.screenshots?.length) })
|
|
487
|
+
: response.payload);
|
|
488
|
+
await copyPayloadToClipboard(payload, response.warning ? `${successMessage} ${response.warning}` : successMessage);
|
|
489
|
+
await refreshLastAnnotationMeta();
|
|
490
|
+
};
|
|
491
|
+
const sendSelectedAnnotation = async (annotationIds, source, label, successMessage) => {
|
|
492
|
+
const response = await loadAnnotationPayload(true);
|
|
493
|
+
if (!response.payload) {
|
|
494
|
+
setAnnotationActionsEnabled(false);
|
|
495
|
+
setAnnotationNote("No completed annotation payload available.");
|
|
496
|
+
renderAnnotationItems(null);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const payload = withPopupContext(annotationIds && annotationIds.length > 0
|
|
500
|
+
? filterAnnotationPayload(response.payload, annotationIds, { includeScreenshots: Boolean(response.payload.screenshots?.length) })
|
|
501
|
+
: response.payload);
|
|
502
|
+
await sendPayloadToAgent(payload, source, label, successMessage);
|
|
503
|
+
};
|
|
380
504
|
const toggle = async () => {
|
|
381
505
|
const isConnected = statusEl.textContent === "Connected";
|
|
382
506
|
setNote();
|
|
@@ -445,49 +569,48 @@ annotationStartButton.addEventListener("click", () => {
|
|
|
445
569
|
if (!response.ok) {
|
|
446
570
|
const message = response.error?.message ?? "Annotation start failed.";
|
|
447
571
|
setAnnotationNote(message);
|
|
448
|
-
|
|
572
|
+
setAnnotationActionsEnabled(false);
|
|
573
|
+
lastAnnotationPayload = null;
|
|
574
|
+
renderAnnotationItems(null);
|
|
449
575
|
return;
|
|
450
576
|
}
|
|
451
577
|
setAnnotationNote("Annotation started. Switch to the tab to select elements.");
|
|
452
|
-
|
|
578
|
+
setAnnotationActionsEnabled(false);
|
|
579
|
+
lastAnnotationPayload = null;
|
|
580
|
+
renderAnnotationItems(null);
|
|
453
581
|
void refreshInjectionStatus();
|
|
454
582
|
})().catch((error) => {
|
|
455
583
|
const message = error instanceof Error ? error.message : "Annotation start failed.";
|
|
456
584
|
setAnnotationNote(message);
|
|
457
|
-
|
|
585
|
+
setAnnotationActionsEnabled(false);
|
|
586
|
+
lastAnnotationPayload = null;
|
|
587
|
+
renderAnnotationItems(null);
|
|
458
588
|
});
|
|
459
589
|
});
|
|
460
590
|
annotationCopyButton.addEventListener("click", () => {
|
|
461
591
|
(async () => {
|
|
462
592
|
setAnnotationNote("Preparing annotation payload...");
|
|
463
|
-
|
|
464
|
-
type: "annotation:getPayload",
|
|
465
|
-
includeScreenshots: true
|
|
466
|
-
});
|
|
467
|
-
if (!response.payload) {
|
|
468
|
-
response = await sendMessage({
|
|
469
|
-
type: "annotation:getPayload",
|
|
470
|
-
includeScreenshots: false
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
if (!response.payload) {
|
|
474
|
-
setAnnotationNote("No completed annotation payload available.");
|
|
475
|
-
setCopyEnabled(false);
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
await copyTextToClipboard(JSON.stringify(response.payload, null, 2));
|
|
479
|
-
if (response.warning) {
|
|
480
|
-
setAnnotationNote(`Copied payload (${response.warning.replace(/\.$/, "")}).`);
|
|
481
|
-
}
|
|
482
|
-
else {
|
|
483
|
-
setAnnotationNote("Copied annotation payload to clipboard.");
|
|
484
|
-
}
|
|
485
|
-
await refreshLastAnnotationMeta();
|
|
593
|
+
await copySelectedAnnotation(undefined, "Copied annotation payload to clipboard.");
|
|
486
594
|
})().catch((error) => {
|
|
487
595
|
const message = error instanceof Error ? error.message : "Copy failed.";
|
|
488
596
|
setAnnotationNote(message);
|
|
489
597
|
});
|
|
490
598
|
});
|
|
599
|
+
annotationSendButton.addEventListener("click", () => {
|
|
600
|
+
(async () => {
|
|
601
|
+
setAnnotationNote("Sending annotation payload to agent inbox...");
|
|
602
|
+
await sendSelectedAnnotation(undefined, "popup_all", "Popup annotation payload", "Sent annotation payload to agent inbox.");
|
|
603
|
+
})().catch((error) => {
|
|
604
|
+
const message = error instanceof Error ? error.message : "Send failed.";
|
|
605
|
+
setAnnotationNote(message);
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
annotationRefreshButton.addEventListener("click", () => {
|
|
609
|
+
refreshLastAnnotationMeta().catch((error) => {
|
|
610
|
+
const message = error instanceof Error ? error.message : "Refresh failed.";
|
|
611
|
+
setAnnotationNote(message);
|
|
612
|
+
});
|
|
613
|
+
});
|
|
491
614
|
autoPairInput.addEventListener("change", () => {
|
|
492
615
|
const enabled = autoPairInput.checked;
|
|
493
616
|
pairingTokenInput.disabled = !pairingEnabledInput.checked || enabled;
|
|
@@ -560,7 +683,8 @@ loadSettings().catch((error) => {
|
|
|
560
683
|
});
|
|
561
684
|
refreshLastAnnotationMeta().catch((error) => {
|
|
562
685
|
logError("popup.annotation_meta", error, { code: "annotation_meta_failed" });
|
|
563
|
-
|
|
686
|
+
setAnnotationActionsEnabled(false);
|
|
687
|
+
renderAnnotationItems(null);
|
|
564
688
|
setAnnotationNote();
|
|
565
689
|
});
|
|
566
690
|
chrome.storage.onChanged.addListener((changes, areaName) => {
|
|
@@ -55,6 +55,7 @@ export class ConnectionManager {
|
|
|
55
55
|
connectPromise = null;
|
|
56
56
|
annotationHandler = null;
|
|
57
57
|
opsHandler = null;
|
|
58
|
+
canvasHandler = null;
|
|
58
59
|
heartbeatTimer = null;
|
|
59
60
|
heartbeatInFlight = false;
|
|
60
61
|
heartbeatIntervalMs = 25_000;
|
|
@@ -91,6 +92,9 @@ export class ConnectionManager {
|
|
|
91
92
|
onOpsMessage(handler) {
|
|
92
93
|
this.opsHandler = handler;
|
|
93
94
|
}
|
|
95
|
+
onCanvasMessage(handler) {
|
|
96
|
+
this.canvasHandler = handler;
|
|
97
|
+
}
|
|
94
98
|
sendAnnotationResponse(response) {
|
|
95
99
|
if (!this.relay)
|
|
96
100
|
return;
|
|
@@ -121,6 +125,16 @@ export class ConnectionManager {
|
|
|
121
125
|
logError("relay.send_ops_message", error, { code: "relay_send_failed" });
|
|
122
126
|
}
|
|
123
127
|
}
|
|
128
|
+
sendCanvasMessage(message) {
|
|
129
|
+
if (!this.relay)
|
|
130
|
+
return;
|
|
131
|
+
try {
|
|
132
|
+
this.relay.sendCanvasMessage(message);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
logError("relay.send_canvas_message", error, { code: "relay_send_failed" });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
124
138
|
getCdpRouter() {
|
|
125
139
|
return this.cdp;
|
|
126
140
|
}
|
|
@@ -370,6 +384,9 @@ export class ConnectionManager {
|
|
|
370
384
|
onOpsMessage: (message) => {
|
|
371
385
|
this.opsHandler?.(message);
|
|
372
386
|
},
|
|
387
|
+
onCanvasMessage: (message) => {
|
|
388
|
+
this.canvasHandler?.(message);
|
|
389
|
+
},
|
|
373
390
|
onClose: (detail) => {
|
|
374
391
|
this.handleRelayClose(detail);
|
|
375
392
|
}
|