@pixelmatters/markup 1.3.11 → 1.4.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 +31 -16
- package/dist/widget.d.ts +9 -0
- package/dist/widget.js +697 -695
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,31 +35,34 @@ 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.
|
|
38
|
+
import { init } from 'https://esm.sh/@pixelmatters/markup@1.4.0'
|
|
39
39
|
// or
|
|
40
|
-
// import { init } from 'https://esm.run/@pixelmatters/markup@1.
|
|
40
|
+
// import { init } from 'https://esm.run/@pixelmatters/markup@1.4.0'
|
|
41
41
|
|
|
42
42
|
init({
|
|
43
43
|
apiUrl: 'https://your-deployment.convex.site',
|
|
44
44
|
apiKey: 'markup_...',
|
|
45
|
-
position: 'bottom-right', // optional
|
|
46
|
-
theme: 'auto', // optional
|
|
45
|
+
position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
|
|
46
|
+
theme: 'auto', // optional: 'auto' | 'light' | 'dark'
|
|
47
|
+
fab: 'default', // optional: 'default' | 'icon-only'
|
|
47
48
|
})
|
|
48
49
|
</script>
|
|
49
50
|
```
|
|
50
51
|
|
|
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.
|
|
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.0`).
|
|
52
53
|
|
|
53
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:
|
|
54
55
|
|
|
55
56
|
```html
|
|
56
57
|
<script
|
|
57
58
|
type="module"
|
|
58
|
-
src="https://esm.sh/@pixelmatters/markup@1.
|
|
59
|
+
src="https://esm.sh/@pixelmatters/markup@1.4.0"
|
|
59
60
|
data-markup-widget="true"
|
|
60
61
|
data-api-url="https://your-deployment.convex.site"
|
|
61
62
|
data-api-key="markup_..."
|
|
62
63
|
data-position="bottom-right"
|
|
64
|
+
data-theme="auto"
|
|
65
|
+
data-fab="default"
|
|
63
66
|
></script>
|
|
64
67
|
```
|
|
65
68
|
|
|
@@ -75,8 +78,9 @@ import { init, destroy } from '@pixelmatters/markup'
|
|
|
75
78
|
const stop = init({
|
|
76
79
|
apiUrl: 'https://your-deployment.convex.site',
|
|
77
80
|
apiKey: 'markup_...',
|
|
78
|
-
position: 'bottom-right', // optional
|
|
79
|
-
theme: 'light', // optional
|
|
81
|
+
position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
|
|
82
|
+
theme: 'light', // optional: 'auto' | 'light' | 'dark'
|
|
83
|
+
fab: 'default', // optional: 'default' | 'icon-only'
|
|
80
84
|
})
|
|
81
85
|
|
|
82
86
|
// Tear down on logout / route change / page unload:
|
|
@@ -94,8 +98,9 @@ export default function App() {
|
|
|
94
98
|
return init({
|
|
95
99
|
apiUrl: import.meta.env.VITE_MARKUP_API_URL,
|
|
96
100
|
apiKey: import.meta.env.VITE_MARKUP_API_KEY,
|
|
97
|
-
position: 'bottom-right', // optional
|
|
98
|
-
theme: 'auto', // optional
|
|
101
|
+
position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
|
|
102
|
+
theme: 'auto', // optional: 'auto' | 'light' | 'dark'
|
|
103
|
+
fab: 'default', // optional: 'default' | 'icon-only'
|
|
99
104
|
})
|
|
100
105
|
}, [])
|
|
101
106
|
|
|
@@ -115,6 +120,9 @@ onMounted(() => {
|
|
|
115
120
|
stop = init({
|
|
116
121
|
apiUrl: import.meta.env.VITE_MARKUP_API_URL,
|
|
117
122
|
apiKey: import.meta.env.VITE_MARKUP_API_KEY,
|
|
123
|
+
position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
|
|
124
|
+
theme: 'auto', // optional: 'auto' | 'light' | 'dark'
|
|
125
|
+
fab: 'default', // optional: 'default' | 'icon-only'
|
|
118
126
|
})
|
|
119
127
|
})
|
|
120
128
|
onBeforeUnmount(() => stop?.())
|
|
@@ -132,6 +140,9 @@ export default function App() {
|
|
|
132
140
|
const stop = init({
|
|
133
141
|
apiUrl: import.meta.env.VITE_MARKUP_API_URL,
|
|
134
142
|
apiKey: import.meta.env.VITE_MARKUP_API_KEY,
|
|
143
|
+
position: 'bottom-right', // optional: 'bottom-right' | 'bottom-left'
|
|
144
|
+
theme: 'auto', // optional: 'auto' | 'light' | 'dark'
|
|
145
|
+
fab: 'default', // optional: 'default' | 'icon-only'
|
|
135
146
|
})
|
|
136
147
|
onCleanup(stop)
|
|
137
148
|
})
|
|
@@ -152,6 +163,7 @@ Mounts the widget. Always tears down any existing instance before mounting, so c
|
|
|
152
163
|
| `apiKey` | `string` | required | Project API key — mint one in the dashboard |
|
|
153
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 |
|
|
154
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 |
|
|
155
167
|
|
|
156
168
|
### `destroy()`
|
|
157
169
|
|
|
@@ -172,16 +184,16 @@ Unmounts the widget and removes the host element. Safe to call when nothing is m
|
|
|
172
184
|
- All UI lives in that shadow root, with `:host { all: initial }` blocking style inheritance.
|
|
173
185
|
- 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
186
|
- 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.
|
|
187
|
+
- 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
188
|
|
|
177
189
|
## Identity
|
|
178
190
|
|
|
179
191
|
Anonymous by default, with two opt-in upgrade paths:
|
|
180
192
|
|
|
181
|
-
- **Sign in with Markup** —
|
|
182
|
-
- **Continue as a guest** — name (and optional email), stored
|
|
193
|
+
- **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.
|
|
194
|
+
- **Continue as a guest** — name (and optional email), stored alongside the server-issued anonymous `clientId` + JWT minted on first load.
|
|
183
195
|
|
|
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.
|
|
196
|
+
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
197
|
|
|
186
198
|
> **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
199
|
|
|
@@ -219,6 +231,7 @@ init({
|
|
|
219
231
|
apiKey: string, // required
|
|
220
232
|
position?: 'bottom-right' | 'bottom-left', // default 'bottom-right'
|
|
221
233
|
theme?: 'light' | 'dark' | 'auto', // default 'auto'
|
|
234
|
+
fab?: 'default' | 'icon-only', // default 'default'
|
|
222
235
|
}) // returns a destroy() function — call it on unmount / logout / route teardown
|
|
223
236
|
```
|
|
224
237
|
|
|
@@ -231,13 +244,14 @@ For a `<script>` tag drop-in (no bundler), use the inline ESM form and **pin the
|
|
|
231
244
|
|
|
232
245
|
```html
|
|
233
246
|
<script type="module">
|
|
234
|
-
import { init } from 'https://esm.sh/@pixelmatters/markup@1.
|
|
247
|
+
import { init } from 'https://esm.sh/@pixelmatters/markup@1.4.0'
|
|
235
248
|
|
|
236
249
|
init({
|
|
237
250
|
apiUrl: '...',
|
|
238
251
|
apiKey: '...',
|
|
239
252
|
position: 'bottom-right',
|
|
240
253
|
theme: 'auto',
|
|
254
|
+
fab: 'default',
|
|
241
255
|
})
|
|
242
256
|
</script>
|
|
243
257
|
```
|
|
@@ -247,11 +261,12 @@ If inline JS is disallowed (some CMS / page-builder editors), use the auto-init
|
|
|
247
261
|
```html
|
|
248
262
|
<script
|
|
249
263
|
type="module"
|
|
250
|
-
src="https://esm.sh/@pixelmatters/markup@1.
|
|
264
|
+
src="https://esm.sh/@pixelmatters/markup@1.4.0"
|
|
251
265
|
data-markup-widget="true"
|
|
252
266
|
data-api-url="..."
|
|
253
267
|
data-api-key="..."
|
|
254
268
|
data-position="bottom-right"
|
|
269
|
+
data-fab="default"
|
|
255
270
|
></script>
|
|
256
271
|
```
|
|
257
272
|
|
package/dist/widget.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type WidgetTheme = 'light' | 'dark' | 'auto';
|
|
2
|
+
export type WidgetFab = 'default' | 'icon-only';
|
|
2
3
|
export interface WidgetConfig {
|
|
3
4
|
/** Convex deployment site URL, e.g. `https://your-deployment.convex.site` */
|
|
4
5
|
apiUrl: string;
|
|
@@ -13,6 +14,14 @@ export interface WidgetConfig {
|
|
|
13
14
|
* - `'auto'` (default) — follow the host's `prefers-color-scheme`
|
|
14
15
|
*/
|
|
15
16
|
theme?: WidgetTheme;
|
|
17
|
+
/**
|
|
18
|
+
* Floating action button variant.
|
|
19
|
+
* - `'default'` — pill button with icon + "Markup" label
|
|
20
|
+
* - `'icon-only'` — circular icon-only button (no label)
|
|
21
|
+
*
|
|
22
|
+
* Defaults to `'default'`.
|
|
23
|
+
*/
|
|
24
|
+
fab?: WidgetFab;
|
|
16
25
|
}
|
|
17
26
|
/**
|
|
18
27
|
* Mounts the feedback widget on the page. Idempotent — calling init again
|