@aeyon-studios/pulse-spot
v0.6.11
Published
Embeddable website-proofing widget for Pulse. Drag a region on any page, comment, and create a card.
Readme
@aeyon-studios/pulse-spot
Embeddable website-proofing widget for Pulse. Drop it onto any site (staging especially), drag a box over a region, write a comment, and it creates a card on a Pulse board — with the cropped screenshot attached and the page URL recorded.
- Framework-agnostic — a vanilla Web Component (Shadow DOM), so it can't be styled by, or leak styles into, the host page. Works on Next.js, Nuxt, plain HTML, anything that runs in a browser.
- SSR-safe — importing it on the server is a no-op;
init()only does anything in a browser. - ~45 KB gzipped, single self-contained file (screenshot engine bundled).
Install
npm install @aeyon-studios/pulse-spotNext.js
Initialise on the client only (it touches window):
"use client";
import { destroy, init } from "@aeyon-studios/pulse-spot";
import { useEffect } from "react";
export function Proofing() {
useEffect(() => {
// Gate to staging so it never ships to production.
if (process.env.NEXT_PUBLIC_ENV !== "staging") return;
init({
baseUrl: "https://pulse.example.com",
boardPublicId: "your_board_public_id",
});
return () => destroy();
}, []);
return null;
}Nuxt
// plugins/proofing.client.ts (the .client suffix keeps it out of SSR)
import { init } from "@aeyon-studios/pulse-spot";
export default defineNuxtPlugin(() => {
if (import.meta.env.VITE_ENV !== "staging") return;
init({
baseUrl: "https://pulse.example.com",
boardPublicId: "your_board_public_id",
});
});Plain <script> (no build step)
<script src="https://unpkg.com/@aeyon-studios/pulse-spot"></script>
<script>
PulseSpot.init({
baseUrl: "https://pulse.example.com",
boardPublicId: "your_board_public_id",
});
</script>Configuration
init({
baseUrl: string, // required — your Pulse instance URL
boardPublicId: string, // required — board that feedback cards land on
shortcut?: string, // default "mod+shift+k" (mod = ⌘ on macOS, Ctrl elsewhere)
launcher?: {
enabled?: boolean, // default true — show the floating button
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left",
trigger?: "always" | "shortcut", // default "always" — see below
},
lists?: { // restrict which board lists the form offers
include?: string[], // only these list names (case-insensitive)
exclude?: string[], // hide these list names (case-insensitive)
default?: string, // list name to preselect (case-insensitive)
},
pins?: {
enabled?: boolean, // default true — record anchors, show the Review toggle
},
disableOnTouch?: boolean, // default true — skip phones/tablets
onError?: (error: Error) => void,
});Keeping the buttons out of the way
By default the launcher sits in the corner for the whole session. Set
trigger: "shortcut" to start it hidden and let the keyboard shortcut summon and
dismiss it — useful when the host page is being reviewed for design and a
permanent button gets in the shot:
init({
baseUrl: "https://pulse.example.com",
boardPublicId: "your_board_public_id",
launcher: { trigger: "shortcut" },
});Captures then start from the Add feedback button; the shortcut only shows and
hides. With enabled: false there are no buttons to summon, so the shortcut
keeps its default job of starting a capture directly.
Example — only let reviewers file into "Todo" or "Questions", default "Todo":
init({
baseUrl: "https://pulse.example.com",
boardPublicId: "your_board_public_id",
lists: {
exclude: ["progress", "in review", "done", "backlog"],
default: "todo",
},
});destroy() removes the widget and all of its listeners.
How it works
- Reviewer clicks the floating Add feedback button (or presses
⌘⇧K) and drags a region. - The region is screenshotted (cropped) client-side.
- Sign in with Pulse — a popup authenticates the reviewer and returns a short-lived token. Cards are attributed to that Pulse user.
- The reviewer picks a list, writes a title/description, and optionally notifies teammates, then submits. A card is created with the screenshot attached.
Pulse server setup
The widget talks to Pulse's REST API (/api/v1) and a small popup auth page
(/widget-auth). Both are part of the Pulse app. Configure:
WIDGET_ALLOWED_ORIGINS— comma-separated list of origins allowed to embed the widget and request tokens, e.g.https://staging.example.com. Required in production; when unset, onlylocalhostis permitted (for local dev).
Host-site requirements (CSP & storage CORS)
If the host site sets a strict Content-Security-Policy, allow the widget to load and reach Pulse:
script-src ... https://pulse.example.com https://unpkg.com ;
connect-src ... https://pulse.example.com https://<your-s3-or-storage-host> ;
img-src ... blob: ;
style-src ... 'unsafe-inline' ; /* the widget injects scoped styles */Screenshots upload directly to your storage via a presigned URL, so your S3 /
storage bucket's CORS must allow PUT from the staging origin.
Caveats
- Screenshot fidelity depends on client-side DOM→image rendering. Cross-origin images/iframes without permissive CORS may render blank in the capture.
- The reviewer's Pulse account must be a member (not a guest) of the board's workspace to create cards.
