@pixelmatters/markup 1.4.1 → 1.6.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,28 +35,28 @@ 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.4.1'
38
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.6.0'
39
39
  // or
40
- // import { init } from 'https://esm.run/@pixelmatters/markup@1.4.1'
40
+ // import { init } from 'https://esm.run/@pixelmatters/markup@1.6.0'
41
41
 
42
42
  init({
43
43
  apiUrl: 'https://your-deployment.convex.site',
44
44
  apiKey: 'markup_...',
45
- position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
45
+ position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
46
46
  theme: 'auto', // optional: 'auto' | 'light' | 'dark'
47
47
  fab: 'default', // optional: 'default' | 'icon-only'
48
48
  })
49
49
  </script>
50
50
  ```
51
51
 
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.4.1`).
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.6.0`).
53
53
 
54
54
  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
55
 
56
56
  ```html
57
57
  <script
58
58
  type="module"
59
- src="https://esm.sh/@pixelmatters/markup@1.4.1"
59
+ src="https://esm.sh/@pixelmatters/markup@1.6.0"
60
60
  data-markup-widget="true"
61
61
  data-api-url="https://your-deployment.convex.site"
62
62
  data-api-key="markup_..."
@@ -78,7 +78,7 @@ import { init, destroy } from '@pixelmatters/markup'
78
78
  const stop = init({
79
79
  apiUrl: 'https://your-deployment.convex.site',
80
80
  apiKey: 'markup_...',
81
- position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
81
+ position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
82
82
  theme: 'light', // optional: 'auto' | 'light' | 'dark'
83
83
  fab: 'default', // optional: 'default' | 'icon-only'
84
84
  })
