@pixelmatters/markup 1.3.11 → 1.3.12

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
@@ -35,9 +35,9 @@ CDN drop-in (no build step) — paste this just before `</body>`:
35
35
  ```html
36
36
  <script type="module">
37
37
  // Pin the exact version — esm.sh resolves it from npm
38
- import { init } from 'https://esm.sh/@pixelmatters/markup@1.3.11'
38
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.3.12'
39
39
  // or
40
- // import { init } from 'https://esm.run/@pixelmatters/markup@1.3.11'
40
+ // import { init } from 'https://esm.run/@pixelmatters/markup@1.3.12'
41
41
 
42
42
  init({
43
43
  apiUrl: 'https://your-deployment.convex.site',
@@ -48,14 +48,14 @@ CDN drop-in (no build step) — paste this just before `</body>`:
48
48
  </script>
49
49
  ```
50
50
 
51
- > **Why pin the version?** CDN URLs without a version (`@pixelmatters/markup`) resolve to whatever's `latest` on npm — a future major release will break your page silently. Always pin (`@pixelmatters/markup@1.3.11`).
51
+ > **Why pin the version?** CDN URLs without a version (`@pixelmatters/markup`) resolve to whatever's `latest` on npm — a future major release will break your page silently. Always pin (`@pixelmatters/markup@1.3.12`).
52
52
 
53
53
  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:
54
54
 
55
55
  ```html
56
56
  <script
57
57
  type="module"
58
- src="https://esm.sh/@pixelmatters/markup@1.3.11"
58
+ src="https://esm.sh/@pixelmatters/markup@1.3.12"
59
59
  data-markup-widget="true"
60
60
  data-api-url="https://your-deployment.convex.site"
61
61
  data-api-key="markup_..."
@@ -172,16 +172,16 @@ Unmounts the widget and removes the host element. Safe to call when nothing is m
172
172
  - All UI lives in that shadow root, with `:host { all: initial }` blocking style inheritance.
173
173
  - 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 FAB and active popovers opt back in to pointer events.
174
174
  - 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.
175
- - Identity lives in **host-page** `localStorage` under `markup.identity`, keyed to the top-level site. New visitors get a random `clientId` so the backend can group their comments across sessions.
175
+ - 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.
176
176
 
177
177
  ## Identity
178
178
 
179
179
  Anonymous by default, with two opt-in upgrade paths:
180
180
 
181
- - **Sign in with Markup** — opens a popup to `${apiUrl}/widget/auth`. Because the popup is first-party to the deployment origin, the better-auth session cookie is sent normally (sidestepping third-party cookie blocks). The popup `postMessage`s a JWT-backed verified identity back to the host page, which persists it under `markup.identity`.
182
- - **Continue as a guest** — name (and optional email), stored under the same key without `isVerified`.
181
+ - **Sign in with Markup** — the widget POSTs to `/widget/popup-exchange` with the apiKey + Origin gate, gets back a single-use 60-second code, then opens `${apiUrl}/widget/auth?code=…` in a popup. Because the popup is first-party to the deployment origin, the better-auth session cookie is sent normally (sidestepping third-party cookie blocks). The popup `postMessage`s a verified identity plus a 7-day server-signed `Bearer` JWT back to the host page, which persists them under `markup.identity` and attaches the JWT as `Authorization: Bearer` on subsequent writes. The apiKey never appears in the popup URL.
182
+ - **Continue as a guest** — name (and optional email), stored alongside the server-issued anonymous `clientId` + JWT minted on first load.
183
183
 
184
- The popup origin is validated against the project's `allowedDomains` before any identity is returned, so only embeds on approved domains can resolve dashboard sessions.
184
+ The popup origin is validated against the project's `allowedDomains` before any identity is returned, so only embeds on approved domains can resolve dashboard sessions. Verified JWTs can be invalidated before their TTL via the dashboard's "Sign out everywhere" action.
185
185
 
186
186
  > **Why a popup, not auto-detect?** Safari ITP, Chrome Storage Partitioning, and Firefox TCP all partition third-party storage and cookies by top-level site. A cross-origin fetch from `customer.com` to `convex.site` cannot see the dashboard session. The popup is the only reliable way to bridge identity across sites without per-host configuration.
187
187
 
@@ -231,7 +231,7 @@ For a `<script>` tag drop-in (no bundler), use the inline ESM form and **pin the
231
231
 
232
232
  ```html
233
233
  <script type="module">
234
- import { init } from 'https://esm.sh/@pixelmatters/markup@1.3.11'
234
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.3.12'
235
235
 
236
236
  init({
237
237
  apiUrl: '...',
@@ -247,7 +247,7 @@ If inline JS is disallowed (some CMS / page-builder editors), use the auto-init
247
247
  ```html
248
248
  <script
249
249
  type="module"
250
- src="https://esm.sh/@pixelmatters/markup@1.3.11"
250
+ src="https://esm.sh/@pixelmatters/markup@1.3.12"
251
251
  data-markup-widget="true"
252
252
  data-api-url="..."
253
253
  data-api-key="..."
package/dist/widget.js CHANGED
@@ -2843,7 +2843,7 @@ function Nr() {
2843
2843
  name: e?.name,
2844
2844
  email: e?.email
2845
2845
  };
2846
- return e ? e.isVerified && typeof e.clientId == "string" && typeof e.token == "string" && typeof e.tokenExpiresAt == "number" && typeof e.name == "string" ? {
2846
+ return e ? e.isVerified && typeof e.clientId == "string" && typeof e.token == "string" && typeof e.tokenExpiresAt == "number" && typeof e.name == "string" && e.tokenExpiresAt > Date.now() ? {
2847
2847
  state: {
2848
2848
  kind: "verified",
2849
2849
  identity: {
@@ -2949,7 +2949,7 @@ async function Rr(e, t) {
2949
2949
  window.removeEventListener("message", i), clearInterval(a), clearTimeout(s);
2950
2950
  }
2951
2951
  function i(i) {
2952
- if (i.origin !== n) return;
2952
+ if (i.origin !== n || i.source !== o) return;
2953
2953
  let a = i.data;
2954
2954
  a?.type === "MARKUP_AUTH_RESULT" && (r(), a.identity?.id && a.identity?.name ? e({
2955
2955
  identity: a.identity,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixelmatters/markup",
3
- "version": "1.3.11",
3
+ "version": "1.3.12",
4
4
  "description": "Embeddable feedback widget for collecting visual bug reports, screenshots, and comments on live web apps.",
5
5
  "keywords": [
6
6
  "annotation",