@pixelmatters/markup 1.20.0 → 1.22.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/README.md CHANGED
@@ -12,7 +12,7 @@ Pin-anchored feedback for live web apps. Drop in a script tag and your stakehold
12
12
  - **Threads, in real time.** Replies stream in via WebSocket. Per-comment edit, delete (with tombstones), and emoji reactions.
13
13
  - **@-mentions.** Typing `@` in the composer opens a project-member picker and inserts a chip; the `@[Name](userId)` wire format never reaches the screen. Mentioned teammates are notified, and signed-in users read those notifications from the toolbar's inbox.
14
14
  - **Annotated screenshots.** Opt-in capture with the pin marker drawn on the image and embedded fonts, so the snapshot matches what the user saw.
15
- - **Drop-in identity.** Anonymous by default, with a popup-based sign-in that survives Safari ITP and Chrome storage partitioning. Signed-in authors show their profile picture; everyone else gets initials.
15
+ - **Drop-in identity.** Anonymous by default, with a popup-based sign-in that survives Safari ITP and Chrome storage partitioning. Signed-in authors show their profile picture; everyone else gets initials. A project can be set to **members only** in the dashboard, in which case visitors are asked to sign in before commenting; everyone still sees the pins.
16
16
  - **Agent replies are labelled.** A comment written by an AI agent through Markup's MCP server carries a bot badge. It's posted under a team member's name, so the badge is the only way a visitor can tell a machine answered.
17
17
  - **Style-isolated.** Runs inside an open shadow root with `:host { all: initial }`, so host CSS can't bleed in and widget CSS can't bleed out.
18
18
  - **SPA-aware.** Patches `history.pushState` / `replaceState` and follows `popstate` to refresh threads on route changes.
@@ -37,9 +37,9 @@ CDN drop-in, no build step. Paste this just before `</body>`:
37
37
  ```html
38
38
  <script type="module">
39
39
  // Pin the exact version; esm.sh resolves it from npm
40
- import { init } from 'https://esm.sh/@pixelmatters/markup@1.20.0'
40
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.22.0'
41
41
  // or
42
- // import { init } from 'https://esm.run/@pixelmatters/markup@1.20.0'
42
+ // import { init } from 'https://esm.run/@pixelmatters/markup@1.22.0'
43
43
 
44
44
  init({
45
45
  apiUrl: 'https://your-deployment.convex.site',
@@ -50,14 +50,14 @@ CDN drop-in, no build step. Paste this just before `</body>`:
50
50
  </script>
51
51
  ```
52
52
 
53
- > **Why pin the version?** CDN URLs without a version (`@pixelmatters/markup`) resolve to whatever's `latest` on npm, so a future major release will break your page with no warning. Always pin (`@pixelmatters/markup@1.20.0`).
53
+ > **Why pin the version?** CDN URLs without a version (`@pixelmatters/markup`) resolve to whatever's `latest` on npm, so a future major release will break your page with no warning. Always pin (`@pixelmatters/markup@1.22.0`).
54
54
 
55
55
  If your platform doesn't allow inline JS (some CMS / page-builder editors), use the auto-init form instead. Point a `<script src=…>` at the bundle and pass config via `data-*` attributes:
56
56
 
