@pixelmatters/markup 1.31.1 → 1.31.3

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
@@ -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.31.1'
40
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.31.3'
41
41
  // or
42
- // import { init } from 'https://esm.run/@pixelmatters/markup@1.31.1'
42
+ // import { init } from 'https://esm.run/@pixelmatters/markup@1.31.3'
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.31.1`).
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.31.3`).
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.31.1"
60
+ src="https://esm.sh/@pixelmatters/markup@1.31.3"
61
61
  data-markup-widget="true"
62
62
  data-api-url="https://your-deployment.convex.site"
63
63
  data-api-key="markup_..."
@@ -304,7 +304,7 @@ Capture degrades instead of failing outright:
304
304
 
305
305
  - **An image the browser won't hand over** comes through blank, and the rest of the page still captures. A third-party avatar served without CORS headers is the usual culprit. It used to abort the whole screenshot.
306
306
  - **Icons from an SVG sprite** are fetched and inlined before the capture. The capture renders your page as an SVG document, which is not allowed to load anything external, so a `<use href="/sprite.svg#icon">` would otherwise draw nothing — on a design system that ships its icons that way, every icon in the screenshot went missing. A sprite the widget can't read (cross-origin without CORS headers, or outside your `connect-src`) leaves those icons blank and the rest captures as before.
307
- - **Captures are sized for storage, not for zooming.** The raster is capped at 1.5x device pixel ratio, so a 2x or 3x display doesn't bank detail nobody looks at in a lightbox. The image is then encoded down a ladder — quality drops first (0.85 → 0.6), then the raster shrinks (full → ¾ → ½) — until it lands under roughly 400 KB. A page that can't get there at any rung keeps the sharpest version that still fits the server's 2 MB hard cap, because a smaller sharp screenshot beats a full-size illegible one but not by any margin.
307
+ - **Captures are sized for storage, not for zooming.** The raster is capped at 1.5x device pixel ratio, so a 2x or 3x display doesn't bank detail nobody looks at in a lightbox. The image is then encoded down a ladder — quality drops first (0.85 → 0.6), then the raster shrinks (full → ¾ → ½) — until it lands under roughly 400 KB. A page that can't get there at any rung keeps the sharpest version that still fits the server's 2 MB hard cap, because a smaller sharp screenshot beats a full-size illegible one but not by any margin. A 640px-wide thumbnail of the same redacted image is uploaded alongside it for the inline preview in a thread, so the full screenshot downloads only when someone opens it.
308
308
  - **If nothing works**, the composer reads _Screenshot unavailable_ and the comment posts without one. Previously the row just disappeared, which looked identical to screenshots being switched off for the project.
309
309
 
310
310
  ## Routing
@@ -405,6 +405,7 @@ to start from `anchorSource` and confirm it, not to trust it blindly.
405
405
  - The widget mounts `<div id="markup-widget">` on `document.body` and attaches an open shadow root. It does so once the page is idle, or 2 s after `init()` on a page that never is, so `init()` returns before the element exists.
406
406
  - All UI lives in that shadow root, with `:host { all: initial }` blocking style inheritance.
407
407
  - 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.
408
+ - A modal on your page, such as a Radix or Base UI dialog, doesn't lock the widget out. Focus, wheel and touch events inside the widget are not passed on to your page, so a focus trap or scroll lock never sees them. A field of yours still gets its `focusout` when focus moves into the widget, without a `relatedTarget`. The widget also removes the `aria-hidden` or `inert` a modal puts on it, so screen readers can reach it. A Base UI popup that allows presses outside it still closes on a click in the widget, and a native `<dialog>` opened with `showModal()` makes the widget inert.
408
409
  - 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.
409
410
  - 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.
410
411
  - 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.
@@ -496,7 +497,7 @@ For a `<script>` tag drop-in (no bundler), use the inline ESM form and **pin the
496
497
 
497
498
  ```html
498
499
  <script type="module">
499
- import { init } from 'https://esm.sh/@pixelmatters/markup@1.31.1'
500
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.31.3'
500
501
 
501
502
  init({
502
503
  apiUrl: '...',
@@ -512,7 +513,7 @@ If inline JS is disallowed (some CMS / page-builder editors), use the auto-init
512
513
  ```html
513
514
  <script
514
515
  type="module"
515
- src="https://esm.sh/@pixelmatters/markup@1.31.1"
516
+ src="https://esm.sh/@pixelmatters/markup@1.31.3"
516
517
  data-markup-widget="true"
517
518
  data-api-url="..."
518
519
  data-api-key="..."