diff --git a/package-lock.json b/package-lock.json index 2d7f379..027962b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -113,7 +113,7 @@ "node_modules/@bb/plugin-sdk": { "version": "0.5.0", "resolved": "file:tooling/vendor/bb-plugin-sdk-0.5.0.tgz", - "integrity": "sha512-avMbN5Go1w8QfvSF7/QuexaSXlgYnBzx1B58C/S1F4l07JVqG4BacXY/PaKZzXbA77HQTHNQlEWULHn9JEnYJA==", + "integrity": "sha512-sRdVBYMZTAS5bV4NWoFEDlTeTEomK9HL9kiFc/QMsrTP87i3+cVpmcI/XwKq1DQd6y1xhTjjVKc+cAGRlqnCAQ==", "dev": true, "peerDependencies": { "@testing-library/react": "^16.3.2", @@ -5064,10 +5064,13 @@ "version": "0.1.0", "license": "UNLICENSED", "dependencies": { + "@hugeicons/core-free-icons": "^4.1.3", + "@hugeicons/react": "^1.1.6", "zod": "^4.3.6" }, "devDependencies": { "@bb/plugin-sdk": "file:../../tooling/vendor/bb-plugin-sdk-0.5.0.tgz", + "@radix-ui/react-tooltip": "^1.2.12", "@testing-library/react": "^16.3.2", "@types/better-sqlite3": "^7.6.12", "@types/node": "^22.0.0", @@ -5153,7 +5156,7 @@ "plugins/improve-prompt/node_modules/@bb/plugin-sdk": { "version": "0.5.0", "resolved": "file:plugins/improve-prompt/vendor/bb-plugin-sdk-0.5.0.tgz", - "integrity": "sha512-avMbN5Go1w8QfvSF7/QuexaSXlgYnBzx1B58C/S1F4l07JVqG4BacXY/PaKZzXbA77HQTHNQlEWULHn9JEnYJA==", + "integrity": "sha512-sRdVBYMZTAS5bV4NWoFEDlTeTEomK9HL9kiFc/QMsrTP87i3+cVpmcI/XwKq1DQd6y1xhTjjVKc+cAGRlqnCAQ==", "dev": true, "peerDependencies": { "@testing-library/react": "^16.3.2", @@ -5275,7 +5278,7 @@ "plugins/timeline-comments/node_modules/@bb/plugin-sdk": { "version": "0.5.0", "resolved": "file:plugins/timeline-comments/vendor/bb-plugin-sdk-0.5.0.tgz", - "integrity": "sha512-avMbN5Go1w8QfvSF7/QuexaSXlgYnBzx1B58C/S1F4l07JVqG4BacXY/PaKZzXbA77HQTHNQlEWULHn9JEnYJA==", + "integrity": "sha512-sRdVBYMZTAS5bV4NWoFEDlTeTEomK9HL9kiFc/QMsrTP87i3+cVpmcI/XwKq1DQd6y1xhTjjVKc+cAGRlqnCAQ==", "dev": true, "peerDependencies": { "@testing-library/react": "^16.3.2", diff --git a/plugins/browser-context/README.md b/plugins/browser-context/README.md index efeffe8..3f84a63 100644 --- a/plugins/browser-context/README.md +++ b/plugins/browser-context/README.md @@ -1,8 +1,8 @@ # Browser Context -Select an element or drag over a region in BB's Browser, annotate the captured preview, then add it to the current thread composer. +Select elements or drag over regions in BB's Browser, annotate the numbered captures, then add the batch to the current thread composer. -![Browser context staged in the current bb composer](docs/screenshot.png) +![Inspectable Browser Context mention in bb](docs/screenshot.png) ## Install @@ -12,7 +12,7 @@ bb plugin install git:https://github.com/brsbl/bb-plugins.git@plugin/browser-con ## Use -Open a Browser tab and click the Browser Context action to enter selection mode. Click an element or drag over a region, then add an optional comment. Choose **Add to prompt** to preserve the existing draft, attach the screenshot, and append the comment followed by a compact quoted summary of the page, target, DOM, relevant styles, and available framework or accessibility hints. The user request stays primary while supporting context can collapse naturally, and the standard Send or Queue flow remains in control. +Open a Browser tab and click the Browser Context action to enter selection mode. Click an element or drag over a region, add an optional comment, and use **Select another** to build a numbered batch. Hover or focus a staged row to return its target to the live preview, edit any comment in place, or remove one selection without affecting the rest. Choose **Add to prompt** once to preserve the existing draft and append one inspectable mention pill followed by its ordinary editable comment for every selection. Activate a pill to reopen its capture preview and exact metadata. Screenshots remain preview-only and are never sent; immutable DOM, React, accessibility, and geometry metadata resolves into hidden agent context at send time. The standard Send or Queue flow remains in control. ## Develop diff --git a/plugins/browser-context/app.css b/plugins/browser-context/app.css index f6b9886..457a15f 100644 --- a/plugins/browser-context/app.css +++ b/plugins/browser-context/app.css @@ -16,6 +16,12 @@ color 140ms ease; } + .bb-browser-context-action-trigger { + display: inline-flex; + width: 28px; + height: 28px; + } + .bb-browser-context-action:hover:not(:disabled), .bb-browser-context-action[aria-pressed="true"] { background: var(--state-hover, rgb(0 0 0 / 6%)); @@ -106,15 +112,23 @@ 0 3px 12px rgb(16 24 40 / 24%); cursor: help; outline: none; + opacity: 0.68; + transition: + background-color 120ms ease, + border-color 120ms ease, + box-shadow 120ms ease, + opacity 120ms ease; } .bb-browser-context-target:hover, - .bb-browser-context-target:focus-visible { + .bb-browser-context-target:focus-visible, + .bb-browser-context-target[data-active="true"] { border-color: #1747c8; background: rgb(56 103 232 / 23%); box-shadow: 0 0 0 2px rgb(255 255 255 / 92%), 0 4px 16px rgb(16 24 40 / 30%); + opacity: 1; } .bb-browser-context-target-badge, @@ -147,12 +161,11 @@ width: max-content; max-width: 280px; gap: 2px; - padding: 7px 9px; - border: 1px solid rgb(255 255 255 / 30%); - border-radius: 7px; - background: rgb(16 24 40 / 92%); - color: #fff; - box-shadow: 0 6px 20px rgb(16 24 40 / 28%); + padding: 6px 12px; + border: 0; + border-radius: 6px; + background: var(--primary, #101828); + color: var(--primary-foreground, #fff); text-align: left; white-space: normal; } @@ -163,7 +176,11 @@ } .bb-browser-context-target-tooltip span { - color: rgb(255 255 255 / 78%); + color: color-mix( + in srgb, + var(--primary-foreground, #fff) 78%, + transparent + ); font-size: 11px; } @@ -282,12 +299,168 @@ outline: none; } + .bb-browser-context-selection-list { + display: grid; + max-height: 148px; + gap: 3px; + overflow-y: auto; + margin: 0; + padding: 2px 0; + list-style: none; + } + + .bb-browser-context-selection-list li { + display: flex; + min-width: 0; + align-items: center; + border-radius: 7px; + } + + .bb-browser-context-selection-list li[data-active="true"] { + background: var(--state-hover, rgb(0 0 0 / 6%)); + } + + .bb-browser-context-selection-row { + display: flex; + min-width: 0; + flex: 1; + align-items: center; + gap: 7px; + padding: 5px 4px 5px 6px; + border: 0; + background: transparent; + color: var(--foreground, #101828); + cursor: pointer; + text-align: left; + } + + .bb-browser-context-selection-row > span:last-child { + display: grid; + min-width: 0; + } + + .bb-browser-context-selection-row strong, + .bb-browser-context-selection-row small { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .bb-browser-context-selection-row strong { + font-size: 11px; + font-weight: 650; + } + + .bb-browser-context-selection-row small { + color: var(--muted-foreground, #667085); + font-size: 10px; + } + + .bb-browser-context-selection-row:focus-visible, + .bb-browser-context-remove:focus-visible { + outline: 2px solid var(--ring, #4c76e8); + outline-offset: -1px; + } + + .bb-browser-context-remove { + display: inline-flex; + width: 25px; + height: 25px; + flex: none; + align-items: center; + justify-content: center; + margin-right: 2px; + border: 0; + border-radius: 6px; + background: transparent; + color: var(--muted-foreground, #667085); + cursor: pointer; + } + + .bb-browser-context-remove:hover:not(:disabled) { + background: color-mix( + in srgb, + var(--destructive, #b42318) 10%, + transparent + ); + color: var(--destructive, #b42318); + } + + .bb-browser-context-remove:disabled { + cursor: wait; + opacity: 0.5; + } + + .bb-browser-context-remove svg { + width: 13px; + height: 13px; + } + .bb-browser-context-comment-card textarea:focus-visible { border-color: var(--ring, #4c76e8); box-shadow: 0 0 0 2px color-mix(in srgb, var(--ring, #4c76e8) 24%, transparent); } + .bb-browser-context-comment-submit-row { + display: flex; + justify-content: flex-end; + margin-top: -1px; + } + + .bb-browser-context-comment-submit { + display: inline-flex; + height: 28px; + align-items: center; + justify-content: center; + gap: 7px; + padding: 0 9px; + border: 1px solid var(--border, #d0d5dd); + border-radius: 7px; + background: var(--background, #fff); + color: var(--foreground, #101828); + cursor: pointer; + font: + 600 11px/1 Inter, + system-ui, + sans-serif; + } + + .bb-browser-context-comment-submit:hover:not(:disabled) { + background: var(--state-hover, #f2f4f7); + } + + .bb-browser-context-comment-submit:focus-visible { + outline: 2px solid var(--ring, #4c76e8); + outline-offset: 2px; + } + + .bb-browser-context-comment-submit:disabled { + cursor: default; + opacity: 0.5; + } + + .bb-browser-context-comment-submit[data-submitted="true"] { + border-color: transparent; + background: transparent; + color: var(--muted-foreground, #667085); + opacity: 1; + } + + .bb-browser-context-comment-submit kbd { + min-width: 34px; + padding: 2px 5px; + border: 1px solid var(--border, #d0d5dd); + border-radius: 4px; + background: var(--surface-raised, #f8fafc); + color: var(--muted-foreground, #667085); + font: + 500 9px/1 Inter, + system-ui, + sans-serif; + text-align: center; + } + .bb-browser-context-error { margin: 0; font-size: 11px; @@ -299,12 +472,17 @@ .bb-browser-context-review-actions { display: flex; - justify-content: flex-end; + align-items: center; + justify-content: space-between; gap: 6px; margin-top: 2px; } .bb-browser-context-review-actions button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 5px; height: 30px; padding: 0 10px; border: 1px solid var(--border, #d0d5dd); @@ -318,6 +496,17 @@ sans-serif; } + .bb-browser-context-review-actions button svg { + width: 13px; + height: 13px; + } + + .bb-browser-context-review-actions .bb-browser-context-secondary { + border-color: transparent; + background: transparent; + color: var(--muted-foreground, #667085); + } + .bb-browser-context-review-actions button:hover:not(:disabled) { background: var(--state-hover, #f2f4f7); } @@ -333,17 +522,33 @@ } .bb-browser-context-review-actions .bb-browser-context-primary { - border-color: #3867e8; - background: #3867e8; - color: #fff; + border-color: var(--primary, #101828); + background: var(--primary, #101828); + color: var(--primary-foreground, #fff); } .bb-browser-context-review-actions .bb-browser-context-primary:hover:not(:disabled) { - background: #2d58cf; + background: color-mix(in srgb, var(--primary, #101828) 88%, transparent); } } +[data-bb-plugin="browser-context"].bb-browser-context-action-tooltip { + z-index: 2147483103; + max-width: min(20rem, var(--radix-tooltip-content-available-width)); + overflow: hidden; + padding: 6px 12px; + border-radius: 6px; + background: var(--primary, #101828); + color: var(--primary-foreground, #fff); + font: + 500 12px/1.25 Inter, + system-ui, + sans-serif; + overflow-wrap: anywhere; + animation: bb-browser-context-tooltip-in 120ms ease-out; +} + @keyframes bb-browser-context-activate { 0% { opacity: 0.62; @@ -356,10 +561,26 @@ } } +@keyframes bb-browser-context-tooltip-in { + from { + opacity: 0; + transform: scale(0.95); + } + + to { + opacity: 1; + transform: scale(1); + } +} + @media (prefers-reduced-motion: reduce) { [data-bb-plugin="browser-context"] .bb-browser-context-action[aria-pressed="true"] svg { animation: none; } + + [data-bb-plugin="browser-context"].bb-browser-context-action-tooltip { + animation: none; + } } diff --git a/plugins/browser-context/app.test.tsx b/plugins/browser-context/app.test.tsx index a627884..74e649d 100644 --- a/plugins/browser-context/app.test.tsx +++ b/plugins/browser-context/app.test.tsx @@ -15,7 +15,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import type { rpcContract } from "./server.js"; const capture: ExperimentalBrowserInspectionResult = { - version: 1, + version: 2, kind: "element", page: { url: "https://example.com/settings", @@ -54,40 +54,64 @@ const capture: ExperimentalBrowserInspectionResult = { const regionCapture: ExperimentalBrowserInspectionResult = { ...capture, kind: "region", + rect: { x: 280, y: 160, width: 360, height: 220 }, element: null, region: { - elements: [ + commonAncestor: { + kind: "element", + absoluteLocator: { selectors: ["main > section.settings"] }, + }, + targets: [ { - selector: "main > section", - tag: "section", - id: null, - classNames: ["settings"], - text: "Settings", - rect: { x: 20, y: 30, width: 420, height: 260 }, + absoluteLocator: { + selectors: ["main > section.settings > button#save"], + }, + relativeLocator: { selectors: [":scope > button#save"] }, + text: "Save", + rect: { x: 300, y: 180, width: 180, height: 36 }, + accessibility: { + source: "dom-hint", + roleHint: "button", + nameHint: "Save", + attributes: {}, + }, }, ], + groups: [], + omittedTargetCount: 0, + omittedGroupCount: 0, + scanTruncated: false, }, }; -function preparedCapture(comment: string) { +function preparedBatch(count: number) { return { - promptText: [ - comment, - "", - '> Browser context · + {previewAnnotations.map((annotation) => { + const annotationIndex = annotations.indexOf(annotation); + const annotationLabel = captureTargetLabel(annotation.capture); + const isActive = annotation.id === active.id; + const rectStyle = { + left: percent(annotation.capture.rect.x, viewport.width), + top: percent(annotation.capture.rect.y, viewport.height), + width: percent(annotation.capture.rect.width, viewport.width), + height: percent(annotation.capture.rect.height, viewport.height), + }; + return ( + + ); + })}