57
57
  ```html
58
58
  <script
59
59
  type="module"
60
- src="https://esm.sh/@pixelmatters/markup@1.20.0"
60
+ src="https://esm.sh/@pixelmatters/markup@1.22.0"
61
61
  data-markup-widget="true"
62
62
  data-api-url="https://your-deployment.convex.site"
63
63
  data-api-key="markup_..."
@@ -207,15 +207,17 @@ Unmounts the widget and removes the host element. Safe to call when nothing is m
207
207
 
208
208
  The widget mounts a single compact pill in the corner set by `position`:
209
209
 
210
- | Control | What it does |
211
- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
212
- | **Comment** | Arms placement. The next click on the page drops a pin, and it flips to a cancel icon while armed. |
213
- | **Inbox** | Mention notifications for this project, with an unread badge. Signed-in users only; anonymous visitors don't get the button. |
214
- | **Pins** (eye) | Hides or shows every pin without hiding the toolbar. |
215
- | **Identity** | Avatar button. Anonymous: a sign-in prompt plus "Forget me on this site". Signed in: name, email, an **Account →** link when `dashboardUrl` is set, and **Sign out**. |
216
- | **Overflow** (`☰`) | **Appearance** (Light / Dark / Auto), **Position** (left / center / right), an **Auto-capture screenshots** toggle, **Privacy & data**, **Keyboard shortcuts**, **Hide for this session**, and the widget version. |
210
+ | Control | What it does |
211
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
212
+ | **Comment** | Arms placement. The next click on the page drops a pin, and it flips to a cancel icon while armed. |
213
+ | **Inbox** | Mention notifications for this project, with an unread badge. Signed-in users only; anonymous visitors don't get the button. |
214
+ | **Pins** (eye) | Hides or shows every pin without hiding the toolbar. |
215
+ | **Identity** | Avatar button. Anonymous: a sign-in prompt plus "Forget me on this site". Signed in: name, email, an **Account →** link when `dashboardUrl` is set, and **Sign out**. |
216
+ | **Overflow** (`☰`) | **Appearance** (Light / Dark / Auto), **Position** (left / center / right), an **Auto-capture screenshots** toggle, a **Show resolved threads** toggle, **Privacy & data**, **Keyboard shortcuts**, **Hide for this session**, and the widget version. |
217
217
 
218
- Appearance persists to the host page's `localStorage` (`markup:widget:theme`) and, once a user has set it, takes precedence over the `theme` option on every later `init()`. Position, pin visibility, and the auto-capture toggle are per-mount. They reset on reload, and `position` seeds the toolbar again.
218
+ Appearance persists to the host page's `localStorage` (`markup:widget:theme`) and, once a user has set it, takes precedence over the `theme` option on every later `init()`. Position, pin visibility, the auto-capture toggle and the resolved-threads toggle are per-mount. They reset on reload, and `position` seeds the toolbar again.
219
+
220
+ **Show resolved threads** is off by default. Turned on, the widget also renders the route's most recently resolved threads (up to 100) as muted check-mark pins. Opening one shows the thread read-only: no replies, edits, reactions or resolve button. Reopening still happens from the dashboard.
219
221
 
220
222
  The last two menu rows open panels anchored to the same corner as the toolbar:
221
223
 
@@ -279,7 +281,7 @@ Capture degrades instead of failing outright:
279
281
  - The widget mounts `<div id="markup-widget">` on `document.body` and attaches an open shadow root.
280
282
  - All UI lives in that shadow root, with `:host { all: initial }` blocking style inheritance.
281
283
  - The host element is `position: fixed; inset: 0; pointer-events: none`, so the widget paints over the entire viewport without blocking the host's clicks; only the toolbar and active popovers opt back in to pointer events.
282
- - Pins are anchored as `(x, y)` fractions of the document plus a best-effort CSS selector (via [`@medv/finder`](https://github.com/antonmedv/finder)). The selector wins when it still resolves; the fraction is the fallback so pins survive layout changes.
284
+ - Pins are anchored as `(x, y)` fractions of the document plus a CSS path from the nearest landmark (a stable `id`, `data-testid`, `main`, `form`, `table`, …) and the element's text. The path wins when it still resolves, relaxing from the top if a wrapper changed, and a match whose text differs is rejected; the fraction is the fallback so pins survive layout changes.
283
285
  - Live thread updates come over a WebSocket to the deployment's `*.convex.cloud` origin, which the widget derives from `apiUrl`. Everything else, meaning comments, identity, screenshots and error reports, goes to `*.convex.site` over HTTP.
284
286
  - Identity lives in **host-page** `localStorage` under `markup.identity`, keyed to the top-level site. On first load the widget mints a server-signed anonymous JWT via `POST /widget/anon-identity` so the backend can verify the `authorClientId` on every anon write. Tampering with the cached `clientId` invalidates the signature. Verified identities upgrade to a `Bearer` JWT via the popup flow described below.
285
287
 
@@ -370,7 +372,7 @@ For a `<script>` tag drop-in (no bundler), use the inline ESM form and **pin the
370
372
 
371
373
  ```html
372
374
  <script type="module">
373
- import { init } from 'https://esm.sh/@pixelmatters/markup@1.20.0'
375
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.22.0'
374
376
 
375
377
  init({
376
378
  apiUrl: '...',
@@ -386,7 +388,7 @@ If inline JS is disallowed (some CMS / page-builder editors), use the auto-init
386
388
  ```html
387
389
  <script
388
390
  type="module"
389
- src="https://esm.sh/@pixelmatters/markup@1.20.0"
391
+ src="https://esm.sh/@pixelmatters/markup@1.22.0"
390
392
  data-markup-widget="true"
391
393
  data-api-url="..."
392
394
  data-api-key="..."