@pixelmatters/markup 1.14.0 → 1.16.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
@@ -35,34 +35,32 @@ 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.14.0'
38
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.16.0'
39
39
  // or
40
- // import { init } from 'https://esm.run/@pixelmatters/markup@1.14.0'
40
+ // import { init } from 'https://esm.run/@pixelmatters/markup@1.16.0'
41
41
 
42
42
  init({
43
43
  apiUrl: 'https://your-deployment.convex.site',
44
44
  apiKey: 'markup_...',
45
45
  position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
46
46
  theme: 'auto', // optional: 'auto' | 'light' | 'dark'
47
- fab: 'default', // optional: 'default' | 'icon-only'
48
47
  })
49
48
  </script>
50
49
  ```
51
50
 
52
- > **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.14.0`).
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.16.0`).
53
52
 
54
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:
55
54
 
56
55
  ```html
57
56
  <script
58
57
  type="module"
59
- src="https://esm.sh/@pixelmatters/markup@1.14.0"
58
+ src="https://esm.sh/@pixelmatters/markup@1.16.0"
60
59
  data-markup-widget="true"
61
60
  data-api-url="https://your-deployment.convex.site"
62
61
  data-api-key="markup_..."
63
62
  data-position="bottom-right"
64
63
  data-theme="auto"
65
- data-fab="default"
66
64
  ></script>
67
65
  ```
68
66
 
@@ -80,7 +78,6 @@ const stop = init({
80
78
  apiKey: 'markup_...',
81
79
  position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
82
80
  theme: 'light', // optional: 'auto' | 'light' | 'dark'
83
- fab: 'default', // optional: 'default' | 'icon-only'
84
81
  })
85
82
 
86
83
  // Tear down on logout / route change / page unload:
@@ -100,7 +97,6 @@ export default function App() {
100
97
  apiKey: import.meta.env.VITE_MARKUP_API_KEY,
101
98
  position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
102
99
  theme: 'auto', // optional: 'auto' | 'light' | 'dark'
103
- fab: 'default', // optional: 'default' | 'icon-only'
104
100
  })
105
101
  }, [])
106
102
 
@@ -122,7 +118,6 @@ onMounted(() => {
122
118
  apiKey: import.meta.env.VITE_MARKUP_API_KEY,
123
119
  position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
124
120
  theme: 'auto', // optional: 'auto' | 'light' | 'dark'
125
- fab: 'default', // optional: 'default' | 'icon-only'
126
121
  })
127
122
  })
128
123
  onBeforeUnmount(() => stop?.())
@@ -142,7 +137,6 @@ export default function App() {
142
137
  apiKey: import.meta.env.VITE_MARKUP_API_KEY,
143
138
  position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
144
139
  theme: 'auto', // optional: 'auto' | 'light' | 'dark'
145
- fab: 'default', // optional: 'default' | 'icon-only'
146
140
  })
147
141
  onCleanup(stop)
148
142
  })
@@ -157,13 +151,12 @@ export default function App() {
157
151
 
158
152
  Mounts the widget. Always tears down any existing instance before mounting, so calling `init` again (with the same or a different config) is safe. Returns the `destroy` function.
159
153
 
160
- | Option | Type | Default | Description |
161
- | ---------- | ---------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
162
- | `apiUrl` | `string` | required | Convex deployment site URL (`https://*.convex.site`) |
163
- | `apiKey` | `string` | required | Project API key — mint one in the dashboard |
164
- | `position` | `'bottom-right' \| 'bottom-left' \| 'bottom-center'` | `'bottom-right'` | Initial placement for the floating action button — users can drag it across the viewport thirds to snap between left / center / right |
165
- | `theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | Visual theme — `'light'` or `'dark'`, or `'auto'` (default) to follow the host's `prefers-color-scheme` |
166
- | `fab` | `'default' \| 'icon-only'` | `'default'` | Floating button variant — `'icon-only'` drops the "Markup" label for a circular icon button |
154
+ | Option | Type | Default | Description |
155
+ | ---------- | ---------------------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
156
+ | `apiUrl` | `string` | required | Convex deployment site URL (`https://*.convex.site`) |
157
+ | `apiKey` | `string` | required | Project API key — mint one in the dashboard |
158
+ | `position` | `'bottom-right' \| 'bottom-left' \| 'bottom-center'` | `'bottom-right'` | Initial placement for the toolbar — users can move it with the Position picker in the toolbar's overflow menu |
159
+ | `theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | Initial theme — `'light'` or `'dark'`, or `'auto'` (default) to follow the host's `prefers-color-scheme`. Users can change this from the overflow menu; their choice persists and wins over this option from then on |
167
160
 
168
161
  ### `destroy()`
169
162
 
@@ -173,9 +166,10 @@ Unmounts the widget and removes the host element. Safe to call when nothing is m
173
166
 
174
167
  | Shortcut | Action |
175
168
  | --------------------- | ---------------------------------------------------------------------- |
169
+ | `c` | Start placing a markup (ignored while typing) |
170
+ | `esc` | Cancel placement, or close the open popover |
176
171
  | `cmd/ctrl + .` | Toggle HUD visibility |
177
- | `cmd/ctrl + click` | Click the FAB to hide the HUD with a hint toast |
178
- | Drag the FAB | Snap it to whichever bottom corner the pointer ends in |
172
+ | `cmd/ctrl + click` | Click the toolbar's comment button to hide the HUD with a hint toast |
179
173
  | Drag a popover header | Move the open thread / new-thread popover; resets to the pin on reopen |
180
174
 
181
175
  ## Screenshots & privacy
@@ -210,7 +204,7 @@ When a screenshot is attached in the composer, a chip shows how many fields were
210
204
 
211
205
  - The widget mounts `<div id="markup-widget">` on `document.body` and attaches an open shadow root.
212
206
  - All UI lives in that shadow root, with `:host { all: initial }` blocking style inheritance.
213
- - 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.
207
+ - 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.
214
208
  - 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.
215
209
  - 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.
216
210
 
@@ -218,7 +212,7 @@ When a screenshot is attached in the composer, a chip shows how many fields were
218
212
 
219
213
  Anonymous by default, with two opt-in upgrade paths:
220
214
 
221
- - **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.
215
+ - **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 short-lived server-signed `Bearer` JWT back to the host page, which attaches it as `Authorization: Bearer` on subsequent writes. Where partitioned (CHIPS) cookies are available the tokens are held in `HttpOnly` cookies and the session refreshes for up to 7 days; where they aren't, the widget falls back to `markup.identity` in the host page's `localStorage` and the session is capped at 24 hours, since a token stored there is readable by any script on the page. The apiKey never appears in the popup URL.
222
216
  - **Continue as a guest** — name (and optional email), stored alongside the server-issued anonymous `clientId` + JWT minted on first load.
223
217
 
224
218
  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.
@@ -247,7 +241,7 @@ You are helping me install the **`@pixelmatters/markup`** feedback widget into m
247
241
 
248
242
  ## What it is
249
243
 
250
- A drop-in feedback widget published on npm as `@pixelmatters/markup`. It mounts a floating action button that lets users pin threaded comments (and optional annotated screenshots) anywhere on the page. It runs inside a shadow DOM so it doesn't affect host CSS.
244
+ A drop-in feedback widget published on npm as `@pixelmatters/markup`. It mounts a compact toolbar that lets users pin threaded comments (and optional annotated screenshots) anywhere on the page. It runs inside a shadow DOM so it doesn't affect host CSS.
251
245
 
252
246
  ## My credentials
253
247
 
@@ -265,7 +259,6 @@ init({
265
259
  apiKey: string, // required
266
260
  position?: 'bottom-right' | 'bottom-left' | 'bottom-center', // default 'bottom-right'
267
261
  theme?: 'light' | 'dark' | 'auto', // default 'auto'
268
- fab?: 'default' | 'icon-only', // default 'default'
269
262
  }) // returns a destroy() function — call it on unmount / logout / route teardown
270
263
  ```
