@pixelmatters/markup 1.1.0 → 1.3.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
@@ -17,25 +17,45 @@ Pin-anchored feedback for live web apps. Drop in a script tag (or one React comp
17
17
  - **Respects the platform** — honours `prefers-reduced-motion` and `prefers-color-scheme`; full keyboard navigation with focus traps in popovers.
18
18
  - **Tiny surface, tiny config** — `init({ apiUrl, apiKey })` is enough to start. No global CSS to import, no provider to wrap.
19
19
 
20
- Built with [Preact](https://preactjs.com). Ships as ESM with a vanilla entry and a React/Preact wrapper.
20
+ Built with [Preact](https://preactjs.com). Ships as a single ESM bundle — wire it into any framework with a one-liner in your root component (snippets below).
21
21
 
22
22
  ## Install
23
23
 
24
24
  ```bash
25
25
  # pnpm
26
- pnpm install @pixelmatters/markup
26
+ pnpm add @pixelmatters/markup
27
27
  # or yarn
28
28
  yarn add @pixelmatters/markup
29
29
  # or npm
30
30
  npm install @pixelmatters/markup
31
31
  ```
32
32
 
33
- CDN drop-in (auto-init on `DOMContentLoaded`):
33
+ CDN drop-in (no build step) — paste this just before `</body>`:
34
+
35
+ ```html
36
+ <script type="module">
37
+ // Pin the exact version — esm.sh resolves it from npm
38
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.3.0'
39
+ // or
40
+ // import { init } from 'https://esm.run/@pixelmatters/markup@1.3.0'
41
+
42
+ init({
43
+ apiUrl: 'https://your-deployment.convex.site',
44
+ apiKey: 'markup_...',
45
+ position: 'bottom-right', // optional
46
+ theme: 'auto', // optional
47
+ })
48
+ </script>
49
+ ```
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.0`).
52
+
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:
34
54
 
35
55
  ```html
36
56
  <script
37
57
  type="module"
38
- src="https://unpkg.com/@pixelmatters/markup"
58
+ src="https://esm.sh/@pixelmatters/markup@1.3.0"
39
59
  data-markup-widget="true"
40
60
  data-api-url="https://your-deployment.convex.site"
41
61
  data-api-key="markup_..."
@@ -43,7 +63,7 @@ CDN drop-in (auto-init on `DOMContentLoaded`):
43
63
  ></script>
44
64
  ```
45
65
 
46
- The `data-markup-widget="true"` attribute is required — it's how the bootstrap locates its own `<script>` tag (since `document.currentScript` is `null` for `type="module"`).
66
+ `data-markup-widget="true"` is required — it's how the bootstrap finds its own `<script>` tag (since `document.currentScript` is `null` for `type="module"`).
47
67
 
48
68
  ## Quickstart
49
69
 
@@ -63,23 +83,60 @@ const stop = init({
63
83
  stop() // equivalent to destroy()
64
84
  ```
65
85
 
66
- ### React / Preact
86
+ ### React
67
87
 
68
88
  ```tsx
69
- import { MarkupWidget } from '@pixelmatters/markup/react'
89
+ import { useEffect } from 'react'
90
+ import { init } from '@pixelmatters/markup'
70
91
 
71
92
  export default function App() {
72
- return (
73
- <>
74
- {/* your app */}
75
- <MarkupWidget
76
- apiUrl={import.meta.env.VITE_MARKUP_API_URL}
77
- apiKey={import.meta.env.VITE_MARKUP_API_KEY}
78
- position="bottom-right" // optional
79
- theme="light" // optional
80
- />
81
- </>
82
- )
93
+ useEffect(() => {
94
+ return init({
95
+ apiUrl: import.meta.env.VITE_MARKUP_API_URL,
96
+ apiKey: import.meta.env.VITE_MARKUP_API_KEY,
97
+ position: 'bottom-right', // optional
98
+ theme: 'auto', // optional
99
+ })
100
+ }, [])
101
+
102
+ return <>{/* your app */}</>
103
+ }
104
+ ```
105
+
106
+ ### Vue 3
107
+
108
+ ```vue
109
+ <script setup lang="ts">
110
+ import { onMounted, onBeforeUnmount } from 'vue'
111
+ import { init } from '@pixelmatters/markup'
112
+
113
+ let stop: (() => void) | undefined
114
+ onMounted(() => {
115
+ stop = init({
116
+ apiUrl: import.meta.env.VITE_MARKUP_API_URL,
117
+ apiKey: import.meta.env.VITE_MARKUP_API_KEY,
118
+ })
119
+ })
120
+ onBeforeUnmount(() => stop?.())
121
+ </script>
122
+ ```
123
+
124
+ ### SolidJS
125
+
126
+ ```tsx
127
+ import { onMount, onCleanup } from 'solid-js'
128
+ import { init } from '@pixelmatters/markup'
129
+
130
+ export default function App() {
131
+ onMount(() => {
132
+ const stop = init({
133
+ apiUrl: import.meta.env.VITE_MARKUP_API_URL,
134
+ apiKey: import.meta.env.VITE_MARKUP_API_KEY,
135
+ })
136
+ onCleanup(stop)
137
+ })
138
+
139
+ return <>{/* your app */}</>
83
140
  }
84
141
  ```
85
142
 
@@ -89,23 +146,25 @@ export default function App() {
89
146
 
90
147
  Mounts the widget. Idempotent: re-calling with the same config is a no-op; calling with a different config tears down the previous instance first. Returns the `destroy` function.
91
148
 
92
- | Option | Type | Default | Description |
93
- | ---------- | --------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------- |
94
- | `apiUrl` | `string` | required | Convex deployment site URL (`https://*.convex.site`) |
95
- | `apiKey` | `string` | required | Project API key — mint one in the dashboard |
96
- | `position` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Where the floating action button sits |
97
- | `theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | Visual theme — `'light'` or `'dark'`, or `'auto'` (default) to follow the host's `prefers-color-scheme` |
149
+ | Option | Type | Default | Description |
150
+ | ---------- | --------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
151
+ | `apiUrl` | `string` | required | Convex deployment site URL (`https://*.convex.site`) |
152
+ | `apiKey` | `string` | required | Project API key — mint one in the dashboard |
153
+ | `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
+ | `theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | Visual theme — `'light'` or `'dark'`, or `'auto'` (default) to follow the host's `prefers-color-scheme` |
98
155
 
99
156
  ### `destroy()`
100
157
 
101
158
  Unmounts the widget and removes the host element. Safe to call when nothing is mounted.
102
159
 
103
- ### Keyboard
160
+ ### Keyboard & mouse
104
161
 
105
- | Shortcut | Action |
106
- | ------------------ | ----------------------------------------------- |
107
- | `cmd/ctrl + .` | Toggle HUD visibility |
108
- | `cmd/ctrl + click` | Click the FAB to hide the HUD with a hint toast |
162
+ | Shortcut | Action |
163
+ | --------------------- | ---------------------------------------------------------------------- |
164
+ | `cmd/ctrl + .` | Toggle HUD visibility |
165
+ | `cmd/ctrl + click` | Click the FAB to hide the HUD with a hint toast |
166
+ | Drag the FAB | Snap it to whichever bottom corner the pointer ends in |
167
+ | Drag a popover header | Move the open thread / new-thread popover; resets to the pin on reopen |
109
168
 
110
169
  ## How it works
111
170
 
@@ -154,8 +213,6 @@ A drop-in feedback widget published on npm as `@pixelmatters/markup`. It mounts
154
213
 
155
214
  ```ts
156
215
  import { init, destroy } from '@pixelmatters/markup'
157
- // or:
158
- import { MarkupWidget } from '@pixelmatters/markup/react'
159
216
 
160
217
  init({
161
218
  apiUrl: string, // required
@@ -165,12 +222,32 @@ init({
165
222
  }) // returns a destroy() function — call it on unmount / logout / route teardown
166
223
  ```
167
224
 
168
- For a `<script>` tag drop-in (no bundler), use:
225
+ For React/Vue/Solid hosts, call `init()` from a mount lifecycle hook
226
+ (`useEffect`, `onMounted`, `onMount`) and call the returned `destroy` on
227
+ cleanup. There's no framework-specific entrypoint — `init` is the entire
228
+ public surface.
229
+
230
+ For a `<script>` tag drop-in (no bundler), use the inline ESM form and **pin the version**:
231
+
232
+ ```html
233
+ <script type="module">
234
+ import { init } from 'https://esm.sh/@pixelmatters/markup@1.3.0'
235
+
236
+ init({
237
+ apiUrl: '...',
238
+ apiKey: '...',
239
+ position: 'bottom-right',
240
+ theme: 'auto',
241
+ })
242
+ </script>
243
+ ```
244
+
245
+ If inline JS is disallowed (some CMS / page-builder editors), use the auto-init `<script src=…>` form with `data-*` attributes (`data-markup-widget="true"` is required):
169
246
 
170
247
  ```html
171
248
  <script
172
249
  type="module"
173
- src="https://unpkg.com/@pixelmatters/markup"
250
+ src="https://esm.sh/@pixelmatters/markup@1.3.0"
174
251
  data-markup-widget="true"
175
252
  data-api-url="..."
176
253
  data-api-key="..."
@@ -178,8 +255,6 @@ For a `<script>` tag drop-in (no bundler), use:
178
255
  ></script>
179
256
  ```
180
257
 
181
- The `data-markup-widget="true"` attribute is required.
182
-
183
258
  ## Your task
184
259
 
185
260
  1. Detect my framework (React, Vue, Svelte, Next.js, plain HTML, etc.) by inspecting the project.
@@ -193,7 +268,7 @@ Constraints:
193
268
 
194
269
  - Do **not** add CSS imports or provider components — the widget needs neither.
195
270
  - Do **not** hardcode the key.
196
- - If the project has a CSP, add `https://unpkg.com` to `script-src` only if I'm using the `<script>` tag path.
271
+ - If the project has a CSP, add `https://esm.sh` to `script-src` only if I'm using the `<script>` tag path.
197
272
  ````
198
273
 
199
274
  ## Browser support
@@ -204,7 +279,7 @@ Modern evergreen browsers (Chrome, Edge, Firefox, Safari) and their mobile equiv
204
279
 
205
280
  ```bash
206
281
  pnpm dev # vite dev server with the playground page
207
- pnpm build # production build → dist/{widget,react}.{js,d.ts}
282
+ pnpm build # production build → dist/widget.{js,d.ts}
208
283
  pnpm typecheck # tsc --noEmit
209
284
  ```
210
285