@pixelmatters/markup 1.28.1 → 1.30.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 +17 -16
- package/dist/react.js +1 -14
- package/dist/react.js.map +1 -1
- package/dist/shared-mount-B9ij4vuL.js +2 -0
- package/dist/{shared-mount-DABK4arh.js.map → shared-mount-B9ij4vuL.js.map} +1 -1
- package/dist/vue.js +1 -20
- package/dist/vue.js.map +1 -1
- package/dist/widget.d.ts +4 -3
- package/dist/widget.js +10 -8776
- package/dist/widget.js.map +1 -1
- package/package.json +1 -1
- package/skills/install-markup-widget/SKILL.md +4 -4
- package/dist/shared-mount-DABK4arh.js +0 -27
package/README.md
CHANGED
|
@@ -37,9 +37,9 @@ CDN drop-in, no build step. Paste this just before `</body>`:
|
|
|
37
37
|
```html
|
|
38
38
|
<script type="module">
|
|
39
39
|
// Pin the exact version; esm.sh resolves it from npm
|
|
40
|
-
import { init } from 'https://esm.sh/@pixelmatters/markup@1.
|
|
40
|
+
import { init } from 'https://esm.sh/@pixelmatters/markup@1.30.0'
|
|
41
41
|
// or
|
|
42
|
-
// import { init } from 'https://esm.run/@pixelmatters/markup@1.
|
|
42
|
+
// import { init } from 'https://esm.run/@pixelmatters/markup@1.30.0'
|
|
43
43
|
|
|
44
44
|
init({
|
|
45
45
|
apiUrl: 'https://your-deployment.convex.site',
|
|
@@ -50,14 +50,14 @@ CDN drop-in, no build step. Paste this just before `</body>`:
|
|
|
50
50
|
</script>
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
> **Why pin the version?** CDN URLs without a version (`@pixelmatters/markup`) resolve to whatever's `latest` on npm, so a future major release will break your page with no warning. Always pin (`@pixelmatters/markup@1.
|
|
53
|
+
> **Why pin the version?** CDN URLs without a version (`@pixelmatters/markup`) resolve to whatever's `latest` on npm, so a future major release will break your page with no warning. Always pin (`@pixelmatters/markup@1.30.0`).
|
|
54
54
|
|
|
55
55
|
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:
|
|
56
56
|
|
|
57
57
|
```html
|
|
58
58
|
<script
|
|
59
59
|
type="module"
|
|
60
|
-
src="https://esm.sh/@pixelmatters/markup@1.
|
|
60
|
+
src="https://esm.sh/@pixelmatters/markup@1.30.0"
|
|
61
61
|
data-markup-widget="true"
|
|
62
62
|
data-api-url="https://your-deployment.convex.site"
|
|
63
63
|
data-api-key="markup_..."
|
|
@@ -255,15 +255,16 @@ close.
|
|
|
255
255
|
|
|
256
256
|
### Keyboard & mouse
|
|
257
257
|
|
|
258
|
-
| Shortcut | Action
|
|
259
|
-
| --------------------- |
|
|
260
|
-
| `c` | Start placing a markup (ignored while typing)
|
|
261
|
-
| `@` | In a composer, open the member picker. `↑`/`↓` to move, `enter` or `tab` to pick
|
|
262
|
-
| `cmd/ctrl + enter` | Post the comment being written
|
|
263
|
-
| `
|
|
264
|
-
| `
|
|
265
|
-
| `cmd/ctrl +
|
|
266
|
-
|
|
|
258
|
+
| Shortcut | Action |
|
|
259
|
+
| --------------------- | --------------------------------------------------------------------------------- |
|
|
260
|
+
| `c` | Start placing a markup (ignored while typing) |
|
|
261
|
+
| `@` | In a composer, open the member picker. `↑`/`↓` to move, `enter` or `tab` to pick |
|
|
262
|
+
| `cmd/ctrl + enter` | Post the comment being written |
|
|
263
|
+
| `enter` | Send a reply. `shift + enter` adds a new line, as `enter` does on touch keyboards |
|
|
264
|
+
| `esc` | Cancel placement, dismiss the mention picker, or close the open popover / menu |
|
|
265
|
+
| `cmd/ctrl + .` | Toggle HUD visibility |
|
|
266
|
+
| `cmd/ctrl + click` | Click the toolbar's comment button to hide the HUD with a hint toast |
|
|
267
|
+
| Drag a popover header | Move the open thread / new-thread popover; resets to the pin on reopen |
|
|
267
268
|
|
|
268
269
|
## Screenshots & privacy
|
|
269
270
|
|
|
@@ -401,7 +402,7 @@ to start from `anchorSource` and confirm it, not to trust it blindly.
|
|
|
401
402
|
|
|
402
403
|
## How it works
|
|
403
404
|
|
|
404
|
-
- The widget mounts `<div id="markup-widget">` on `document.body` and attaches an open shadow root.
|
|
405
|
+
- The widget mounts `<div id="markup-widget">` on `document.body` and attaches an open shadow root. It does so once the page is idle, or 2 s after `init()` on a page that never is, so `init()` returns before the element exists.
|
|
405
406
|
- All UI lives in that shadow root, with `:host { all: initial }` blocking style inheritance.
|
|
406
407
|
- 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.
|
|
407
408
|
- Pins are anchored as `(x, y)` fractions of the document plus a CSS path from the nearest landmark (a stable `id`, `data-testid`, `main`, `form`, `table`, …) and the element's text. The path wins when it still resolves, relaxing from the top if a wrapper changed, and a match whose text differs is rejected; the fraction is the fallback so pins survive layout changes.
|
|
@@ -495,7 +496,7 @@ For a `<script>` tag drop-in (no bundler), use the inline ESM form and **pin the
|
|
|
495
496
|
|
|
496
497
|
```html
|
|
497
498
|
<script type="module">
|
|
498
|
-
import { init } from 'https://esm.sh/@pixelmatters/markup@1.
|
|
499
|
+
import { init } from 'https://esm.sh/@pixelmatters/markup@1.30.0'
|
|
499
500
|
|
|
500
501
|
init({
|
|
501
502
|
apiUrl: '...',
|
|
@@ -511,7 +512,7 @@ If inline JS is disallowed (some CMS / page-builder editors), use the auto-init
|
|
|
511
512
|
```html
|
|
512
513
|
<script
|
|
513
514
|
type="module"
|
|
514
|
-
src="https://esm.sh/@pixelmatters/markup@1.
|
|
515
|
+
src="https://esm.sh/@pixelmatters/markup@1.30.0"
|
|
515
516
|
data-markup-widget="true"
|
|
516
517
|
data-api-url="..."
|
|
517
518
|
data-api-key="..."
|
package/dist/react.js
CHANGED
|
@@ -1,15 +1,2 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { init as e, t } from "./widget.js";
|
|
3
|
-
import { t as n } from "./shared-mount-DABK4arh.js";
|
|
4
|
-
import { useEffect as r } from "react";
|
|
5
|
-
//#region src/react.ts
|
|
6
|
-
function i(i) {
|
|
7
|
-
let { enabled: a = !0, ...o } = i, s = a ? t(o) : null;
|
|
8
|
-
r(() => {
|
|
9
|
-
if (s !== null) return n(s, e);
|
|
10
|
-
}, [s]);
|
|
11
|
-
}
|
|
12
|
-
//#endregion
|
|
13
|
-
export { i as useMarkup };
|
|
14
|
-
|
|
1
|
+
"use client";import{init as e,t}from"./widget.js";import{t as n}from"./shared-mount-B9ij4vuL.js";import{useEffect as r}from"react";function i(i){let{enabled:a=!0,...o}=i,s=a?t(o):null;r(()=>{if(s!==null)return n(s,e)},[s])}export{i as useMarkup};
|
|
15
2
|
//# sourceMappingURL=react.js.map
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","names":[],"sources":["../src/react.ts"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\n\nimport { configKey } from './runtime/config-key'\nimport { acquireMount } from './runtime/shared-mount'\nimport { init, type WidgetConfig } from './widget'\n\nexport type { WidgetConfig } from './widget'\n\nexport type UseMarkupOptions = WidgetConfig & {\n /**\n * `false` unmounts the widget, or never mounts it. Drive it from your build\n * environment to keep the widget off production.\n *\n * @default true\n */\n enabled?: boolean\n}\n\n/**\n * Mounts the widget for as long as the calling component is mounted. Call it\n * once near your app root.\n *\n * An inline options object is fine: the widget remounts only when a value\n * changes, not when the object is recreated. Two components using the same\n * options share one widget, and it stays until both unmount. Server rendering\n * mounts nothing.\n */\nexport function useMarkup(options: UseMarkupOptions): void {\n const { enabled = true, ...config } = options\n const key = enabled ? configKey(config) : null\n\n useEffect(() => {\n if (key === null) return\n return acquireMount(key, init)\n }, [key])\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"react.js","names":[],"sources":["../src/react.ts"],"sourcesContent":["'use client'\n\nimport { useEffect } from 'react'\n\nimport { configKey } from './runtime/config-key'\nimport { acquireMount } from './runtime/shared-mount'\nimport { init, type WidgetConfig } from './widget'\n\nexport type { WidgetConfig } from './widget'\n\nexport type UseMarkupOptions = WidgetConfig & {\n /**\n * `false` unmounts the widget, or never mounts it. Drive it from your build\n * environment to keep the widget off production.\n *\n * @default true\n */\n enabled?: boolean\n}\n\n/**\n * Mounts the widget for as long as the calling component is mounted. Call it\n * once near your app root.\n *\n * An inline options object is fine: the widget remounts only when a value\n * changes, not when the object is recreated. Two components using the same\n * options share one widget, and it stays until both unmount. Server rendering\n * mounts nothing.\n */\nexport function useMarkup(options: UseMarkupOptions): void {\n const { enabled = true, ...config } = options\n const key = enabled ? configKey(config) : null\n\n useEffect(() => {\n if (key === null) return\n return acquireMount(key, init)\n }, [key])\n}\n"],"mappings":"mIA6BA,SAAgB,EAAU,EAAiC,CACzD,GAAM,CAAE,UAAU,GAAM,GAAG,GAAW,EAChC,EAAM,EAAU,EAAU,CAAM,EAAI,KAE1C,MAAgB,CACV,OAAQ,KACZ,OAAO,EAAa,EAAK,CAAI,CAC/B,EAAG,CAAC,CAAG,CAAC,CACV"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=/* @__PURE__ */ new Map;function t(t,n){let r=n(JSON.parse(t)),i=(e.get(t)?.count??0)+1;e.delete(t),e.set(t,{count:i,dispose:r});let a=!1;return()=>{if(a)return;a=!0;let r=e.get(t);if(!r||(--r.count,r.count>0))return;e.delete(t),r.dispose();let i=[...e.entries()].at(-1);if(i)try{i[1].dispose=n(JSON.parse(i[0]))}catch(e){i[1].dispose=()=>{},console.warn(`[markup] could not remount the widget`,e)}}}export{t};
|
|
2
|
+
//# sourceMappingURL=shared-mount-B9ij4vuL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-mount-
|
|
1
|
+
{"version":3,"file":"shared-mount-B9ij4vuL.js","names":[],"sources":["../src/runtime/shared-mount.ts"],"sourcesContent":["import type { WidgetConfig } from '../widget'\n\ntype Holder = { count: number; dispose: () => void }\n\nconst holders = new Map<string, Holder>()\n\n/**\n * Counts hook holders per config, so the first of two components to unmount\n * doesn't take the widget away from the other. `init` still runs on every\n * acquire: it is a no-op for the live config, and it remounts one that a\n * different config has since replaced.\n *\n * Holders of different configs contend for the one widget, and the latest to\n * acquire wins. When it lets go, the most recent remaining config is mounted\n * again, so no holder is left without a widget while it still wants one.\n */\nexport function acquireMount(key: string, init: (config: WidgetConfig) => () => void): () => void {\n const dispose = init(JSON.parse(key) as WidgetConfig)\n const count = (holders.get(key)?.count ?? 0) + 1\n // Re-inserted so the map's order is recency, which the release path reads.\n holders.delete(key)\n holders.set(key, { count, dispose })\n\n let isReleased = false\n return () => {\n if (isReleased) return\n isReleased = true\n const current = holders.get(key)\n if (!current) return\n current.count -= 1\n if (current.count > 0) return\n holders.delete(key)\n current.dispose()\n const survivor = [...holders.entries()].at(-1)\n if (!survivor) return\n // This runs inside some other component's cleanup; a failure to bring\n // the survivor back must not surface as that component's error.\n try {\n survivor[1].dispose = init(JSON.parse(survivor[0]) as WidgetConfig)\n } catch (error) {\n survivor[1].dispose = () => {}\n console.warn('[markup] could not remount the widget', error)\n }\n }\n}\n"],"mappings":"AAIA,IAAM,kBAAU,IAAI,IAYpB,SAAgB,EAAa,EAAa,EAAwD,CAChG,IAAM,EAAU,EAAK,KAAK,MAAM,CAAG,CAAiB,EAC9C,GAAS,EAAQ,IAAI,CAAG,CAAC,EAAE,OAAS,GAAK,EAE/C,EAAQ,OAAO,CAAG,EAClB,EAAQ,IAAI,EAAK,CAAE,QAAO,SAAQ,CAAC,EAEnC,IAAI,EAAa,GACjB,UAAa,CACX,GAAI,EAAY,OAChB,EAAa,GACb,IAAM,EAAU,EAAQ,IAAI,CAAG,EAG/B,GAFI,CAAC,IACL,IAAQ,MACJ,EAAQ,MAAQ,GAAG,OACvB,EAAQ,OAAO,CAAG,EAClB,EAAQ,QAAQ,EAChB,IAAM,EAAW,CAAC,GAAG,EAAQ,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,EACxC,KAGL,GAAI,CACF,EAAS,EAAE,CAAC,QAAU,EAAK,KAAK,MAAM,EAAS,EAAE,CAAiB,CACpE,OAAS,EAAO,CACd,EAAS,EAAE,CAAC,YAAgB,CAAC,EAC7B,QAAQ,KAAK,wCAAyC,CAAK,CAC7D,CACF,CACF"}
|
package/dist/vue.js
CHANGED
|
@@ -1,21 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { t as n } from "./shared-mount-DABK4arh.js";
|
|
3
|
-
import { computed as r, onBeforeUnmount as i, onMounted as a, toValue as o, watch as s } from "vue";
|
|
4
|
-
//#region src/vue.ts
|
|
5
|
-
function c(c) {
|
|
6
|
-
let l = r(() => {
|
|
7
|
-
let { enabled: e = !0, ...n } = o(c);
|
|
8
|
-
return e ? t(n) : null;
|
|
9
|
-
}), u = null, d = (t) => {
|
|
10
|
-
u?.(), u = t === null ? null : n(t, e);
|
|
11
|
-
}, f = null;
|
|
12
|
-
a(() => {
|
|
13
|
-
d(l.value), f = s(l, d);
|
|
14
|
-
}), i(() => {
|
|
15
|
-
f?.(), d(null);
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
//#endregion
|
|
19
|
-
export { c as useMarkup };
|
|
20
|
-
|
|
1
|
+
import{init as e,t}from"./widget.js";import{t as n}from"./shared-mount-B9ij4vuL.js";import{computed as r,onBeforeUnmount as i,onMounted as a,toValue as o,watch as s}from"vue";function c(c){let l=r(()=>{let{enabled:e=!0,...n}=o(c);return e?t(n):null}),u=null,d=t=>{u?.(),u=t===null?null:n(t,e)},f=null;a(()=>{d(l.value),f=s(l,d)}),i(()=>{f?.(),d(null)})}export{c as useMarkup};
|
|
21
2
|
//# sourceMappingURL=vue.js.map
|
package/dist/vue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.js","names":[],"sources":["../src/vue.ts"],"sourcesContent":["import { computed, onBeforeUnmount, onMounted, toValue, watch, type MaybeRefOrGetter } from 'vue'\n\nimport { configKey } from './runtime/config-key'\nimport { acquireMount } from './runtime/shared-mount'\nimport { init, type WidgetConfig } from './widget'\n\nexport type { WidgetConfig } from './widget'\n\nexport type UseMarkupOptions = WidgetConfig & {\n /**\n * `false` unmounts the widget, or never mounts it. Drive it from your build\n * environment to keep the widget off production.\n *\n * @default true\n */\n enabled?: boolean\n}\n\n/**\n * Mounts the widget for as long as the calling component is mounted. Call it\n * once from your root component's `setup`.\n *\n * Pass a plain object, a ref, or a getter. The widget remounts only when a\n * value changes, not when the object is replaced by an equal one. Two\n * components using the same options share one widget, and it stays until\n * both unmount. Nothing mounts during server rendering, since mounting waits\n * for `onMounted`.\n */\nexport function useMarkup(options: MaybeRefOrGetter<UseMarkupOptions>): void {\n const key = computed(() => {\n const { enabled = true, ...config } = toValue(options)\n return enabled ? configKey(config) : null\n })\n\n let release: (() => void) | null = null\n const apply = (next: string | null) => {\n release?.()\n release = next === null ? null : acquireMount(next, init)\n }\n\n let stopWatching: (() => void) | null = null\n onMounted(() => {\n apply(key.value)\n stopWatching = watch(key, apply)\n })\n onBeforeUnmount(() => {\n stopWatching?.()\n apply(null)\n })\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"vue.js","names":[],"sources":["../src/vue.ts"],"sourcesContent":["import { computed, onBeforeUnmount, onMounted, toValue, watch, type MaybeRefOrGetter } from 'vue'\n\nimport { configKey } from './runtime/config-key'\nimport { acquireMount } from './runtime/shared-mount'\nimport { init, type WidgetConfig } from './widget'\n\nexport type { WidgetConfig } from './widget'\n\nexport type UseMarkupOptions = WidgetConfig & {\n /**\n * `false` unmounts the widget, or never mounts it. Drive it from your build\n * environment to keep the widget off production.\n *\n * @default true\n */\n enabled?: boolean\n}\n\n/**\n * Mounts the widget for as long as the calling component is mounted. Call it\n * once from your root component's `setup`.\n *\n * Pass a plain object, a ref, or a getter. The widget remounts only when a\n * value changes, not when the object is replaced by an equal one. Two\n * components using the same options share one widget, and it stays until\n * both unmount. Nothing mounts during server rendering, since mounting waits\n * for `onMounted`.\n */\nexport function useMarkup(options: MaybeRefOrGetter<UseMarkupOptions>): void {\n const key = computed(() => {\n const { enabled = true, ...config } = toValue(options)\n return enabled ? configKey(config) : null\n })\n\n let release: (() => void) | null = null\n const apply = (next: string | null) => {\n release?.()\n release = next === null ? null : acquireMount(next, init)\n }\n\n let stopWatching: (() => void) | null = null\n onMounted(() => {\n apply(key.value)\n stopWatching = watch(key, apply)\n })\n onBeforeUnmount(() => {\n stopWatching?.()\n apply(null)\n })\n}\n"],"mappings":"+KA4BA,SAAgB,EAAU,EAAmD,CAC3E,IAAM,EAAM,MAAe,CACzB,GAAM,CAAE,UAAU,GAAM,GAAG,GAAW,EAAQ,CAAO,EACrD,OAAO,EAAU,EAAU,CAAM,EAAI,IACvC,CAAC,EAEG,EAA+B,KAC7B,EAAS,GAAwB,CACrC,IAAU,EACV,EAAU,IAAS,KAAO,KAAO,EAAa,EAAM,CAAI,CAC1D,EAEI,EAAoC,KACxC,MAAgB,CACd,EAAM,EAAI,KAAK,EACf,EAAe,EAAM,EAAK,CAAK,CACjC,CAAC,EACD,MAAsB,CACpB,IAAe,EACf,EAAM,IAAI,CACZ,CAAC,CACH"}
|
package/dist/widget.d.ts
CHANGED
|
@@ -132,9 +132,10 @@ export interface WidgetConfig {
|
|
|
132
132
|
analytics?: boolean;
|
|
133
133
|
}
|
|
134
134
|
/**
|
|
135
|
-
* Mounts the feedback widget on the page
|
|
136
|
-
*
|
|
137
|
-
*
|
|
135
|
+
* Mounts the feedback widget on the page once it is idle, or after
|
|
136
|
+
* `IDLE_MOUNT_TIMEOUT_MS` on a page that never is. Idempotent — calling init
|
|
137
|
+
* again with the same config is a no-op; calling with different config tears
|
|
138
|
+
* down the previous instance first. A host removed from the document by the page
|
|
138
139
|
* (a body swap on navigation, say) is remounted even when the config matches.
|
|
139
140
|
*
|
|
140
141
|
* Returns a function that unmounts this instance and removes its host
|