Comments
How visitors pin comments on elements, regions and text, and how threads, resolving and deep links work.
Comment mode is the Figma-style half of the widget: point at something on the live page and say what's wrong. Every comment is anchored to the element it was left on (Anchoring), so it stays in place across scrolling, breakpoints and redesigns.
Leave a comment
Open the launcher and choose Create new annotation, or press C. The page is covered by a transparent layer, so clicks and drags never trigger your own handlers or select text, and a hint bar shows the controls.
| Gesture | Result |
|---|---|
| Click | A pin on the element under the cursor, at the exact point clicked. |
| Drag (more than 5 px) | A region: a rectangle anchored to the smallest element that contains all of it. Regions under 8 × 8 px count as a click. |
| Hold Shift | Target the parent of the hovered element. |
| Arrow keys, then Enter | Walk the DOM without a mouse: ↑ parent, ↓ first child, ← → siblings; Enter picks the outlined element. |
| Esc | Leave comment mode. |
| Touch: long-press, then drag | Long-press (≈ 0.5 s) to pin; keep the finger down and drag to select a region. Before the long-press fires, a swipe still scrolls the page. |
The composer opens next to the pin or region and stays on screen (as a bottom sheet below 640 px). Visitors
pick a type — Question, Change, Bug or Other (stored as the kinds question, idea, bug and
comment; with no type picked it is sent as Other) — write their message (up to 10,000 characters; the box
grows with the text) and, unless you identified them, can leave a name and email. The name and email are
remembered in the browser for next time. Send stays disabled until there is some text.
Ctrl/⌘ + Enter sends. Pressing Esc on a composer with text asks for a second Esc before discarding it, and clicking the page outside only closes an empty composer.
The pin appears immediately and gets its number (#142) when the server confirms. If sending fails, the
composer reopens with the text, so nothing is lost.
Regions follow the layout
A region is stored as percentages of its element's box, not as pixels, so it stretches with the element at every breakpoint.
Comment on selected text
Select text on the page, then enter comment mode — or, with right-click commenting on, right-click the selection — and the widget offers Comment on selection. The quote is stored with the comment and highlighted on the page whenever that text is still there; the thread shows it as a blockquote.
Right-click
With rightClickComment enabled in the project settings, a plain right-click on the page opens the composer
at the cursor. Right-clicks with a modifier key, on form fields, or from the keyboard context-menu key keep the
browser menu. On a text selection the browser menu stays too (people usually want Copy) and the widget
offers Comment on selection next to it.
Comment on the whole page
Some feedback isn't about one spot ("the page loads slowly", "the tone is off"). Pinwren.open("page") opens the
composer without a pin; the comment is listed in the sidebar and the dashboard as Whole page. Bug reports
are page-level too.
Attachments, screenshots and metadata
- Attach (the paper clip in the composer) adds up to three images or PDFs, 5 MB each, like a bug report.
- With Screenshot with every comment on (project Capture tab), every new comment also sends a screenshot of what the commenter saw, masked like a bug report's, plus the viewport it shows, so the team sees the comment in context and Page review can place it. The comment never waits more than a few seconds for it: if the capture is slow or fails, the comment goes without one.
- Whatever your code passed to
Pinwren.setMetadata()goes with it.
When sending fails
Messages are in the visitor's language and say what to do: offline, too slow, too large (remove an attachment), too many at once (wait a minute), or that the site isn't accepting feedback. A request gets 30 seconds (90 for a report with files) before it counts as timed out. When the cause is temporary (offline, a timeout, a server error), the comment is kept in the browser's outbox and sent by itself when the connection is back; its marker stays on the page, marked as waiting.
The launcher
The launcher is a speed dial. Its badge counts the open items on the page; pressing it fans out labelled actions:
| Action | What it does |
|---|---|
| Hide / Show markers | Takes the markers off the page and brings them back. Remembered per visitor and project. |
| Create new annotation | Starts comment mode (C). |
| Report a bug | Opens the bug report (B), when bug reports are on. Notes an unsent draft. |
| Show sidebar | Opens the sidebar with every item, with the open count. |
It is a menu button: Enter or Space opens it with focus on the first action, the arrow keys (and Home / End) move, Esc closes it and puts focus back on the launcher. A report waiting to send (offline) adds a reports waiting to send action that retries it.
Markers on the page
When public read is on for the project, the widget places the current page's items as numbered circles coloured by type — blue for questions, orange for changes, red for bugs, grey for other. Resolved items stay as a muted check, so a review shows what is done. Regions are outlined areas and text comments highlights. Hovering a marker shows a preview (type, how long ago, the first lines) and outlines the element it is pinned to; clicking opens its thread.
When public read is off (the default for new projects), visitors see no one else's feedback — but a visitor still
sees their own items and the team's public replies on them: the browser keeps the reporter token it got for
each item and asks for those (GET /w/mine). View comments and the sidebar's Mine filter work the same
way.
A pin whose element was found with lower confidence (the page changed) is marked Moved?; the team can re-pin it. Items whose element is gone entirely are listed in the sidebar instead of being dropped, and page-level items are labelled Whole page.
Threads
A thread card shows the original comment, its public replies (team members get a Team badge; internal notes from the dashboard never reach the page) and, if the project defines them, panel actions.
- Reply — type in the box at the bottom; Ctrl/⌘ + Enter sends. Replies share the report rate limit.
- Resolve / Reopen — allowed when the project lets visitors resolve (
visitorsCanResolve, off by default), or always for the browser that created the item: the server hands that browser a one-time reporter token, kept inlocalStorage. Resolved items stay on the page as a muted check marker. - Copy link — copies a deep link to the thread.
In read-only mode, threads are visible but replying and resolving are hidden.
Deep links
Any page URL with ?pinwren=<feedback id> opens with that thread showing: the widget scrolls to the pin,
pulses it and opens the card. If the item is resolved, the widget looks it up among closed items; if its
element is gone, it opens the sidebar with the thread. The page's own query string and hash are kept.
https://example.com/pricing?plan=pro&pinwren=7b0c5c1e-…A plain deep link only shows what the visitor may see anyway. The dashboard's Open on page and the links in
reporter emails add a signed token, &pwt=<token>: it opens that one item and its public replies even when the
project keeps pins private, and lets the reader reply. Links from the dashboard last 7 days, those in reporter emails 30; an expired or altered one shows
"This link has expired or the item was removed". The deep-link parameters (pinwren, pwt,
pinwren_review) never become part of a page's address.
Sidebar
Show sidebar opens the list of feedback, including items not placed on the page.
- Stats: how many items are open and resolved, how many are bugs, and a resolved progress bar.
- Filters: search; Type (Question, Change, Bug, Other); All / Open / Resolved; This page / All pages (every page of the project, each card showing its page); Mine (items sent from this browser).
- Sort and group: Newest first / Oldest first and By page.
- Cards show the number, type, status, how long ago and the text, with a coloured bar for the type. Clicking a card scrolls to its marker, flashes it and opens the thread; a card from another page opens that page with the thread showing (deep link).
- Resolve / Reopen appears where the visitor may change the status (see Threads).
- Delete appears only on the visitor's own items: the browser that sent an item can delete it with its reporter token. The widget asks first; the item goes to the dashboard's trash, where an Admin can restore it.
- Select all and the card checkboxes resolve or delete several items at once; Delete all deletes the visitor's own items in the current list.
- Export downloads the cards currently listed as CSV or JSON, built in the browser from data the widget already has. Columns: number, kind, status, title, body, author, created, page URL and deep link. The CSV opens cleanly in Excel (UTF-8 with BOM) and neutralises cells that a spreadsheet would run as a formula.
Below 640 px the sidebar goes full screen.
Keyboard shortcuts
| Key | Where | Action |
|---|---|---|
| C | Page | Start (or stop) comment mode |
| B | Page | Report a bug |
| Esc | Anywhere | Close the card, menu, offer or panel |
| ↑ ↓ | Launcher | Open it, then move between actions |
| Ctrl/⌘ + Enter | Composer, reply | Send |
Shortcuts never fire while focus is in a text field — yours or the widget's — or with
Ctrl/⌘/Alt held. Turn them off with the project's shortcuts setting.
Accessibility
Every control is a real button reachable by keyboard. Cards and dialogs trap focus and give it back to whatever opened them; markers carry labels such as “Other 3 by Maria: Logo too small”; toasts are announced through a live region; motion is reduced when the visitor asks for it.