
Security News
RubyGems Adds Cooldown Feature to Bundler for Newly Published Gems
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.
pro-ui-feedbacks
Advanced tools
Annotate any website, generate structured visual feedback for AI agents — works as React component or standalone embed
Annotate any UI. AI understands the rest.
Click elements, leave notes, copy AI-ready markdown — works with Claude, ChatGPT, Cursor, or any AI tool.
Lightweight widget for any website: React, Next.js, Vue, HTML, WordPress, and more.
Screenshots lack context. Text descriptions are ambiguous. AI agents need structured data to act on your UI feedback.
| Method | What AI gets | What's missing |
|---|---|---|
| Screenshot | Pixels | No selectors, no DOM structure — AI guesses |
| Copy HTML from DevTools | Raw markup | Too much noise, no annotations |
| Text description | "the blue button on the right" | Ambiguous, no coordinates |
| pro-ui-feedbacks | CSS selector + DOM path + metadata + annotation + order | Nothing — structured & actionable |
⌘⇧C. All annotations are formatted as structured markdown and copied to your clipboard.When you copy feedbacks, AI receives structured markdown like this:
## Page Feedback: /dashboard
**Viewport:** 1440×900
### 1. button: "Submit Order"
**Location:** .checkout-form > .actions > button.btn-primary
**Position:** 892px, 1247px (120×40px)
**Feedback:** Change button color from blue to green for better conversion
### 2. heading: "Order Summary"
**Location:** .sidebar > h2.summary-title
**Position:** 1080px, 200px (300×32px)
**Feedback:** Font size too small on mobile — increase to 18px
Switch to debug mode for even richer output: full DOM paths, computed styles, annotation coordinates, viewport info, and device pixel ratio.
detailed (compact markdown with selector, location, position) or debug (full markdown with environment, DOM path, computed styles, annotation coordinates)localStorage persistence so feedbacks survive page reload, with orphan detection for missing elements⌘⇧F toolbar, ⌘⇧I inspector, ⌘⇧C copy, ⌘⇧L list, [/] navigate markers, and morecreatePortal so it never conflicts with your app's layout or z-indexnpm install pro-ui-feedbacks lucide-react
reactandreact-dom(v18 or v19) are required as peer dependencies.
import { ProUIFeedbacks } from 'pro-ui-feedbacks'
function App() {
return (
<>
{/* your app */}
<ProUIFeedbacks
onFeedbackSubmit={(fb) => console.log('New feedback:', fb)}
onFeedbackDelete={(id) => console.log('Deleted:', id)}
/>
</>
)
}
The toolbar appears as a collapsed floating button (bottom-right by default). Click it to expand, hit Start to activate the inspector, then click any element to leave feedback.
Drop a single script tag on any site — HTML, WordPress, Vue, Angular, or anything else. No build tools required.
<script src="https://unpkg.com/pro-ui-feedbacks/dist/embed.global.js"></script>
<pro-ui-feedbacks position="bottom-right" theme="dark" persist></pro-ui-feedbacks>
<script src="https://unpkg.com/pro-ui-feedbacks/dist/embed.global.js"></script>
<script>
const widget = ProUIFeedbacks.init({
position: 'bottom-right',
theme: 'dark',
persist: 'my-session'
});
// Later: widget.destroy();
</script>
| Attribute | Type | Default | Description |
|---|---|---|---|
position | string | 'bottom-right' | Toolbar position |
theme | string | 'dark' | Color theme ('dark' or 'light') |
z-index | number | 9999 | Base z-index |
collapsed | boolean | true | Start collapsed |
persist | boolean|string | — | Enable localStorage persistence |
const widget = document.querySelector('pro-ui-feedbacks');
widget.addEventListener('toggle', (e) => console.log('Active:', e.detail.active));
widget.addEventListener('feedback-submit', (e) => console.log('Feedback:', e.detail.feedback));
widget.addEventListener('feedback-delete', (e) => console.log('Deleted:', e.detail.feedbackId));
widget.addEventListener('copy', () => console.log('Copied'));
<!-- Add to theme footer or Custom HTML widget -->
<script src="https://unpkg.com/pro-ui-feedbacks/dist/embed.global.js"></script>
<script>
ProUIFeedbacks.init({
position: 'bottom-right',
theme: 'dark',
persist: 'wp-feedback'
});
</script>
Bundle size: ~76KB gzip (includes React runtime, fully self-contained)
| Prop | Type | Default | Description |
|---|---|---|---|
position | 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'bottom-right' | Toolbar position on the viewport |
theme | 'dark' | 'light' | 'dark' | Color theme |
defaultCollapsed | boolean | true | Start with toolbar collapsed |
zIndex | number | 9999 | Base z-index for all layers |
triggerIcon | ReactNode | <Menu /> | Custom icon for the trigger button |
style | CSSProperties | — | Additional inline styles for the container |
persist | boolean | string | — | Enable localStorage persistence. true = per-page key, string = custom key |
| Prop | Type | Description |
|---|---|---|
onToggle | (active: boolean) => void | Inspector started or stopped |
onInspect | (element: InspectedElement) => void | Element selected via inspector |
onFeedbackSubmit | (feedback: FeedbackItem) => void | Feedback annotation submitted |
onFeedbackDelete | (feedbackId: string) => void | Feedback marker deleted |
onFeedbackUpdate | (feedbackId: string, content: string) => void | Feedback text edited |
onFeedback | () => void | "Feedbacks" toolbar button clicked |
onCopy | () => void | "Copy" toolbar button clicked |
onDelete | () => void | "Delete All" toolbar button clicked |
onSettings | () => void | "Settings" toolbar button clicked |
interface ElementAccessibility {
role?: string
label?: string
description?: string
}
interface ElementMetadata {
accessibility: ElementAccessibility
boundingBox: { x: number; y: number; width: number; height: number }
computedStyles: Record<string, string>
cssClasses: string[]
elementDescription: string // e.g. `paragraph: "Some text..."`
elementPath: string // short class-based CSS path
fullPath: string // full tag+class CSS path
isFixed: boolean
nearbyElements: string
nearbyText: string
}
interface InspectedElement {
element: HTMLElement
tagName: string
className: string
id: string
selector: string // generated CSS selector
rect: DOMRect
dimensions: { width: number; height: number }
metadata: ElementMetadata // rich element metadata
}
interface FeedbackItem {
id: string
stepNumber: number // 1-based
content: string // user-entered text
selector: string // CSS selector of target element
offsetX: number // offset from element's top-left
offsetY: number
pageX: number // absolute page X coordinate
pageY: number // absolute page Y coordinate
targetElement: HTMLElement | null
element: InspectedElement | null
createdAt: number // timestamp
orphan?: boolean // true when element not found after reload
areaData?: AreaData // area metadata for multi-select
isAreaOnly?: boolean // true if annotation on empty space
elements?: InspectedElement[] // all elements in multi-select group
}
interface AreaData {
centerX: number // absolute page X of area center
centerY: number // absolute page Y of area center
width: number // area width in pixels
height: number // area height in pixels
elementCount: number // total elements in group
}
type ToolbarPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
type ToolbarTheme = 'dark' | 'light'
All types are exported from the package entry point, including ElementMetadata, ElementAccessibility, SerializedFeedbackItem, AreaData, AreaBounds, and InspectAreaEvent.
Drag to select multiple elements and annotate them as a group:
Features:
Limits:
| Shortcut | Action |
|---|---|
⌘⇧F | Toggle toolbar |
⌘⇧I | Toggle inspector |
⌘⇧C | Copy all feedbacks |
⌘⇧L | Open feedback list |
⌘⇧, | Open settings |
⌘⇧⌫ | Delete all feedbacks |
⌘Z | Undo last delete |
[ / ] | Navigate between markers |
Enter | Edit focused marker |
Escape | Close popups → stop inspector → deselect marker → collapse toolbar |
On Windows/Linux, use
Ctrlinstead of⌘.
<ProUIFeedbacks position="top-left" theme="light" />
function ReviewPage() {
const feedbacks = useRef<FeedbackItem[]>([])
return (
<>
<ProUIFeedbacks
onFeedbackSubmit={(fb) => feedbacks.current.push(fb)}
onCopy={() => {
const data = feedbacks.current.map((fb) => ({
step: fb.stepNumber,
note: fb.content,
selector: fb.selector,
}))
navigator.clipboard.writeText(JSON.stringify(data, null, 2))
}}
/>
</>
)
}
// Auto per-page key (based on pathname)
<ProUIFeedbacks persist />
// Custom storage key
<ProUIFeedbacks persist="my-review-session" />
When persist is enabled, feedbacks are saved to localStorage and restored on page reload. If a target element can no longer be found in the DOM, its marker is displayed as an orphan with a warning indicator.
import { Bug } from 'lucide-react'
<ProUIFeedbacks triggerIcon={<Bug size={18} />} />
React Package:
Embeddable Script:
git clone https://github.com/bienhoang/pro-ui-feedbacks.git
cd pro-ui-feedbacks
npm install
npm run dev # watch mode
npm run build # production build
npm run typecheck
FAQs
Annotate any website, generate structured visual feedback for AI agents — works as React component or standalone embed
We found that pro-ui-feedbacks demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.

Security News
Federal audit finds NIST lacked a plan to clear the NVD backlog, wasted funds on duplicate work, and delayed use of CISA data.