, , , etc.) or a sizeable (>12,000px²)2. Composite Signatures: Each comment stores multiple fingerprints:
- Selector path (e.g.,
main > section.hero > div.container)
- Tag name
- Heading text (nearest h1-h6 or aria-label)
- Text snippet (first 60 chars of text content)
- Bounding rectangle hint
- Route key (pathname)3. Confidence-Based Resolution: When re-finding elements:
- First tries the selector path directly
- Falls back to finding candidates and scoring similarity
- Only matches if confidence ≥ 0.75
- Shows ⚠️ badge if element can't be reliably found
4. Automatic Rehydration: Listens for:
- DOM mutations (via MutationObserver)
- Route changes (Next.js navigation)
- Scroll/resize events (for repositioning)
$3
The widget now prefers selecting child components when they look substantive (block display, stable classes, meaningful text, or adequate size). If a clicked element is too small or purely structural, the selector gently bubbles up to the nearest meaningful container.
- Prefer child: Visible blocks, components with stable classes, or elements with text
- Bubble up: Tiny or inline-only elements without meaningful content
- Tip: Aim your cursor at the specific subcomponent you want; the overlay will preview what will be selected.
$3
Comments are stored in
localStorage by default, keyed as ayo-comments. Each comment includes:`typescript
{
id: string;
userName: string;
timestamp: number;
text: string;
signature: ElementSignature; // Multi-fingerprint for robust resolution
resolved: boolean; // False if element can't be found
replies?: Comment[];
}
`User names are stored separately in
sessionStorage as ayo-user-name.API
$3
Options:
-
apiEndpoint?: string - Backend endpoint for persisting comments (optional)
- enableSelection?: boolean - Enable element selection mode (default: true)
- position?: 'right' | 'left' - Bubble position (default: 'right')
- showIndicators?: boolean - Show visual indicators on commented elements (default: true)Methods:
-
destroy() - Clean up and remove the widget (removes event listeners, MutationObserver, DOM elements)
- toggleSelection() - Toggle element selection mode
- toggleIndicators() - Toggle comment indicators visibility
- getComments() - Get all comments from storage$3
Each comment can be marked as done to indicate completion. In the panel, use the "Mark Done" / "Undo Done" button on a comment. Done comments show a ✓ badge, are dimmed, and have strike-through text.
Programmatic updates via storage:
`ts
// Toggle done state
await commentStorage.updateComment(commentId, { done: true });
`Troubleshooting
$3
This happens when the element signature can't be matched with sufficient confidence (< 0.75). Common causes:
- Major layout restructuring (changed DOM hierarchy)
- Element removed from the page
- Significant text content changes
- Route mismatch (comment was on different page)
Solutions:
- Navigate to the correct page where the comment was made
- Check if the element still exists
- Delete and re-create the comment on the new structure
$3
1. Check
showIndicators is true` in options2. Verify comments exist for the current route
3. Check browser console for resolution errors
4. Ensure MutationObserver is running (widget not destroyed)
$3
The widget uses debouncing (80ms for mutations, 200ms for scroll/resize) to prevent excessive rehydration. If you have 100+ comments:
- Consider filtering by route more aggressively
- Adjust confidence threshold lower (0.70) for faster matching
- Implement backend pagination instead of storing all in localStorage
License
MIT