271
264
 
@@ -278,14 +271,13 @@ For a `<script>` tag drop-in (no bundler), use the inline ESM form and **pin the
278
271
 
279
272
  ```html
280
273
  <script type="module">
281
- import { init } from 'https://esm.sh/@pixelmatters/markup@1.14.0'
274
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.16.0'
282
275
 
283
276
  init({
284
277
  apiUrl: '...',
285
278
  apiKey: '...',
286
279
  position: 'bottom-right',
287
280
  theme: 'auto',
288
- fab: 'default',
289
281
  })
290
282
  </script>
291
283
  ```
@@ -295,12 +287,11 @@ If inline JS is disallowed (some CMS / page-builder editors), use the auto-init
295
287
  ```html
296
288
  <script
297
289
  type="module"
298
- src="https://esm.sh/@pixelmatters/markup@1.14.0"
290
+ src="https://esm.sh/@pixelmatters/markup@1.16.0"
299
291
  data-markup-widget="true"
300
292
  data-api-url="..."
301
293
  data-api-key="..."
302
294
  data-position="bottom-right"
303
- data-fab="default"
304
295
  ></script>
305
296
  ```
306
297
 
package/dist/widget.d.ts CHANGED
@@ -1,4 +1,10 @@
1
1
  export type WidgetTheme = 'light' | 'dark' | 'auto';
2
+ /**
3
+ * @deprecated The floating action button was replaced by the toolbar
4
+ * pill, which has no variants. Kept so existing type imports keep
5
+ * compiling; it is no longer referenced by anything the widget renders
6
+ * and will be removed in the next major.
7
+ */
2
8
  export type WidgetFab = 'default' | 'icon-only';
3
9
  export interface ScreenshotsConfig {
4
10
  /** Disable screenshot capture entirely. Default: enabled. */
@@ -13,7 +19,10 @@ export interface WidgetConfig {
13
19
  apiUrl: string;
14
20
  /** Raw API key minted from a project's settings page */
15
21
  apiKey: string;
16
- /** Where the floating button sits. Defaults to bottom-right. */
22
+ /**
23
+ * Initial corner for the toolbar. Defaults to bottom-right. Users can
24
+ * move it afterwards from the toolbar's overflow menu.
25
+ */
17
26
  position?: 'bottom-right' | 'bottom-left' | 'bottom-center';
18
27
  /**
19
28
  * Visual theme.
@@ -23,11 +32,11 @@ export interface WidgetConfig {
23
32
  */
24
33
  theme?: WidgetTheme;
25
34
  /**
26
- * Floating action button variant.
27
- * - `'default'` pill button with icon + "Markup" label
28
- * - `'icon-only'` circular icon-only button (no label)
29
- *
30
- * Defaults to `'default'`.
35
+ * @deprecated Ignored. The floating action button was replaced by the
36
+ * toolbar pill, which has no variants, so neither `'default'` nor
37
+ * `'icon-only'` describes anything the widget can render. Accepted
38
+ * (and dropped) so upgrading doesn't break callers that still pass it;
39
+ * remove it from your config. Will be deleted in the next major.
31
40
  */
32
41
  fab?: WidgetFab;
33
42
  /** Screenshot capture and PII-scrub options. */