@@ -98,7 +98,7 @@ export default function App() {
98
98
  return init({
99
99
  apiUrl: import.meta.env.VITE_MARKUP_API_URL,
100
100
  apiKey: import.meta.env.VITE_MARKUP_API_KEY,
101
- position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
101
+ position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
102
102
  theme: 'auto', // optional: 'auto' | 'light' | 'dark'
103
103
  fab: 'default', // optional: 'default' | 'icon-only'
104
104
  })
@@ -120,7 +120,7 @@ onMounted(() => {
120
120
  stop = init({
121
121
  apiUrl: import.meta.env.VITE_MARKUP_API_URL,
122
122
  apiKey: import.meta.env.VITE_MARKUP_API_KEY,
123
- position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
123
+ position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
124
124
  theme: 'auto', // optional: 'auto' | 'light' | 'dark'
125
125
  fab: 'default', // optional: 'default' | 'icon-only'
126
126
  })
@@ -140,7 +140,7 @@ export default function App() {
140
140
  const stop = init({
141
141
  apiUrl: import.meta.env.VITE_MARKUP_API_URL,
142
142
  apiKey: import.meta.env.VITE_MARKUP_API_KEY,
143
- position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
143
+ position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left' | 'bottom-center'
144
144
  theme: 'auto', // optional: 'auto' | 'light' | 'dark'
145
145
  fab: 'default', // optional: 'default' | 'icon-only'
146
146
  })
@@ -157,13 +157,13 @@ export default function App() {
157
157
 
158
158
  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
159
 
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-right'` | Initial corner for the floating action button — users can drag it across the viewport midline to flip corners at runtime |
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 |
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 |
167
167
 
168
168
  ### `destroy()`
169
169
 
@@ -178,6 +178,34 @@ Unmounts the widget and removes the host element. Safe to call when nothing is m
178
178
  | Drag the FAB | Snap it to whichever bottom corner the pointer ends in |
179
179
  | Drag a popover header | Move the open thread / new-thread popover; resets to the pin on reopen |
180
180
 
181
+ ## Screenshots & privacy
182
+
183
+ By default, the widget captures the visible viewport as a JPEG before you submit a thread. Sensitive fields are blacked out **before** the image is produced — the live DOM is mutated only for the duration of the capture and immediately restored. No image content leaves the browser until the user explicitly attaches the screenshot and posts.
184
+
185
+ **Auto-scrubbed (zero config):**
186
+
187
+ - `input[type="password"]`
188
+ - Any `<input>` whose `autocomplete` attribute contains `cc-number`, `cc-csc`, `cc-exp`, `cc-name`, `cc-type`, `current-password`, `new-password`, or `one-time-code`
189
+
190
+ **Attribute API** — add to any element to control capture:
191
+
192
+ | Attribute | Behaviour |
193
+ | --------------------- | ---------------------------------------------------------------------- |
194
+ | `data-markup-private` | Always mask — contents replaced with a solid block. |
195
+ | `data-markup-redact` | Alias for `data-markup-private`. |
196
+ | `data-markup-safe` | Exempts descendants from the default auto-detect rules (escape hatch). |
197
+ | `data-markup-skip` | Removes the element from the screenshot entirely. |
198
+
199
+ **`init()` `screenshots` config:**
200
+
201
+ | Option | Type | Default | Description |
202
+ | ---------------------------- | --------- | ------- | ---------------------------------------------------------- |
203
+ | `screenshots.enabled` | `boolean` | `true` | Set to `false` to disable capture entirely. |
204
+ | `screenshots.strictScrub` | `boolean` | `false` | Also masks all `input`, `select`, and `textarea` elements. |
205
+ | `screenshots.redactSelector` | `string` | — | Custom CSS selector — matched elements are always masked. |
206
+
207
+ When a screenshot is attached in the composer, a chip shows how many fields were redacted. Clicking it expands the list of CSS selectors that were masked.
208
+
181
209
  ## How it works
182
210
 
183
211
  - The widget mounts `<div id="markup-widget">` on `document.body` and attaches an open shadow root.
@@ -206,7 +234,13 @@ The popup origin is validated against the project's `allowedDomains` before any
206
234
 
207
235
  ## AI prompt
208
236
 
209
- Paste the block below into Claude, ChatGPT, Cursor, or any other LLM and it'll wire the widget into your codebase end-to-end.
237
+ If your IDE / agent supports the [`skills`](https://www.npmjs.com/package/skills) CLI (Claude Code, Cursor, etc.), install the bundled skill instead of pasting a prompt — it gives the agent persistent, up-to-date install + troubleshooting guidance:
238
+
239
+ ```bash
240
+ npx skills add Pixelmatters/markup --skill pixelmatters-markup
241
+ ```
242
+
243
+ Otherwise, paste the block below into Claude, ChatGPT, Cursor, or any other LLM and it'll wire the widget into your codebase end-to-end.
210
244
 
211
245
  ````markdown
212
246
  You are helping me install the **`@pixelmatters/markup`** feedback widget into my web app.
@@ -229,7 +263,7 @@ import { init, destroy } from '@pixelmatters/markup'
229
263
  init({
230
264
  apiUrl: string, // required
231
265
  apiKey: string, // required
232
- position?: 'bottom-right' | 'bottom-left', // default 'bottom-right'
266
+ position?: 'bottom-right' | 'bottom-left' | 'bottom-center', // default 'bottom-right'
233
267
  theme?: 'light' | 'dark' | 'auto', // default 'auto'
234
268
  fab?: 'default' | 'icon-only', // default 'default'
235
269
  }) // returns a destroy() function — call it on unmount / logout / route teardown
@@ -244,7 +278,7 @@ For a `<script>` tag drop-in (no bundler), use the inline ESM form and **pin the
244
278
 
245
279
  ```html
246
280
  <script type="module">
247
- import { init } from 'https://esm.sh/@pixelmatters/markup@1.4.1'
281
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.6.0'
248
282
 
249
283
  init({
250
284
  apiUrl: '...',
@@ -261,7 +295,7 @@ If inline JS is disallowed (some CMS / page-builder editors), use the auto-init
261
295
  ```html
262
296
  <script
263
297
  type="module"
264
- src="https://esm.sh/@pixelmatters/markup@1.4.1"
298
+ src="https://esm.sh/@pixelmatters/markup@1.6.0"
265
299
  data-markup-widget="true"
266
300
  data-api-url="..."
267
301
  data-api-key="..."
package/dist/widget.d.ts CHANGED
@@ -1,12 +1,20 @@
1
1
  export type WidgetTheme = 'light' | 'dark' | 'auto';
2
2
  export type WidgetFab = 'default' | 'icon-only';
3
+ export interface ScreenshotsConfig {
4
+ /** Disable screenshot capture entirely. Default: enabled. */
5
+ enabled?: boolean;
6
+ /** Also masks all inputs/selects/textareas in addition to the default sensitive set. Default: false. */
7
+ strictScrub?: boolean;
8
+ /** Custom CSS selector — matched elements are always masked. */
9
+ redactSelector?: string;
10
+ }
3
11
  export interface WidgetConfig {
4
12
  /** Convex deployment site URL, e.g. `https://your-deployment.convex.site` */
5
13
  apiUrl: string;
6
14
  /** Raw API key minted from a project's settings page */
7
15
  apiKey: string;
8
16
  /** Where the floating button sits. Defaults to bottom-right. */
9
- position?: 'bottom-right' | 'bottom-left';
17
+ position?: 'bottom-right' | 'bottom-left' | 'bottom-center';
10
18
  /**
11
19
  * Visual theme.
12
20
  * - `'light'` — force light palette
@@ -22,6 +30,8 @@ export interface WidgetConfig {
22
30
  * Defaults to `'default'`.
23
31
  */
24
32
  fab?: WidgetFab;
33
+ /** Screenshot capture and PII-scrub options. */
34
+ screenshots?: ScreenshotsConfig;
25
35
  }
26
36
  /**
27
37
  * Mounts the feedback widget on the page. Idempotent — calling init again