overplay 4.0.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,194 @@
1
+ # Changelog
2
+
3
+ ## 4.0.0 — 2026-10-17
4
+
5
+ The package and code now use the Overplay name everywhere. This release has breaking changes.
6
+
7
+ - New npm package: `overplay`. Run `npm uninstall @commentwidget/core` and `npm install overplay`. The old package gets no more releases.
8
+ - New API names: `initOverplay()` replaces `initCommentWidget()`. The script-tag global is `Overplay` (`Overplay.init()`), not `CommentWidget`. The types are `OverplayConfig` and `OverplayAPI`. The old names are removed.
9
+ - New built files: `dist/overplay.esm.js`, `dist/overplay.umd.js` and `dist/overplay.iife.js`.
10
+ - New anchor attribute: use `data-overplay-anchor` in place of `data-comment-anchor`. Pins saved with the old attribute in their selector no longer find their component until you update your markup.
11
+ - New browser storage keys (`overplay:*`). Widget settings, the saved name, launcher position and filters reset once. Local-only notes from the default store are not carried over; pass `new LocalStorageAdapter('comment-widget:<projectId>:comments')` to keep reading them. Hosted users sign in again once.
12
+ - The widget host element has `data-overplay` in place of `data-comment-widget`.
13
+ - Webhooks: the headers are now `X-Overplay-Event`, `X-Overplay-Delivery`, `X-Overplay-Timestamp` and `X-Overplay-Signature`. The signature scheme is unchanged. Update receivers that read the old `X-CommentWidget-*` headers.
14
+ - Account exports use the format label `overplay-export`.
15
+ - The source repository moves to `github.com/artemsvit/Overplay`. Old links redirect.
16
+
17
+ ## 3.13.0 — 2026-10-17
18
+
19
+ - New name and look: the product is now **Overplay**. A new design system inspired by dock.us: one blue accent (`#0068f9`), near-black ink, cool gray surfaces, the Inter font (self-hosted), pill buttons, hairline borders and large rounded page panels.
20
+ - Website: the landing hero is a blue panel with white type; the product preview sits in a gray dotted stage. Section headings are larger. Eyebrow labels use sentence case.
21
+ - Dashboard: quieter sidebar with a gray active row, ink tabs, and accent text that stays readable in dark mode.
22
+ - Widget: pill buttons, the new blue as the default `primaryColor`, and a near-black floating button. The Blue pin color is now the brand blue.
23
+ - Emails, the sign-in window, dialogs, the link preview image, the touch icon and the email logo use the new brand.
24
+ - The logo is the comment pin with a ring inside. Icon buttons are circles. Tabs and segmented controls are pills; the widget's Open / Resolved / All tabs fill the panel width.
25
+ - Cloud: the old address `comments.artsvit.com` only redirects to `https://overplay.app` (301, or 308 for requests that send data). Sign in again on overplay.app, and reconnect MCP clients at `https://overplay.app/mcp`. Account emails come from `accounts@mail.overplay.app`. The widget calls `https://overplay.app` when `serverUrl` is the old address.
26
+ - The landing hero keeps its dot grid and soft glow, without a frame. Dashboard rows show no gray hover; the sidebar uses Title Case and Upgrade Plan is a button.
27
+ - Teams: the account owner creates teams, adds people by username, makes some of them team admins and gives each team chosen projects. Team members get access to those projects; team admins manage them. Each person uses one seat per project.
28
+ - MCP Server page: setup first, then Project Access, then **Connections** and **Recent Activity** tabs. Recent Activity is a log of who used MCP, with which agent, in which project, and the result. Owners and admins see everyone's agent actions in their projects.
29
+ - Cloud: MCP is part of Pro and Max. Free accounts see an upgrade banner; their connections are kept and work again after an upgrade. Community keeps MCP for everyone.
30
+ - Cloud: Free now allows 2 members per project. Plan cards list MCP, and the Yearly switch shows **2 Months Free** with the price per month.
31
+ - Widget: the comment menu holds only **Delete Comment**. Where a comment opens stays a Settings choice.
32
+
33
+ ## 3.12.0 — 2026-10-16
34
+
35
+ - Search and filters in the widget panel. Search finds words in a note, its replies, the people who wrote them and the name of its element, and ignores case and accents. Matches are marked, and a row shows the reply or element that matched. Filter by **Open**, **Resolved** or **All**, by **Mine**, or by an author from the list. **This Page** and **All Pages** count what each view shows. **Clear Filters** brings the list back when no notes match. Escape clears the search, and **/** moves to it from anywhere in the panel. Pins on the page follow the filters. The filters stay while the browser tab is open.
36
+ - Website: a public **Changelog** page at `/changelog`, built from this file. Every page footer links to it.
37
+ - Website: on phones, a menu button in the header opens How it works, Pricing, Docs and Sign in (or Dashboard). Press Esc to close it.
38
+ - Dashboard: new accounts see a **Get started** checklist on the Projects page: create a project, install the widget, leave a first comment, add a member and connect an AI agent. Done steps are checked from your account data. **Hide Checklist** hides it in this browser.
39
+ - Cloud: Plan & billing shows meters for comments this UTC month, projects, and members in your largest project. The sidebar shows a compact version. At 80% and 100% of the monthly comment limit, the dashboard shows a banner and the account owner gets one email per threshold each month.
40
+ - Cloud: optional yearly billing for Pro ($150/year) and Max ($300/year), tax included. A Monthly/Yearly switch appears on /pricing and Plan & billing once the yearly Paddle prices are configured (`PADDLE_PRICE_ID_YEARLY`, `PADDLE_MAX_PRICE_ID_YEARLY`).
41
+ - Cloud: Plan & billing has a billing history with the date, plan, amount and status of each payment, and a **Download Invoice** button for the Paddle invoice PDF.
42
+ - Workspace: a new **Team** page. The account owner adds admins by username. An admin manages every project of the owner (settings, websites, members, join requests, and moving or deleting any comment) but cannot delete projects, change billing, or add and remove admins. Admins use a member seat in each project; Free has no room for admins. Admins leave a team on the same page. MCP owner tools follow the same rules.
43
+ - Workspace: a new **Activity** tab in each project, for the owner and admins. It lists member, request, website, settings, team and agent connection changes, comments deleted by someone other than their author, and agent comment actions, newest first. Events are kept for one year.
44
+ - `CommentUser.role` accepts `admin`. The widget lets an admin move and delete every comment, like the owner. Older widget versions treat an admin as a member.
45
+ - Workspace: a new **Account** page. Turn on two-factor authentication with an authenticator app (TOTP) and 10 one-time recovery codes; sign-in then asks for a code after the password. Download your data as one JSON file. Delete your account and everything you own in one step (blocked while a paid subscription is active).
46
+ - New **Security** page (`/security`) and `/.well-known/security.txt`, linked from every footer. The Privacy Notice covers export, deletion and two-factor data.
47
+ - Self-hosting: optional `TWO_FACTOR_KEY` encrypts two-factor secrets; `community/disable-two-factor.ts` turns two-factor off for a locked-out user.
48
+ - Project integrations in **Settings → Integrations**: Slack messages, signed webhooks (HMAC-SHA256), and issues in GitHub, Linear or Jira, created by hand with **Create Issue** or for every new comment. Tokens stay on the server and are encrypted when `INTEGRATIONS_KEY` is set. Each integration shows its last delivery and has **Send Test**. Migration `0014_notifications.sql` is required. See [Integrations](docs/INTEGRATIONS.md).
49
+ - Cloud: comment email per member and project in the new **Notifications** tab: instant, a daily digest from the daily cron, or off (the default).
50
+
51
+ ## 3.11.0 — 2026-10-16
52
+
53
+ - Workspace: the **Members** list shows each member's email under their name, in place of "Can comment" and "You". Only the project owner sees this list. MCP `list_members` still leaves emails out.
54
+ - The project owner can move and delete every comment in the project, in the widget and through the service. Members still move and delete only their own comments. Only the author can change a comment's text, and MCP agents keep the author-only rule. `CommentUser` has an optional `role` (`"owner"` or `"member"`); the hosted service sets it.
55
+
56
+ ## 3.10.1 — 2026-10-16
57
+
58
+ - Workspace: the **MCP server** page has a new design. One card shows the server switch, its address and three steps to connect an agent. Each connection shows its status, projects and permissions at a glance, with **Edit Access** and a **New Token** button. Project access and recent activity use the same rows as the rest of the workspace. The sidebar shows the MCP logo.
59
+ - A note on an item in a closed menu now shows **Element hidden**, not **Element removed**. Many menus (Radix, Headless UI, MUI, Vue) take closed items out of the page. A note that saved how its menu opens counts as hidden while the menu's button is on the page. It shows **Element removed** only when that button is gone too, or opening the menu does not bring the item back.
60
+ - MCP `list_comments` and `search_comments` accept `includeReplies` sent as text (`"true"`), as agents with an older tool list do.
61
+ - MCP `remove_member` says "Member not found." for a user who is not in the project. Before, it reported success.
62
+
63
+ ## 3.10.0 — 2026-10-16
64
+
65
+ - MCP `list_comments` and `search_comments` return each comment's reply count and last reply time. Set `includeReplies` to get replies in the same call. Omit `projectId` to read every included project at once.
66
+ - MCP `get_project` and `list_projects` show whether a project allows only its listed websites.
67
+ - Saved element selectors no longer start from IDs that frameworks make at run time, such as Radix (`radix-_r_3p_`) or React `useId`. These IDs change on each page load.
68
+ - The MCP connect window is compact and shows only the request. **Authorize** and **Cancel** stay visible at the bottom of the window. Connection forms in MCP settings keep their save button in view too.
69
+
70
+ ## 3.9.0 — 2026-10-16
71
+
72
+ - Connect coding agents through the new MCP server, with OAuth sign-in or a manual token. Select projects and permissions for each connection, pause or revoke access, and switch all account MCP access off.
73
+ - Agents can read and search comments and replies, retrieve saved element context and activity, post replies, create or edit their own pins, resolve threads, and perform separately permitted project administration.
74
+ - MCP comments and replies show the agent name. Comment links can open the exact thread, and revision checks protect against stale agent edits.
75
+ - Add an MCP section to the landing page. Cloudflare and Community use the same MCP tools and access controls.
76
+
77
+ ## 3.8.1 — 2026-10-16
78
+
79
+ - Dropping the floating **Comments** button after a drag no longer opens the sidebar. Only a click opens it. (3.8.0 broke this.)
80
+
81
+ ## 3.8.0 — 2026-10-16
82
+
83
+ - Pick the color of the floating **Comments** button in **Settings → Comments button**: the default dark button, the site color, or one of 8 colors. A live sample shows the button, also while the sidebar hides it. Each person's choice is saved per project in this browser, like the pin color. **Reset to Defaults** brings back the dark button.
84
+ - Notes in menus open the menu again. Leave a note on an item in a dropdown, hamburger menu, drawer, submenu, tab, popover or dialog. The note saves how the menu was opened, and opening the note later (from the sidebar or **Show Element**) repeats it and shows the item. It works with native HTML, CSS-only menus (`:hover`, `:focus-within`, the checkbox hack, `:target`), Bootstrap, Radix/shadcn, Headless UI, MUI, Vue and hand-written menus. Links to other pages and form submits are never repeated.
85
+ - The opened menu stays open while you read and reply. Presses and focus in the widget no longer count as a click outside the page's menu, and the widget moves into an open modal dialog or popover while its note is open.
86
+ - You can add a note in an open menu with the **Add Comment** button; the menu no longer closes first.
87
+ - An element in a closed `<details>`, in a collapsed accordion (`max-height: 0`), at opacity 0 or in a drawer moved off the screen now shows **Element hidden**. Before, these counted as shown.
88
+ - If the menu cannot open, the note says which button to press, for example *Open “Menu” to see it* (a phone menu seen at desktop width).
89
+ - Menus that hide the page from screen readers (`aria-hidden` on other elements) no longer hide the widget.
90
+
91
+ ## 3.7.0 — 2026-10-15
92
+
93
+ - A comment now says what happened to its element. **Element hidden** names the element and the menu, tab or section that holds it, such as *Checkbox “Peanuts” in Filters*, with **Show Element**. **Element removed** says nothing on the page matches it now. Both show a dashed pin where the note was left; drag the pin to attach the note again. The list carries the same flag.
94
+ - Each note saves a readable name for its element, so people who did not write the note still know which element it is.
95
+ - A dragged pin hides its comment card and outlines the component under it, the same as placing a new note.
96
+ - The update notice has a clearer design: the new version, your version and the install command with **Copy**. **×** hides it until the next version.
97
+ - A click that a script makes on the page, such as **Show Element** opening a menu, no longer closes the open comment card.
98
+
99
+ ## 3.6.0 — 2026-10-15
100
+
101
+ - Dark theme. The panel, comment cards, menus, notices and the launcher have a dark version. Choose **Light**, **Dark** or **System** in **Settings → Appearance**. System follows the device.
102
+ - New `theme` option (`'light'`, `'dark'` or `'system'`, default `'light'`) sets the site's default. New `setTheme()` changes it later, for example when your site switches to dark mode. A theme a person picks in Settings still wins.
103
+ - A comment whose element is hidden opens it: closed `<details>`, popovers, menus and accordions (a button with `aria-controls` and `aria-expanded="false"`) and tabs. New `revealComponent(comment, element)` hook lets a site show it itself, for example zoom a map to a marker. Set `autoReveal: false` to turn the automatic part off.
104
+ - Each comment card in the list has the number at the right and the status under it, so cards line up and nothing moves when counts change.
105
+ - The pick outline is a plain stroke, without a label or fill.
106
+ - 3 more pin colors: Violet, Amber and Red.
107
+ - Buttons use Title Case, for example **Add Comment** and **Post Comment**.
108
+ - Comment fields hide their scrollbars.
109
+ - Page names in "All Comments" use the page's main heading when the document title is the same generic name on every route, and show the path next to the name.
110
+
111
+ ## 3.5.0 — 2026-10-15
112
+
113
+ - The panel header holds your account and **Settings**, next to close. Your picture (or initials) opens a menu with your name, **Edit your name** for guests, **Settings** and **Sign out**. The bottom bar is gone; it shows only when a new version is available.
114
+ - **Settings** are per project and per person, saved in this browser. Choose the pin color, green or gray resolved pins, regular or small pins, numbers or author initials on pins, where comments open (next to the pin or in the sidebar), whether the panel pushes the page aside, activity notices and the keyboard shortcut. **Reset to defaults** goes back to the site's configuration.
115
+ - Site options such as `pushContent`, `showNotifications` and `enableKeyboardShortcuts` are now the defaults that each person can change.
116
+ - Avatars use the first letters of the first and last name, for example "AS".
117
+
118
+ ## 3.4.1 — 2026-10-15
119
+
120
+ - The comment card matches the website demo. The header shows the note number and "Open conversation" or "Resolved conversation", with close and a **⋯** menu. The menu holds **Open in the sidebar** and **Delete comment**.
121
+ - The open card ends with one reply line and **Resolve**. Once you type, the button becomes **Send reply**. Enter sends, and Shift+Enter starts a new line. A resolved card shows **Resolved** and **Reopen conversation**.
122
+ - Pins stay on screen. A dragged pin cannot leave the screen or go under the sidebar. A pin at the top edge points up from below its spot, and a pin at a side edge slides in.
123
+ - The new comment field has space above it again.
124
+
125
+ ## 3.4.0 — 2026-10-15
126
+
127
+ - A comment opens in a card next to its pin, beside the component it is about. The card has the note number, its status, the messages, **Resolve** or **Reopen**, and a reply field. A new comment opens in the same card.
128
+ - Prefer the sidebar? The dock button in the card's header opens the comment in the sidebar instead. The widget remembers the choice in this browser. The same button in the sidebar brings the card back next to the pin.
129
+ - A click on the page closes the card, unless it holds text you have not sent. Escape still closes it.
130
+ - Picking works like a browser inspector: the outline is solid, and a label names the element, for example `td · Line circuit breaker`.
131
+ - A `data-comment-anchor` region that covers more than a quarter of the screen, such as a whole dashboard or app frame, no longer takes every click. The row, cell or control inside it gets the note. Smaller anchors, such as a card, still win.
132
+ - Paths for elements inside a unique `data-comment-anchor` start at that anchor, so they survive layout changes outside it.
133
+ - On phones the card is a bottom sheet.
134
+
135
+ ## 3.3.0 — 2026-10-15
136
+
137
+ - A click now attaches the note to the element under the pointer: the button, link, field, heading, paragraph, image or icon. Before, any parent with an `id` (such as `#root`, `#__next` or `#main`) took the click, so many notes attached to the whole page frame.
138
+ - Text inside a paragraph or heading attaches to that paragraph or heading. Icon parts attach to the whole icon. Explicit `data-comment-anchor` keys still win.
139
+ - While you place a note, a dashed outline shows the element the note will attach to.
140
+ - A click on empty page space no longer fails with "Choose a visible page element". The note attaches to the page.
141
+ - Comment cards in the sidebar are more compact. The note number and status sit on the right of the author line. Text shows two lines.
142
+ - Hover or focus a comment in the sidebar to find it on the page: its pin pulses and its element gets a dashed outline.
143
+ - **All comments** groups other pages by their page title. A comment saves `pageTitle` (the page's `document.title`). Older comments without a title still show the path.
144
+ - Removed the "Feedback that stays in context." subtitle. Error messages have space above them again.
145
+ - Hosted service: new migration `0007_comment_page_title.sql` stores the page title.
146
+
147
+ ## 3.2.0 — 2026-10-15
148
+
149
+ - Drag a pin to move it. The **Move pin** button and its placement step are gone. A short click still opens the comment. `canMove` still decides who can drag.
150
+ - The open comment has a clearer header: a "Comments" back link and close on one bar, then the note number with an Open or Resolved status chip.
151
+ - While a comment is open, the other pins on the page fade to 30%. Hover a pin to see it at full strength.
152
+ - **Resolve** has a green check and no longer wraps. Delete asks for confirmation with **Keep** and a red **Delete** button.
153
+ - The sidebar shows **New update available** next to your name when npm has a newer version. A browser cannot update a site's code, so a click copies `npm install @commentwidget/core@latest`. By default only development hosts (`localhost`, `*.local`, `*.test`) check, at most every 12 hours. Set `updateNotice: true` or `false` to change this.
154
+
155
+ ## 3.1.0 — 2026-10-15
156
+
157
+ - Comments open in a full-height sidebar instead of a small pop-up. Threads, replies and new comments use the whole sidebar, and the floating button hides while it is open, so nothing covers the comment field.
158
+ - The open sidebar pushes the page aside instead of covering it, so you can still comment on every part of the page. Set `pushContent: false` to keep it above the page. On phones it fills the screen.
159
+ - Drag the floating comment button anywhere on the screen. The widget remembers the place in this browser. A short press still opens the sidebar.
160
+ - The sidebar has two views: **Page comments** (this page) and **All comments** (every page of the site, grouped by page). Select a comment from another page to go to that page. The sidebar opens there with the comment, and its pin and component are highlighted.
161
+ - A clearer refresh icon, shown as an outlined button next to **Add a comment**.
162
+ - Hosted service: `GET /api/comments` without `pageUrl` returns the comments of every page in the project. The widget uses it for **All comments**. Deploy the service together with the widget.
163
+ - `hideSidebar: true` now shows the sidebar only for an open thread or a new comment, with no list.
164
+
165
+ ## 3.0.0 — 2026-10-15
166
+
167
+ This is a major version because two defaults change for existing sites: the toggle shortcut, and the page ID for URLs with in-page anchors. To keep 2.x behavior, set `keyboardShortcut: 'c'` and a `getPageId` function that keeps the hash. Update install commands from `@^2` to `@^3`.
168
+
169
+ - The overlay no longer flashes when comments refresh. The panel and thread keep their place, and only the refresh icon spins. A background check that finds no change does not redraw anything.
170
+ - When someone else adds a comment or reply, a short notification appears near the comment button. It has no actions and hides after 7 seconds. Set `showNotifications: false` to turn it off.
171
+ - The default toggle shortcut is now Alt+Shift+C (Option+Shift+C on Mac) instead of the single key C, so it is not triggered by accident (WCAG 2.1.4). `keyboardShortcut` accepts modifier combinations such as `ctrl+shift+k`. Set `keyboardShortcut: 'c'` to keep the old behavior.
172
+ - The default page ID no longer includes in-page anchors such as `#pricing`, so pins stay visible after anchor links. Hash routes such as `#/settings` remain separate pages. Local notes saved with an anchor still show. Hosted notes saved with an anchor are stored under the old page ID; set `getPageId` to keep the previous behavior.
173
+ - Searching comments and background updates no longer rebuild text fields while you type, so IME input, undo history and screen reader focus are kept. Errors are announced once.
174
+ - The widget no longer fails to start on plain-HTTP pages outside localhost. It no longer needs `crypto.randomUUID`, which browsers offer only on HTTPS.
175
+ - Placement instructions now explain the keyboard method: Tab to an element and press Enter.
176
+ - Hosted sign-in survives network errors and HTML error pages from proxies. A 403 from an author-only action no longer signs the user out.
177
+ - Community server: set `TRUSTED_PROXY=1` behind a reverse proxy so rate limits apply per client (`compose.yaml` sets it). Request bodies are read before the request is queued, so a slow upload no longer blocks other requests.
178
+
179
+ ## 2.0.0 — 2026-10-15
180
+
181
+ - Replaced the React overlay with a framework-independent widget and isolated Shadow DOM styles. No runtime dependencies or separate CSS import are required.
182
+ - Added hosted account sign-in and shared comments through `serverUrl` and a dashboard project ID. Custom REST and storage adapters remain available.
183
+ - Attached pins to stable page components with relative positions. Pins follow responsive layouts, resizing, remounts and scrolling. Authors can move an existing pin without replacing the comment.
184
+ - Increased text sizes and reduced the floating launcher to an icon and comment counter.
185
+ - Added stable guest identities, author permissions, idempotent mutation retries and cross-tab local storage synchronization.
186
+ - Kept drafts after save failures and added cleanup, route handling, safe navigation and SSR-safe imports.
187
+
188
+ ### Migration from 1.x
189
+
190
+ - Initialize with `initCommentWidget(config)` after the page mounts, and call `destroy()` when it unmounts.
191
+ - The widget no longer requires React/ReactDOM. Remove the old separate widget CSS import.
192
+ - Use the hosted service, `APIAdapter`, or a working custom adapter instead of the former Firebase/Supabase placeholder adapters.
193
+ - `usePortal` is deprecated. The overlay always uses an isolated portal; `container` restricts pin placement.
194
+ - Existing local storage keys can be retained by explicitly constructing `LocalStorageAdapter` with the old key.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Overplay contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ <p align="center">
2
+ <img src="service/public/logo.svg" width="56" height="56" alt="Overplay logo">
3
+ </p>
4
+
5
+ <h1 align="center">Overplay</h1>
6
+
7
+ <p align="center">
8
+ Leave comments on any element of your website. Your team sees them in context.
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/overplay"><img src="https://img.shields.io/npm/v/overplay?color=575ce5" alt="npm version"></a>
13
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-575ce5" alt="MIT license"></a>
14
+ <a href="https://overplay.app/"><img src="https://img.shields.io/badge/cloud-overplay.app-575ce5" alt="Cloud"></a>
15
+ </p>
16
+
17
+ <p align="center">
18
+ <a href="https://overplay.app/">Website</a> ·
19
+ <a href="https://overplay.app/docs">Docs</a> ·
20
+ <a href="docs/API.md">API reference</a> ·
21
+ <a href="docs/SELF_HOSTING.md">Self-host</a> ·
22
+ <a href="CHANGELOG.md">Changelog</a>
23
+ </p>
24
+
25
+ ---
26
+
27
+ ## Features
28
+
29
+ - **Pins on components.** A note attaches to the element you click. It follows that element across layouts, resizes and re-renders.
30
+ - **Notes in menus.** Leave a note on an item in a dropdown, hamburger menu, drawer, tab or popover. When someone opens the note later, the widget opens that menu again and shows the item. It works with any frontend: native HTML, CSS-only menus, Bootstrap, Radix/shadcn, Headless UI, MUI, Vue and hand-written menus.
31
+ - **Full-height sidebar.** Read and reply to threads beside the page. Switch between **Page comments** and **All comments** for the whole site.
32
+ - **Draggable launcher.** Move the comment button anywhere on the screen. Drag a pin to move a note.
33
+ - **Team access.** Only project members can read and write. A new person who signs in sends a join request. The owner approves or declines it.
34
+ - **Integrations and email.** Send comments to Slack or a signed webhook, and create issues in GitHub, Linear or Jira. Members choose instant email, a daily digest or none. See [Integrations](docs/INTEGRATIONS.md).
35
+ - **AI Install.** The dashboard gives you a ready prompt. Paste it into Claude Code, Cursor, Copilot or another coding agent, and it installs the widget for you.
36
+ - **No dependencies.** One script, no React and no CSS import. The UI lives in a Shadow DOM, so it does not change your page styles.
37
+
38
+ ## Quick start
39
+
40
+ 1. Create a free account at [overplay.app](https://overplay.app/) and create a project.
41
+ 2. Open **Install** in the project. Use **AI Install**, or follow the manual steps below.
42
+ 3. Open your site, sign in through the widget and leave a comment.
43
+
44
+ ### npm
45
+
46
+ ```sh
47
+ npm install overplay
48
+ ```
49
+
50
+ ```ts
51
+ import { initOverplay } from 'overplay';
52
+
53
+ const widget = initOverplay({
54
+ serverUrl: 'https://overplay.app',
55
+ projectId: 'YOUR_PROJECT_ID',
56
+ });
57
+
58
+ // Call widget.destroy() when the page unmounts.
59
+ ```
60
+
61
+ ### Script tag
62
+
63
+ ```html
64
+ <script src="https://overplay.app/widget.js"></script>
65
+ <script>
66
+ Overplay.init({
67
+ serverUrl: 'https://overplay.app',
68
+ projectId: 'YOUR_PROJECT_ID',
69
+ });
70
+ </script>
71
+ ```
72
+
73
+ Place the script tag at the end of `<body>`, after your page content.
74
+
75
+ The project ID is public. It grants no access by itself. On a new website address, a project member approves that address once at sign-in.
76
+
77
+ **Tip:** add a stable `data-overplay-anchor` to your main components. Pins then stay on the right component across layouts.
78
+
79
+ ```html
80
+ <section data-overplay-anchor="pricing">…</section>
81
+ ```
82
+
83
+ ### Without an account
84
+
85
+ Omit `serverUrl` to keep notes in this browser only (localStorage). This is good for prototypes and private notes. You can also connect your own backend with the REST adapter or a custom storage adapter. See the [API reference](docs/API.md).
86
+
87
+ ## Pricing
88
+
89
+ | | Community | Free | Pro | Max |
90
+ | --- | --- | --- | --- | --- |
91
+ | Price | Free, self-hosted | $0 | $15 / month | $30 / month |
92
+ | Comments and replies per month | No limit | 10 | 1,000 | 5,000 |
93
+ | Projects | No limit | 1 | 5 | 25 |
94
+ | Members per project | No limit | 1 | 5 | 25 |
95
+ | Hosting | Your server (Node.js or Docker) | Cloud | Cloud | Cloud |
96
+
97
+ Cloud prices are in USD, before tax. Max has five times the Pro limits. Reading, moving and resolving comments is free. See [pricing](https://overplay.app/pricing).
98
+
99
+ ## Self-host (Community)
100
+
101
+ Run the same service on your own server with Node.js 24+ or Docker and SQLite. There are no plans, quotas or license keys. You manage hosting, backups and updates. The MCP server for AI agents runs on your server too: add `https://your-server/mcp` as a remote MCP server (Claude connectors need HTTPS).
102
+
103
+ ```sh
104
+ docker compose up --build -d
105
+ ```
106
+
107
+ Read the [self-hosting guide](docs/SELF_HOSTING.md).
108
+
109
+ ## Keyboard
110
+
111
+ | Key | Action |
112
+ | --- | --- |
113
+ | <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>C</kbd> (<kbd>Option</kbd> on Mac) | Open or close the widget |
114
+ | <kbd>Esc</kbd> | Cancel |
115
+ | <kbd>Enter</kbd> | Place a pin on the focused element |
116
+
117
+ ## Development
118
+
119
+ ```sh
120
+ npm ci
121
+ npm run dev # demo page with local storage
122
+ npm run typecheck
123
+ npm run lint
124
+ npm test
125
+ npm run build
126
+ ```
127
+
128
+ `npm run test:reveal` checks notes in menus in a real browser on 24 kinds of menus. It needs the `playwright`
129
+ package and Chromium (`npm i --no-save playwright && npx playwright install chromium`).
130
+
131
+ The hosted service (Cloudflare Worker + D1) is in [`service/`](service/README.md).
132
+
133
+ ## License
134
+
135
+ [MIT](./LICENSE)
136
+
137
+ ## Connect a coding agent
138
+
139
+ Overplay includes an account-level MCP server for reading feedback, retrieving saved element context, replying, and resolving threads. Choose projects and permissions in **MCP server**, directly below Projects. You can switch access off at any time. See [MCP setup and permissions](docs/MCP.md).
@@ -0,0 +1,229 @@
1
+ /** A REST contract, not a hosted service. See README for server routes. */
2
+ export declare class APIAdapter implements StorageAdapter {
3
+ private config;
4
+ constructor(config: APIAdapterConfig);
5
+ private request;
6
+ loadComments(): Promise<Comment_2[]>;
7
+ saveComments(comments: Comment_2[]): Promise<void>;
8
+ upsertComment(comment: Comment_2): Promise<void>;
9
+ deleteComment(id: string): Promise<void>;
10
+ addReply(id: string, reply: Reply): Promise<void>;
11
+ setResolved(id: string, resolved: boolean): Promise<void>;
12
+ }
13
+
14
+ export declare interface APIAdapterConfig {
15
+ baseUrl: string;
16
+ /** Called per request, allowing refreshed authentication tokens. */
17
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
18
+ credentials?: RequestCredentials;
19
+ projectId?: string;
20
+ endpoints?: {
21
+ load?: string;
22
+ save?: string;
23
+ };
24
+ }
25
+
26
+ declare interface Comment_2 {
27
+ via?: string;
28
+ revision?: number;
29
+ updatedAt?: string;
30
+ id: string;
31
+ x: number;
32
+ y: number;
33
+ text: string;
34
+ author: string;
35
+ authorId?: string;
36
+ avatarUrl?: string;
37
+ timestamp: Date;
38
+ replies: Reply[];
39
+ resolved: boolean;
40
+ pageUrl: string;
41
+ /** The page's document title when the comment was made. Shown in "All comments". */
42
+ pageTitle?: string;
43
+ selector?: string;
44
+ /** Relative position inside the attached element, from 0 to 1. */
45
+ anchor?: {
46
+ x: number;
47
+ y: number;
48
+ };
49
+ /** A readable name for the attached element, saved when the note is made. It names the element after it is hidden or removed. */
50
+ element?: ElementInfo;
51
+ }
52
+ export { Comment_2 as Comment }
53
+
54
+ export declare interface CommentPosition {
55
+ x: number;
56
+ y: number;
57
+ }
58
+
59
+ /** Hosted mode supplies the account. Custom adapters use stable IDs from the host app. */
60
+ export declare interface CommentUser {
61
+ id: string;
62
+ name: string;
63
+ avatarUrl?: string;
64
+ /** "owner" and "admin" may move and delete every comment in the project; others only their own. The hosted service sets it. */
65
+ role?: 'owner' | 'admin' | 'member';
66
+ }
67
+
68
+ /** Such as { label: 'Checkbox “Peanuts”', place: 'Filters' }. `reveal` holds the steps that opened its menu when the note was made. */
69
+ export declare interface ElementInfo {
70
+ label: string;
71
+ place?: string;
72
+ reveal?: RevealStep[];
73
+ }
74
+
75
+ /** Authenticated storage for the hosted Overplay service. Tokens are scoped to one project and website origin. */
76
+ export declare class HostedAdapter implements StorageAdapter {
77
+ private projectId;
78
+ private page;
79
+ private sessionChanged;
80
+ private base;
81
+ private key;
82
+ private accessToken;
83
+ private expiresAt;
84
+ private initialized;
85
+ private pending?;
86
+ private cancelSignIn?;
87
+ user?: CommentUser;
88
+ constructor(serverUrl: string, projectId: string, page: () => string, sessionChanged: (user?: CommentUser) => void);
89
+ get signedIn(): boolean;
90
+ private clear;
91
+ private request;
92
+ private initialize;
93
+ signIn(): Promise<void>;
94
+ signOut(): Promise<void>;
95
+ destroy(): void;
96
+ loadComments(): Promise<Comment_2[]>;
97
+ subscribeToChanges(callback: (comments: Comment_2[]) => void): () => void;
98
+ saveComments(): Promise<void>;
99
+ upsertComment(comment: Comment_2): Promise<void>;
100
+ deleteComment(id: string): Promise<void>;
101
+ addReply(id: string, reply: Reply): Promise<void>;
102
+ setResolved(id: string, resolved: boolean): Promise<void>;
103
+ }
104
+
105
+ export declare const init: typeof initOverplay;
106
+
107
+ /** Call after the document body exists, or in your framework's mount effect. */
108
+ export declare function initOverplay(config?: OverplayConfig): OverplayAPI;
109
+
110
+ export declare class LocalStorageAdapter implements StorageAdapter {
111
+ private storageKey;
112
+ constructor(storageKey?: string);
113
+ loadComments(): Promise<Comment_2[]>;
114
+ saveComments(comments: Comment_2[]): Promise<void>;
115
+ upsertComment(comment: Comment_2): Promise<void>;
116
+ deleteComment(id: string): Promise<void>;
117
+ addReply(id: string, reply: Reply): Promise<void>;
118
+ setResolved(id: string, resolved: boolean): Promise<void>;
119
+ private change;
120
+ subscribeToChanges(callback: (comments: Comment_2[]) => void): () => void;
121
+ }
122
+
123
+ export declare interface OverplayAPI {
124
+ /** Initial storage load. Errors are shown in the UI and sent to onError. */
125
+ ready: Promise<void>;
126
+ destroy(): void;
127
+ show(): void;
128
+ hide(): void;
129
+ startComment(position?: CommentPosition): void;
130
+ getAllComments(): Comment_2[];
131
+ getUser(): CommentUser;
132
+ /** Call on sign-in, sign-out, or account changes. Omit to restore the saved guest. */
133
+ setUser(user?: CommentUser): void;
134
+ signIn(): Promise<void>;
135
+ signOut(): Promise<void>;
136
+ refresh(): Promise<void>;
137
+ navigateToComment(id: string): void;
138
+ /** Changes the site's default theme, for example when the page switches to dark mode. A theme the person chose in Settings still wins. */
139
+ setTheme(theme: 'light' | 'dark' | 'system'): void;
140
+ }
141
+
142
+ export declare interface OverplayConfig {
143
+ /** Namespaces local comments and guest identity. Use a stable project ID. */
144
+ projectId?: string;
145
+ /** Defaults to localStorage. Supply an adapter for shared comments. */
146
+ storage?: StorageAdapter;
147
+ /** Hosted service origin. Requires its dashboard project ID; handles account sign-in. */
148
+ serverUrl?: string;
149
+ /** Without serverUrl, omit to create a guest identity saved in this browser. */
150
+ user?: CommentUser;
151
+ primaryColor?: string;
152
+ /** Color theme of the panel, cards and notices. "system" follows the device. Defaults to "light". Each person can change it in Settings. */
153
+ theme?: 'light' | 'dark' | 'system';
154
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
155
+ enableKeyboardShortcuts?: boolean;
156
+ keyboardShortcut?: string;
157
+ /** Only allow pins inside this element. UI stays in an isolated portal. */
158
+ container?: HTMLElement | null;
159
+ hideButton?: boolean;
160
+ hideSidebar?: boolean;
161
+ /** The open sidebar pushes the page aside instead of covering it. Defaults to true. Phones always show it full screen. */
162
+ pushContent?: boolean;
163
+ /** Show "New update available" in the sidebar when npm has a newer version. Defaults to development hosts only (localhost, *.local, *.test). */
164
+ updateNotice?: boolean;
165
+ /** Show a short notification when someone else adds a comment or reply. Defaults to true. */
166
+ showNotifications?: boolean;
167
+ /** @deprecated The isolated overlay always uses a portal. */
168
+ usePortal?: boolean;
169
+ /** Override route identity, for example to ignore tracking parameters. */
170
+ getPageId?: () => string;
171
+ onChange?: (comments: Comment_2[]) => void;
172
+ onError?: (error: Error) => void;
173
+ /**
174
+ * Called when someone opens a comment, before the widget looks for its component. Use it to show what hides the
175
+ * component, for example zoom a map to a marker with a data-overplay-anchor key. Return true when you handled it.
176
+ */
177
+ revealComponent?: (comment: Comment_2, element: Element | null) => boolean | void | Promise<boolean | void>;
178
+ /**
179
+ * Open the menu, drawer, tab or popover that hides a comment's component. A note saves the presses and hovers that
180
+ * opened it, and the widget repeats them. Older notes fall back to closed <details>, popovers and ARIA menus and tabs.
181
+ * Defaults to true. False also stops recording.
182
+ */
183
+ autoReveal?: boolean;
184
+ /** UI permission only. Enforce permissions again in your backend. */
185
+ canDelete?: (comment: Comment_2, user: CommentUser) => boolean;
186
+ /** Defaults to the comment author. Enforce this permission in your backend too. */
187
+ canMove?: (comment: Comment_2, user: CommentUser) => boolean;
188
+ /** CSP nonce for the style element inside the shadow root. */
189
+ nonce?: string;
190
+ }
191
+
192
+ export declare interface Reply {
193
+ via?: string;
194
+ id: string;
195
+ text: string;
196
+ author: string;
197
+ authorId?: string;
198
+ avatarUrl?: string;
199
+ timestamp: Date;
200
+ commentId: string;
201
+ }
202
+
203
+ /** One step that opens the menu, drawer, tab or popover holding a note's element. */
204
+ declare interface RevealStep {
205
+ action: 'press' | 'hover' | 'focus';
206
+ target: RevealTarget;
207
+ }
208
+
209
+ /** An element to press or hover again: a selector, with its role and name as a second way to find it. */
210
+ declare interface RevealTarget {
211
+ selector: string;
212
+ role?: string;
213
+ name?: string;
214
+ tag?: string;
215
+ }
216
+
217
+ export declare interface StorageAdapter {
218
+ loadComments(): Promise<Comment_2[]>;
219
+ /** Legacy snapshot fallback. For collaboration, implement individual mutations. */
220
+ saveComments(comments: Comment_2[]): Promise<void>;
221
+ upsertComment?(comment: Comment_2): Promise<void>;
222
+ deleteComment?(id: string): Promise<void>;
223
+ /** Implement atomically on a backend to avoid concurrent reply overwrites. */
224
+ addReply?(commentId: string, reply: Reply): Promise<void>;
225
+ setResolved?(commentId: string, resolved: boolean): Promise<void>;
226
+ subscribeToChanges?(callback: (comments: Comment_2[]) => void): () => void;
227
+ }
228
+
229
+ export { }