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
|
@@ -69,6 +69,7 @@ const ensureRoot = () => {
|
|
|
69
69
|
<div class="odb-title">Annotate</div>
|
|
70
70
|
<div class="odb-actions">
|
|
71
71
|
<button class="odb-btn odb-btn-ghost" data-action="copy">Copy</button>
|
|
72
|
+
<button class="odb-btn odb-btn-ghost" data-action="send">Send</button>
|
|
72
73
|
<button class="odb-btn odb-btn-ghost" data-action="cancel">Cancel</button>
|
|
73
74
|
<button class="odb-btn odb-btn-primary" data-action="submit">Submit</button>
|
|
74
75
|
<button class="odb-btn odb-btn-icon" data-action="close" aria-label="Close">×</button>
|
|
@@ -132,6 +133,12 @@ const ensureRoot = () => {
|
|
|
132
133
|
setCopyFeedback("Copy failed");
|
|
133
134
|
});
|
|
134
135
|
}
|
|
136
|
+
if (action === "send") {
|
|
137
|
+
sendPayload(undefined, "annotate_all", "Annotation payload").catch((error) => {
|
|
138
|
+
logError("annotation.send_payload", error, { code: "annotation_send_failed" });
|
|
139
|
+
setCopyFeedback("Send failed");
|
|
140
|
+
});
|
|
141
|
+
}
|
|
135
142
|
if (action === "cancel") {
|
|
136
143
|
cancelSession();
|
|
137
144
|
}
|
|
@@ -275,9 +282,15 @@ const handleClick = (event) => {
|
|
|
275
282
|
return;
|
|
276
283
|
event.preventDefault();
|
|
277
284
|
event.stopPropagation();
|
|
278
|
-
const
|
|
285
|
+
const clickedTarget = event.target instanceof Element && !isUiElement(event.target)
|
|
286
|
+
? event.target
|
|
287
|
+
: null;
|
|
288
|
+
const target = clickedTarget ?? state.hoverEl;
|
|
279
289
|
if (!target)
|
|
280
290
|
return;
|
|
291
|
+
state.hoverEl = target;
|
|
292
|
+
state.hoverChain = buildAncestorChain(target);
|
|
293
|
+
state.hoverIndex = 0;
|
|
281
294
|
if (event.shiftKey) {
|
|
282
295
|
toggleSelection(target);
|
|
283
296
|
}
|
|
@@ -372,17 +385,36 @@ const createNote = (element, id) => {
|
|
|
372
385
|
note.innerHTML = `
|
|
373
386
|
<div class="odb-note-header">
|
|
374
387
|
<span>${describeElement(element)}</span>
|
|
375
|
-
<
|
|
388
|
+
<div class="odb-actions">
|
|
389
|
+
<button class="odb-btn odb-btn-ghost" data-role="copy-item" type="button">Copy</button>
|
|
390
|
+
<button class="odb-btn odb-btn-ghost" data-role="send-item" type="button">Send</button>
|
|
391
|
+
<button class="odb-note-close" data-role="remove-item" aria-label="Remove" type="button">x</button>
|
|
392
|
+
</div>
|
|
376
393
|
</div>
|
|
377
394
|
<textarea class="odb-note-input" rows="3" placeholder="Add annotation..."></textarea>
|
|
378
395
|
`;
|
|
379
|
-
const close = note.querySelector("button");
|
|
396
|
+
const close = note.querySelector("button[data-role='remove-item']");
|
|
380
397
|
close.addEventListener("click", () => {
|
|
381
398
|
note.remove();
|
|
382
399
|
state.selections.delete(id);
|
|
383
400
|
updateCount();
|
|
384
401
|
scheduleConnectorUpdate();
|
|
385
402
|
});
|
|
403
|
+
const copyButton = note.querySelector("button[data-role='copy-item']");
|
|
404
|
+
copyButton.addEventListener("click", () => {
|
|
405
|
+
void copyPayload([id], copyButton).catch((error) => {
|
|
406
|
+
logError("annotation.copy_item_payload", error, { code: "annotation_copy_item_failed", extra: { id } });
|
|
407
|
+
setButtonFeedback(copyButton, "Copy failed");
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
const sendButton = note.querySelector("button[data-role='send-item']");
|
|
411
|
+
sendButton.addEventListener("click", () => {
|
|
412
|
+
const selection = state.selections.get(id);
|
|
413
|
+
void sendPayload([id], "annotate_item", selection ? describeAnnotationItem(buildAnnotationItem(selection)) : "Annotation item", sendButton).catch((error) => {
|
|
414
|
+
logError("annotation.send_item_payload", error, { code: "annotation_send_item_failed", extra: { id } });
|
|
415
|
+
setButtonFeedback(sendButton, "Send failed");
|
|
416
|
+
});
|
|
417
|
+
});
|
|
386
418
|
const textarea = note.querySelector("textarea");
|
|
387
419
|
textarea.addEventListener("input", () => {
|
|
388
420
|
const selection = state.selections.get(id);
|
|
@@ -495,7 +527,7 @@ const updateConnectors = () => {
|
|
|
495
527
|
state.connectorLayer.appendChild(line);
|
|
496
528
|
}
|
|
497
529
|
};
|
|
498
|
-
const
|
|
530
|
+
const buildCompletePayload = async () => {
|
|
499
531
|
const url = window.location.href;
|
|
500
532
|
const title = document.title;
|
|
501
533
|
const timestamp = new Date().toISOString();
|
|
@@ -518,6 +550,15 @@ const buildPayload = async () => {
|
|
|
518
550
|
annotations
|
|
519
551
|
};
|
|
520
552
|
};
|
|
553
|
+
const buildPayload = async (annotationIds) => {
|
|
554
|
+
const payload = await buildCompletePayload();
|
|
555
|
+
if (!annotationIds || annotationIds.length === 0) {
|
|
556
|
+
return payload;
|
|
557
|
+
}
|
|
558
|
+
return filterAnnotationPayload(payload, annotationIds, {
|
|
559
|
+
includeScreenshots: Boolean(payload.screenshots?.length)
|
|
560
|
+
});
|
|
561
|
+
};
|
|
521
562
|
const extractBase64 = (dataUrl) => {
|
|
522
563
|
if (!dataUrl.includes(","))
|
|
523
564
|
return dataUrl;
|
|
@@ -841,12 +882,75 @@ const mergeOptions = (options) => {
|
|
|
841
882
|
context: options?.context ?? DEFAULT_OPTIONS.context
|
|
842
883
|
};
|
|
843
884
|
};
|
|
844
|
-
const
|
|
845
|
-
const
|
|
885
|
+
const filterAnnotationPayload = (payload, annotationIds, options = {}) => {
|
|
886
|
+
const includeScreenshots = options.includeScreenshots ?? true;
|
|
887
|
+
const wanted = new Set(annotationIds);
|
|
888
|
+
const annotations = payload.annotations.filter((annotation) => wanted.has(annotation.id));
|
|
889
|
+
const screenshotIds = new Set(annotations
|
|
890
|
+
.map((annotation) => annotation.screenshotId)
|
|
891
|
+
.filter((value) => typeof value === "string" && value.length > 0));
|
|
892
|
+
const filtered = {
|
|
893
|
+
...payload,
|
|
894
|
+
screenshotMode: includeScreenshots ? payload.screenshotMode : "none",
|
|
895
|
+
annotations: annotations.map((annotation) => {
|
|
896
|
+
if (includeScreenshots) {
|
|
897
|
+
return annotation;
|
|
898
|
+
}
|
|
899
|
+
const { screenshotId, ...next } = annotation;
|
|
900
|
+
void screenshotId;
|
|
901
|
+
return next;
|
|
902
|
+
})
|
|
903
|
+
};
|
|
904
|
+
if (includeScreenshots) {
|
|
905
|
+
filtered.screenshots = payload.screenshots?.filter((screenshot) => screenshotIds.has(screenshot.id));
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
delete filtered.screenshots;
|
|
909
|
+
}
|
|
910
|
+
return filtered;
|
|
911
|
+
};
|
|
912
|
+
const describeAnnotationItem = (item) => {
|
|
913
|
+
const selector = item.selector?.trim().length ? item.selector : item.tag;
|
|
914
|
+
const label = item.note?.trim().length ? item.note.trim() : item.text?.trim();
|
|
915
|
+
return label ? `${selector} — ${label}` : selector;
|
|
916
|
+
};
|
|
917
|
+
const copyPayload = async (annotationIds, button) => {
|
|
918
|
+
const payload = await buildPayload(annotationIds);
|
|
846
919
|
const text = JSON.stringify(payload);
|
|
847
920
|
await writeClipboard(text);
|
|
921
|
+
if (button) {
|
|
922
|
+
setButtonFeedback(button, "Copied");
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
848
925
|
setCopyFeedback("Copied");
|
|
849
926
|
};
|
|
927
|
+
const sendPayload = async (annotationIds, source, label, button) => {
|
|
928
|
+
const payload = await buildPayload(annotationIds);
|
|
929
|
+
await new Promise((resolve, reject) => {
|
|
930
|
+
chrome.runtime.sendMessage({
|
|
931
|
+
type: "annotation:sendPayload",
|
|
932
|
+
payload,
|
|
933
|
+
source,
|
|
934
|
+
label
|
|
935
|
+
}, (response) => {
|
|
936
|
+
const lastError = chrome.runtime.lastError;
|
|
937
|
+
if (lastError) {
|
|
938
|
+
reject(new Error(lastError.message));
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
if (!response || response.ok !== true) {
|
|
942
|
+
reject(new Error(response?.error?.message ?? "Send failed"));
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
resolve();
|
|
946
|
+
});
|
|
947
|
+
});
|
|
948
|
+
if (button) {
|
|
949
|
+
setButtonFeedback(button, "Sent");
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
setCopyFeedback("Sent");
|
|
953
|
+
};
|
|
850
954
|
const writeClipboard = async (value) => {
|
|
851
955
|
if (navigator.clipboard?.writeText) {
|
|
852
956
|
try {
|
|
@@ -874,17 +978,23 @@ const setCopyFeedback = (label) => {
|
|
|
874
978
|
const button = state.copyButton;
|
|
875
979
|
if (!button)
|
|
876
980
|
return;
|
|
877
|
-
|
|
981
|
+
setButtonFeedback(button, label, true);
|
|
982
|
+
};
|
|
983
|
+
const setButtonFeedback = (button, label, useSharedTimer = false) => {
|
|
984
|
+
const original = button.dataset.originalLabel ?? button.textContent ?? button.getAttribute("aria-label") ?? "Action";
|
|
878
985
|
if (!button.dataset.originalLabel) {
|
|
879
986
|
button.dataset.originalLabel = original;
|
|
880
987
|
}
|
|
881
988
|
button.textContent = label;
|
|
882
|
-
if (state.copyTimeout !== null) {
|
|
989
|
+
if (useSharedTimer && state.copyTimeout !== null) {
|
|
883
990
|
window.clearTimeout(state.copyTimeout);
|
|
884
991
|
}
|
|
885
|
-
|
|
992
|
+
const restore = window.setTimeout(() => {
|
|
886
993
|
button.textContent = original;
|
|
887
994
|
}, 1500);
|
|
995
|
+
if (useSharedTimer) {
|
|
996
|
+
state.copyTimeout = restore;
|
|
997
|
+
}
|
|
888
998
|
};
|
|
889
999
|
const bootstrap = () => {
|
|
890
1000
|
if (window.__odbAnnotate) {
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2
|
+
const readString = (value) => typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
3
|
+
const formatCanvasUrl = (documentId, page) => {
|
|
4
|
+
const safePath = page.path && page.path.trim().length > 0 ? page.path : page.id;
|
|
5
|
+
return `canvas://${documentId}${safePath.startsWith("/") ? safePath : `/${safePath}`}`;
|
|
6
|
+
};
|
|
7
|
+
const readCanvasNodeText = (node) => {
|
|
8
|
+
const raw = node.props.text ?? node.metadata.text ?? node.name;
|
|
9
|
+
if (raw === undefined || raw === null) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const text = typeof raw === "string" ? raw.trim() : String(raw).trim();
|
|
13
|
+
return text.length > 0 ? text.slice(0, 240) : undefined;
|
|
14
|
+
};
|
|
15
|
+
const formatCanvasStyleValue = (value) => {
|
|
16
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
17
|
+
return `${value}px`;
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
};
|
|
24
|
+
const buildCanvasStyles = (node) => {
|
|
25
|
+
const style = isRecord(node.style) ? node.style : {};
|
|
26
|
+
return {
|
|
27
|
+
color: formatCanvasStyleValue(style.color),
|
|
28
|
+
backgroundColor: formatCanvasStyleValue(style.backgroundColor),
|
|
29
|
+
fontSize: formatCanvasStyleValue(style.fontSize),
|
|
30
|
+
fontFamily: formatCanvasStyleValue(style.fontFamily),
|
|
31
|
+
fontWeight: formatCanvasStyleValue(style.fontWeight),
|
|
32
|
+
lineHeight: formatCanvasStyleValue(style.lineHeight),
|
|
33
|
+
display: formatCanvasStyleValue(style.display),
|
|
34
|
+
position: formatCanvasStyleValue(style.position ?? "absolute")
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const buildCanvasAttributes = (node) => {
|
|
38
|
+
const propsAttributes = isRecord(node.props.attributes) ? node.props.attributes : {};
|
|
39
|
+
const result = {
|
|
40
|
+
"data-node-id": node.id,
|
|
41
|
+
"data-canvas-kind": node.kind
|
|
42
|
+
};
|
|
43
|
+
for (const [key, value] of Object.entries(propsAttributes)) {
|
|
44
|
+
const next = readString(value);
|
|
45
|
+
if (next) {
|
|
46
|
+
result[key] = next;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const tagName = readString(node.props.tagName);
|
|
50
|
+
if (tagName) {
|
|
51
|
+
result["data-tag-name"] = tagName;
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
const resolveCanvasTag = (node) => {
|
|
56
|
+
return readString(node.props.tagName)?.toLowerCase()
|
|
57
|
+
?? readString(isRecord(node.metadata.codeSync) ? node.metadata.codeSync.tagName : null)?.toLowerCase()
|
|
58
|
+
?? node.kind;
|
|
59
|
+
};
|
|
60
|
+
export function stripAnnotationPayloadScreenshots(payload) {
|
|
61
|
+
const { screenshots, annotations, ...rest } = payload;
|
|
62
|
+
void screenshots;
|
|
63
|
+
return {
|
|
64
|
+
...rest,
|
|
65
|
+
screenshotMode: "none",
|
|
66
|
+
annotations: annotations.map((annotation) => {
|
|
67
|
+
const { screenshotId, ...next } = annotation;
|
|
68
|
+
void screenshotId;
|
|
69
|
+
return next;
|
|
70
|
+
})
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function filterAnnotationPayload(payload, annotationIds, options = {}) {
|
|
74
|
+
const includeScreenshots = options.includeScreenshots ?? true;
|
|
75
|
+
const wanted = new Set(annotationIds);
|
|
76
|
+
const annotations = payload.annotations.filter((annotation) => wanted.has(annotation.id));
|
|
77
|
+
if (annotations.length === payload.annotations.length && includeScreenshots) {
|
|
78
|
+
return payload;
|
|
79
|
+
}
|
|
80
|
+
const screenshotIds = new Set(annotations
|
|
81
|
+
.map((annotation) => annotation.screenshotId)
|
|
82
|
+
.filter((value) => typeof value === "string" && value.length > 0));
|
|
83
|
+
const filtered = {
|
|
84
|
+
...payload,
|
|
85
|
+
screenshotMode: includeScreenshots ? payload.screenshotMode : "none",
|
|
86
|
+
annotations: annotations.map((annotation) => {
|
|
87
|
+
if (includeScreenshots) {
|
|
88
|
+
return annotation;
|
|
89
|
+
}
|
|
90
|
+
const { screenshotId, ...next } = annotation;
|
|
91
|
+
void screenshotId;
|
|
92
|
+
return next;
|
|
93
|
+
})
|
|
94
|
+
};
|
|
95
|
+
if (includeScreenshots) {
|
|
96
|
+
filtered.screenshots = payload.screenshots?.filter((screenshot) => screenshotIds.has(screenshot.id));
|
|
97
|
+
return filtered;
|
|
98
|
+
}
|
|
99
|
+
delete filtered.screenshots;
|
|
100
|
+
return filtered;
|
|
101
|
+
}
|
|
102
|
+
export function describeAnnotationItem(item) {
|
|
103
|
+
const selector = item.selector?.trim().length ? item.selector : item.tag;
|
|
104
|
+
const label = item.note?.trim().length ? item.note.trim() : item.text?.trim();
|
|
105
|
+
return label ? `${selector} — ${label}` : selector;
|
|
106
|
+
}
|
|
107
|
+
export function formatDispatchSourceLabel(source) {
|
|
108
|
+
switch (source) {
|
|
109
|
+
case "annotate_item":
|
|
110
|
+
return "annotation item";
|
|
111
|
+
case "annotate_all":
|
|
112
|
+
return "annotation payload";
|
|
113
|
+
case "popup_item":
|
|
114
|
+
return "popup annotation item";
|
|
115
|
+
case "popup_all":
|
|
116
|
+
return "popup annotation payload";
|
|
117
|
+
case "canvas_item":
|
|
118
|
+
return "canvas annotation item";
|
|
119
|
+
case "canvas_all":
|
|
120
|
+
return "canvas annotation payload";
|
|
121
|
+
default:
|
|
122
|
+
return "annotation payload";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export function buildCanvasAnnotationPayload(options) {
|
|
126
|
+
const nodesById = new Map(options.page.nodes.map((node) => [node.id, node]));
|
|
127
|
+
const annotations = options.drafts.flatMap((draft) => {
|
|
128
|
+
const node = nodesById.get(draft.nodeId);
|
|
129
|
+
if (!node) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const tag = resolveCanvasTag(node);
|
|
133
|
+
return [{
|
|
134
|
+
id: node.id,
|
|
135
|
+
selector: `[data-node-id="${node.id}"]`,
|
|
136
|
+
tag,
|
|
137
|
+
idAttr: node.id,
|
|
138
|
+
classes: [`canvas-node`, `canvas-${node.kind}`],
|
|
139
|
+
text: readCanvasNodeText(node),
|
|
140
|
+
rect: {
|
|
141
|
+
x: node.rect.x,
|
|
142
|
+
y: node.rect.y,
|
|
143
|
+
width: node.rect.width,
|
|
144
|
+
height: node.rect.height
|
|
145
|
+
},
|
|
146
|
+
attributes: buildCanvasAttributes(node),
|
|
147
|
+
a11y: {
|
|
148
|
+
role: readString(isRecord(node.metadata.accessibility) ? node.metadata.accessibility.role : null) ?? undefined,
|
|
149
|
+
label: readString(isRecord(node.metadata.accessibility) ? node.metadata.accessibility.label : null) ?? undefined
|
|
150
|
+
},
|
|
151
|
+
styles: buildCanvasStyles(node),
|
|
152
|
+
note: readString(draft.note) ?? undefined
|
|
153
|
+
}];
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
url: formatCanvasUrl(options.document.documentId, options.page),
|
|
157
|
+
title: `${options.document.title} • ${options.page.name}`,
|
|
158
|
+
timestamp: new Date().toISOString(),
|
|
159
|
+
context: options.context,
|
|
160
|
+
screenshotMode: "none",
|
|
161
|
+
annotations
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -3,10 +3,17 @@ import { NativePortManager } from "./services/NativePortManager.js";
|
|
|
3
3
|
import { DEFAULT_AUTO_CONNECT, DEFAULT_AUTO_PAIR, DEFAULT_DISCOVERY_PORT, DEFAULT_NATIVE_ENABLED, DEFAULT_PAIRING_ENABLED, DEFAULT_RELAY_PORT } from "./relay-settings.js";
|
|
4
4
|
import { logError } from "./logging.js";
|
|
5
5
|
import { OpsRuntime } from "./ops/ops-runtime.js";
|
|
6
|
+
import { CanvasRuntime } from "./canvas/canvas-runtime.js";
|
|
7
|
+
import { formatDispatchSourceLabel, stripAnnotationPayloadScreenshots } from "./annotation-payload.js";
|
|
6
8
|
const connection = new ConnectionManager();
|
|
9
|
+
const canvasRuntime = new CanvasRuntime({
|
|
10
|
+
send: (message) => connection.sendCanvasMessage(message)
|
|
11
|
+
});
|
|
7
12
|
const opsRuntime = new OpsRuntime({
|
|
8
13
|
send: (message) => connection.sendOpsMessage(message),
|
|
9
|
-
cdp: connection.getCdpRouter()
|
|
14
|
+
cdp: connection.getCdpRouter(),
|
|
15
|
+
getCanvasPageState: (targetId) => canvasRuntime.getPageStateByTargetId(targetId),
|
|
16
|
+
performCanvasPageAction: (targetId, action, selector) => canvasRuntime.performPageAction(targetId, action, selector)
|
|
10
17
|
});
|
|
11
18
|
const nativePort = new NativePortManager({
|
|
12
19
|
onMessage: (payload) => {
|
|
@@ -31,10 +38,13 @@ const ANNOTATION_MAX_PAYLOAD_BYTES = 10 * 1024 * 1024;
|
|
|
31
38
|
const ANNOTATION_REQUEST_TIMEOUT_MS = 120_000;
|
|
32
39
|
const LAST_ANNOTATION_META_KEY = "annotationLastMeta";
|
|
33
40
|
const LAST_ANNOTATION_PAYLOAD_KEY = "annotationLastPayloadSansScreenshots";
|
|
41
|
+
const LAST_AGENT_ANNOTATION_META_KEY = "annotationAgentMeta";
|
|
42
|
+
const LAST_AGENT_ANNOTATION_PAYLOAD_KEY = "annotationAgentPayloadSansScreenshots";
|
|
34
43
|
const BADGE_CONNECTED_DOT_COLOR = "#16a34a";
|
|
35
44
|
const BADGE_DISCONNECTED_DOT_COLOR = "#dc2626";
|
|
36
45
|
const annotationSessions = new Map();
|
|
37
46
|
let lastAnnotationFull = null;
|
|
47
|
+
let lastAgentAnnotationFull = null;
|
|
38
48
|
connection.onAnnotationCommand((command) => {
|
|
39
49
|
handleRelayAnnotationCommand(command).catch((error) => {
|
|
40
50
|
logError("annotation.relay_command", error, { code: "annotation_command_failed" });
|
|
@@ -43,6 +53,9 @@ connection.onAnnotationCommand((command) => {
|
|
|
43
53
|
connection.onOpsMessage((message) => {
|
|
44
54
|
opsRuntime.handleMessage(message);
|
|
45
55
|
});
|
|
56
|
+
connection.onCanvasMessage((message) => {
|
|
57
|
+
canvasRuntime.handleMessage(message);
|
|
58
|
+
});
|
|
46
59
|
const RESTRICTED_PROTOCOLS = new Set([
|
|
47
60
|
"chrome:",
|
|
48
61
|
"chrome-extension:",
|
|
@@ -156,6 +169,8 @@ const buildRelayHealthNote = (health) => {
|
|
|
156
169
|
return "Annotation channel disconnected. Keep the extension open and retry.";
|
|
157
170
|
case "ops_disconnected":
|
|
158
171
|
return "Ops channel disconnected. Start a new session and retry.";
|
|
172
|
+
case "canvas_disconnected":
|
|
173
|
+
return "Canvas channel disconnected. Reopen the design canvas command and retry.";
|
|
159
174
|
case "cdp_disconnected":
|
|
160
175
|
return "No CDP clients connected. Start a session and retry.";
|
|
161
176
|
case "relay_down":
|
|
@@ -303,6 +318,7 @@ const fetchRelayHealth = async (port) => {
|
|
|
303
318
|
const cdpConnected = data.cdpConnected === true;
|
|
304
319
|
const annotationConnected = data.annotationConnected === true;
|
|
305
320
|
const opsConnected = data.opsConnected === true;
|
|
321
|
+
const canvasConnected = data.canvasConnected === true;
|
|
306
322
|
const pairingRequired = data.pairingRequired === true;
|
|
307
323
|
const ok = extensionConnected && handshake;
|
|
308
324
|
return {
|
|
@@ -313,6 +329,7 @@ const fetchRelayHealth = async (port) => {
|
|
|
313
329
|
cdpConnected,
|
|
314
330
|
annotationConnected,
|
|
315
331
|
opsConnected,
|
|
332
|
+
canvasConnected,
|
|
316
333
|
pairingRequired
|
|
317
334
|
};
|
|
318
335
|
}
|
|
@@ -675,19 +692,7 @@ const validatePayloadSize = (payload) => {
|
|
|
675
692
|
const generateAnnotationRequestId = () => {
|
|
676
693
|
return crypto.randomUUID();
|
|
677
694
|
};
|
|
678
|
-
const
|
|
679
|
-
const { screenshots, annotations, ...rest } = payload;
|
|
680
|
-
void screenshots;
|
|
681
|
-
return {
|
|
682
|
-
...rest,
|
|
683
|
-
annotations: annotations.map((item) => {
|
|
684
|
-
const { screenshotId, ...restItem } = item;
|
|
685
|
-
void screenshotId;
|
|
686
|
-
return restItem;
|
|
687
|
-
})
|
|
688
|
-
};
|
|
689
|
-
};
|
|
690
|
-
const buildLastAnnotationMeta = (requestId, response, hasFullPayloadInMemory) => {
|
|
695
|
+
const buildLastAnnotationMeta = (requestId, response, hasFullPayloadInMemory, extras = {}) => {
|
|
691
696
|
const payload = response.payload;
|
|
692
697
|
const annotationCount = payload ? payload.annotations.length : undefined;
|
|
693
698
|
const screenshotCount = payload?.screenshots?.length ?? 0;
|
|
@@ -695,6 +700,8 @@ const buildLastAnnotationMeta = (requestId, response, hasFullPayloadInMemory) =>
|
|
|
695
700
|
requestId,
|
|
696
701
|
status: response.status,
|
|
697
702
|
error: response.error,
|
|
703
|
+
source: extras.source,
|
|
704
|
+
label: extras.label,
|
|
698
705
|
url: payload?.url,
|
|
699
706
|
title: payload?.title,
|
|
700
707
|
timestamp: payload?.timestamp,
|
|
@@ -712,6 +719,12 @@ const persistLastAnnotation = async (meta, payload) => {
|
|
|
712
719
|
[LAST_ANNOTATION_PAYLOAD_KEY]: payload
|
|
713
720
|
});
|
|
714
721
|
};
|
|
722
|
+
const persistAgentAnnotation = async (meta, payload) => {
|
|
723
|
+
await setStorage({
|
|
724
|
+
[LAST_AGENT_ANNOTATION_META_KEY]: meta,
|
|
725
|
+
[LAST_AGENT_ANNOTATION_PAYLOAD_KEY]: payload
|
|
726
|
+
});
|
|
727
|
+
};
|
|
715
728
|
const loadPersistedLastAnnotation = async () => {
|
|
716
729
|
const data = await new Promise((resolve) => {
|
|
717
730
|
chrome.storage.local.get([LAST_ANNOTATION_META_KEY, LAST_ANNOTATION_PAYLOAD_KEY], (items) => resolve(items));
|
|
@@ -722,6 +735,77 @@ const loadPersistedLastAnnotation = async () => {
|
|
|
722
735
|
const payload = payloadRecord && typeof payloadRecord === "object" ? payloadRecord : null;
|
|
723
736
|
return { meta, payload };
|
|
724
737
|
};
|
|
738
|
+
const loadPersistedAgentAnnotation = async () => {
|
|
739
|
+
const data = await new Promise((resolve) => {
|
|
740
|
+
chrome.storage.local.get([LAST_AGENT_ANNOTATION_META_KEY, LAST_AGENT_ANNOTATION_PAYLOAD_KEY], (items) => resolve(items));
|
|
741
|
+
});
|
|
742
|
+
const metaRecord = data[LAST_AGENT_ANNOTATION_META_KEY];
|
|
743
|
+
const payloadRecord = data[LAST_AGENT_ANNOTATION_PAYLOAD_KEY];
|
|
744
|
+
const meta = metaRecord && typeof metaRecord === "object" ? metaRecord : null;
|
|
745
|
+
const payload = payloadRecord && typeof payloadRecord === "object" ? payloadRecord : null;
|
|
746
|
+
return { meta, payload };
|
|
747
|
+
};
|
|
748
|
+
const isAnnotationPayload = (value) => {
|
|
749
|
+
if (!value || typeof value !== "object") {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
const payload = value;
|
|
753
|
+
return typeof payload.url === "string"
|
|
754
|
+
&& typeof payload.timestamp === "string"
|
|
755
|
+
&& typeof payload.screenshotMode === "string"
|
|
756
|
+
&& Array.isArray(payload.annotations);
|
|
757
|
+
};
|
|
758
|
+
const storeAgentAnnotationPayload = async (payload, source, label) => {
|
|
759
|
+
if (!validatePayloadSize(payload)) {
|
|
760
|
+
throw new Error("Annotation payload exceeded size limits.");
|
|
761
|
+
}
|
|
762
|
+
const response = {
|
|
763
|
+
version: 1,
|
|
764
|
+
requestId: crypto.randomUUID(),
|
|
765
|
+
status: "ok",
|
|
766
|
+
payload
|
|
767
|
+
};
|
|
768
|
+
const meta = buildLastAnnotationMeta(response.requestId, response, true, {
|
|
769
|
+
source,
|
|
770
|
+
label: label?.trim().length ? label.trim() : formatDispatchSourceLabel(source)
|
|
771
|
+
});
|
|
772
|
+
lastAgentAnnotationFull = { meta, payload };
|
|
773
|
+
await persistAgentAnnotation(meta, stripAnnotationPayloadScreenshots(payload));
|
|
774
|
+
return meta;
|
|
775
|
+
};
|
|
776
|
+
const loadAgentAnnotationPayload = async (includeScreenshots) => {
|
|
777
|
+
if (includeScreenshots && lastAgentAnnotationFull) {
|
|
778
|
+
return {
|
|
779
|
+
version: 1,
|
|
780
|
+
requestId: crypto.randomUUID(),
|
|
781
|
+
status: "ok",
|
|
782
|
+
payload: lastAgentAnnotationFull.payload
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
if (lastAgentAnnotationFull) {
|
|
786
|
+
return {
|
|
787
|
+
version: 1,
|
|
788
|
+
requestId: crypto.randomUUID(),
|
|
789
|
+
status: "ok",
|
|
790
|
+
payload: stripAnnotationPayloadScreenshots(lastAgentAnnotationFull.payload)
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
const stored = await loadPersistedAgentAnnotation();
|
|
794
|
+
if (stored.payload) {
|
|
795
|
+
return {
|
|
796
|
+
version: 1,
|
|
797
|
+
requestId: crypto.randomUUID(),
|
|
798
|
+
status: "ok",
|
|
799
|
+
payload: stored.payload
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
return {
|
|
803
|
+
version: 1,
|
|
804
|
+
requestId: crypto.randomUUID(),
|
|
805
|
+
status: "error",
|
|
806
|
+
error: { code: "payload_unavailable", message: "No agent-dispatched annotation payload available." }
|
|
807
|
+
};
|
|
808
|
+
};
|
|
725
809
|
async function handleNativePortMessage(payload) {
|
|
726
810
|
if (!payload || typeof payload !== "object") {
|
|
727
811
|
return;
|
|
@@ -746,6 +830,14 @@ const handleRelayAnnotationCommand = async (command, transport = "relay") => {
|
|
|
746
830
|
await cancelAnnotationSession(payload.requestId, transport);
|
|
747
831
|
return;
|
|
748
832
|
}
|
|
833
|
+
if (payload.command === "fetch_stored") {
|
|
834
|
+
const stored = await loadAgentAnnotationPayload(payload.options?.includeScreenshots === true);
|
|
835
|
+
sendAnnotationResponse({
|
|
836
|
+
...stored,
|
|
837
|
+
requestId: payload.requestId
|
|
838
|
+
}, transport);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
749
841
|
try {
|
|
750
842
|
await startAnnotationSession(payload, transport);
|
|
751
843
|
sendAnnotationEvent({
|
|
@@ -805,7 +897,7 @@ const handleAnnotationComplete = (requestId, payload) => {
|
|
|
805
897
|
const meta = buildLastAnnotationMeta(requestId, response, true);
|
|
806
898
|
lastAnnotationFull = { meta, payload };
|
|
807
899
|
const storageMeta = { ...meta, hasFullPayloadInMemory: false };
|
|
808
|
-
const sanitizedPayload =
|
|
900
|
+
const sanitizedPayload = stripAnnotationPayloadScreenshots(payload);
|
|
809
901
|
persistLastAnnotation(storageMeta, sanitizedPayload).catch((error) => {
|
|
810
902
|
logError("annotation.persist_sanitized_payload", error, { code: "annotation_persist_failed" });
|
|
811
903
|
});
|
|
@@ -1237,9 +1329,9 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
1237
1329
|
if (message.includeScreenshots) {
|
|
1238
1330
|
const response = {
|
|
1239
1331
|
type: "annotation:payloadResult",
|
|
1240
|
-
payload:
|
|
1332
|
+
payload: stored.payload,
|
|
1241
1333
|
meta: storedMeta,
|
|
1242
|
-
source: "none",
|
|
1334
|
+
source: stored.payload ? "storage" : "none",
|
|
1243
1335
|
warning: "Full payload not available; screenshots may have been dropped."
|
|
1244
1336
|
};
|
|
1245
1337
|
sendResponse(response);
|
|
@@ -1248,7 +1340,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
1248
1340
|
if (lastAnnotationFull) {
|
|
1249
1341
|
const response = {
|
|
1250
1342
|
type: "annotation:payloadResult",
|
|
1251
|
-
payload:
|
|
1343
|
+
payload: stripAnnotationPayloadScreenshots(lastAnnotationFull.payload),
|
|
1252
1344
|
meta: { ...lastAnnotationFull.meta, hasFullPayloadInMemory: true },
|
|
1253
1345
|
source: "memory"
|
|
1254
1346
|
};
|
|
@@ -1285,6 +1377,39 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
1285
1377
|
});
|
|
1286
1378
|
return true;
|
|
1287
1379
|
}
|
|
1380
|
+
if (message.type === "annotation:sendPayload") {
|
|
1381
|
+
(async () => {
|
|
1382
|
+
if (!isAnnotationPayload(message.payload)) {
|
|
1383
|
+
const response = {
|
|
1384
|
+
type: "annotation:sendPayloadResult",
|
|
1385
|
+
ok: false,
|
|
1386
|
+
meta: null,
|
|
1387
|
+
error: { code: "invalid_request", message: "Invalid annotation payload." }
|
|
1388
|
+
};
|
|
1389
|
+
sendResponse(response);
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
const meta = await storeAgentAnnotationPayload(message.payload, message.source ?? "popup_all", message.label);
|
|
1393
|
+
const response = {
|
|
1394
|
+
type: "annotation:sendPayloadResult",
|
|
1395
|
+
ok: true,
|
|
1396
|
+
meta: { ...meta, hasFullPayloadInMemory: true }
|
|
1397
|
+
};
|
|
1398
|
+
sendResponse(response);
|
|
1399
|
+
})().catch((error) => {
|
|
1400
|
+
const response = {
|
|
1401
|
+
type: "annotation:sendPayloadResult",
|
|
1402
|
+
ok: false,
|
|
1403
|
+
meta: null,
|
|
1404
|
+
error: {
|
|
1405
|
+
code: "payload_too_large",
|
|
1406
|
+
message: error instanceof Error ? error.message : "Annotation dispatch failed."
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
sendResponse(response);
|
|
1410
|
+
});
|
|
1411
|
+
return true;
|
|
1412
|
+
}
|
|
1288
1413
|
if (message.type === "annotation:capture") {
|
|
1289
1414
|
(async () => {
|
|
1290
1415
|
const tab = sender.tab;
|
|
@@ -1319,3 +1444,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
1319
1444
|
}
|
|
1320
1445
|
return false;
|
|
1321
1446
|
});
|
|
1447
|
+
chrome.runtime.onConnect.addListener((port) => {
|
|
1448
|
+
if (port.name === "canvas-page") {
|
|
1449
|
+
canvasRuntime.attachPort(port);
|
|
1450
|
+
}
|
|
1451
|
+
});
|