@tonytangdev/pin-point 0.1.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 ADDED
@@ -0,0 +1,119 @@
1
+ # pin-point
2
+
3
+ Visual feedback overlay for React. Drop pins on any page, leave comments.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install pin-point
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ import { FeedbackOverlay } from 'pin-point';
15
+ import 'pin-point/styles.css';
16
+
17
+ function App() {
18
+ return (
19
+ <FeedbackOverlay
20
+ onCommentCreate={async (comment, authHeaders) => {
21
+ // Send to your backend with auth headers
22
+ }}
23
+ onCommentsFetch={async (authHeaders) => {
24
+ // Fetch from your backend
25
+ return [];
26
+ }}
27
+ >
28
+ <YourApp />
29
+ </FeedbackOverlay>
30
+ );
31
+ }
32
+ ```
33
+
34
+ The toolbar is always visible when `FeedbackOverlay` is mounted. Anonymous users can view existing comments but cannot create new ones. To leave feedback, a user needs either:
35
+
36
+ - A **feedback link** — a URL containing `?pin-token=<id>`, generated by an admin and shared with reviewers.
37
+ - The **admin key** — paste the admin secret via the toolbar's key icon. It's stored in `localStorage`.
38
+
39
+ Admins generate shareable feedback links from the toolbar's Share button.
40
+
41
+ ### With pin-point-server
42
+
43
+ If you don't want to build your own backend, use [pin-point-server](https://www.npmjs.com/package/pin-point-server):
44
+
45
+ ```bash
46
+ npx pin-point-server
47
+ ```
48
+
49
+ ```tsx
50
+ const API = 'http://localhost:3000';
51
+
52
+ <FeedbackOverlay
53
+ onCommentCreate={async (comment, authHeaders) => {
54
+ await fetch(`${API}/comments`, {
55
+ method: 'POST',
56
+ headers: { 'Content-Type': 'application/json', ...authHeaders },
57
+ body: JSON.stringify(comment),
58
+ });
59
+ }}
60
+ onCommentsFetch={async (authHeaders) => {
61
+ const res = await fetch(
62
+ `${API}/comments?url=${location.pathname}`,
63
+ { headers: authHeaders },
64
+ );
65
+ return res.json();
66
+ }}
67
+ onAdminValidate={async (secret) => {
68
+ const res = await fetch(`${API}/admin/tokens`, {
69
+ headers: { 'X-Pin-Admin': secret },
70
+ });
71
+ return res.ok;
72
+ }}
73
+ onShareLinkCreate={async (label, expiresInHours, authHeaders) => {
74
+ const res = await fetch(`${API}/admin/tokens`, {
75
+ method: 'POST',
76
+ headers: { 'Content-Type': 'application/json', ...authHeaders },
77
+ body: JSON.stringify({ label, expiresInHours }),
78
+ });
79
+ const token = await res.json();
80
+ return { tokenId: token.id };
81
+ }}
82
+ >
83
+ <YourApp />
84
+ </FeedbackOverlay>
85
+ ```
86
+
87
+ ## Props
88
+
89
+ All callbacks receive an `authHeaders` object containing the relevant auth header (`X-Pin-Token` or `X-Pin-Admin`) based on the current user's auth state. Spread it into your `fetch` headers.
90
+
91
+ | Prop | Type | Required | Description |
92
+ |------|------|----------|-------------|
93
+ | `onCommentCreate` | `(comment: PinComment, authHeaders: Record<string, string>) => Promise<void>` | Yes | Called when user submits a comment |
94
+ | `onCommentsFetch` | `(authHeaders: Record<string, string>) => Promise<PinComment[]>` | Yes | Called on mount to load existing comments |
95
+ | `onCommentDelete` | `(id: string, authHeaders: Record<string, string>) => Promise<void>` | No | Called when an admin deletes a comment |
96
+ | `onCommentUpdate` | `(id: string, content: string, authHeaders: Record<string, string>) => Promise<PinComment>` | No | Called when an admin edits a comment |
97
+ | `onAdminValidate` | `(secret: string) => Promise<boolean>` | No | Validates an admin key entered via the toolbar |
98
+ | `onShareLinkCreate` | `(label: string, expiresInHours: number \| null, authHeaders: Record<string, string>) => Promise<{ tokenId: string }>` | No | Mints a feedback-link token (admin-only) |
99
+
100
+ ## PinComment
101
+
102
+ ```ts
103
+ type PinComment = {
104
+ id: string;
105
+ url: string;
106
+ content: string;
107
+ anchor: {
108
+ selector: string;
109
+ xPercent: number;
110
+ yPercent: number;
111
+ };
112
+ viewport: { width: number };
113
+ createdAt: string;
114
+ };
115
+ ```
116
+
117
+ ## License
118
+
119
+ MIT
@@ -0,0 +1,32 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type PinComment = {
4
+ id: string;
5
+ url: string;
6
+ content: string;
7
+ anchor: {
8
+ selector: string;
9
+ xPercent: number;
10
+ yPercent: number;
11
+ };
12
+ viewport: {
13
+ width: number;
14
+ };
15
+ createdAt: string;
16
+ };
17
+ type AuthHeaders = Record<string, string>;
18
+ type FeedbackOverlayProps = {
19
+ onCommentCreate: (comment: PinComment, authHeaders: AuthHeaders) => Promise<void>;
20
+ onCommentsFetch: (authHeaders: AuthHeaders) => Promise<PinComment[]>;
21
+ onCommentDelete?: (id: string, authHeaders: AuthHeaders) => Promise<void>;
22
+ onCommentUpdate?: (id: string, content: string, authHeaders: AuthHeaders) => Promise<PinComment>;
23
+ onAdminValidate?: (secret: string) => Promise<boolean>;
24
+ onShareLinkCreate?: (label: string | undefined, expiresInHours: number | undefined, authHeaders: AuthHeaders) => Promise<{
25
+ tokenId: string;
26
+ }>;
27
+ children: React.ReactNode;
28
+ };
29
+
30
+ declare function FeedbackOverlay({ onCommentCreate, onCommentsFetch, onCommentDelete, onCommentUpdate, onAdminValidate, onShareLinkCreate, children, }: FeedbackOverlayProps): react_jsx_runtime.JSX.Element;
31
+
32
+ export { FeedbackOverlay, type FeedbackOverlayProps, type PinComment };
@@ -0,0 +1,32 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type PinComment = {
4
+ id: string;
5
+ url: string;
6
+ content: string;
7
+ anchor: {
8
+ selector: string;
9
+ xPercent: number;
10
+ yPercent: number;
11
+ };
12
+ viewport: {
13
+ width: number;
14
+ };
15
+ createdAt: string;
16
+ };
17
+ type AuthHeaders = Record<string, string>;
18
+ type FeedbackOverlayProps = {
19
+ onCommentCreate: (comment: PinComment, authHeaders: AuthHeaders) => Promise<void>;
20
+ onCommentsFetch: (authHeaders: AuthHeaders) => Promise<PinComment[]>;
21
+ onCommentDelete?: (id: string, authHeaders: AuthHeaders) => Promise<void>;
22
+ onCommentUpdate?: (id: string, content: string, authHeaders: AuthHeaders) => Promise<PinComment>;
23
+ onAdminValidate?: (secret: string) => Promise<boolean>;
24
+ onShareLinkCreate?: (label: string | undefined, expiresInHours: number | undefined, authHeaders: AuthHeaders) => Promise<{
25
+ tokenId: string;
26
+ }>;
27
+ children: React.ReactNode;
28
+ };
29
+
30
+ declare function FeedbackOverlay({ onCommentCreate, onCommentsFetch, onCommentDelete, onCommentUpdate, onAdminValidate, onShareLinkCreate, children, }: FeedbackOverlayProps): react_jsx_runtime.JSX.Element;
31
+
32
+ export { FeedbackOverlay, type FeedbackOverlayProps, type PinComment };