@sylergydigital/issue-pin-sdk 0.6.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/LICENSE +74 -0
- package/README.md +376 -0
- package/dist/index.cjs +2500 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +282 -0
- package/dist/index.d.ts +282 -0
- package/dist/index.js +2451 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
Issue Pin SDK Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sylergy Digital. All rights reserved.
|
|
4
|
+
|
|
5
|
+
Permission is granted to any person or entity obtaining a copy of this software
|
|
6
|
+
and associated documentation files (the "Software") to use the Software solely
|
|
7
|
+
as compiled code or as installed from an authorized package registry, subject to
|
|
8
|
+
the conditions below.
|
|
9
|
+
|
|
10
|
+
1. Permitted Use
|
|
11
|
+
|
|
12
|
+
You may:
|
|
13
|
+
|
|
14
|
+
- use the Software internally within your own applications and systems;
|
|
15
|
+
- integrate the unmodified Software into applications you own or operate;
|
|
16
|
+
- make a reasonable number of copies as necessary for backup, deployment, and
|
|
17
|
+
internal development;
|
|
18
|
+
- modify the Software only for your own internal use, provided such
|
|
19
|
+
modifications are not distributed or disclosed outside your organization.
|
|
20
|
+
|
|
21
|
+
2. Restrictions
|
|
22
|
+
|
|
23
|
+
You may not:
|
|
24
|
+
|
|
25
|
+
- sell, sublicense, redistribute, publish, or otherwise make the Software
|
|
26
|
+
available to any third party except as embedded in your own application in a
|
|
27
|
+
manner that does not expose the Software itself for reuse;
|
|
28
|
+
- distribute source code of the Software, including modified versions;
|
|
29
|
+
- offer the Software on a standalone basis;
|
|
30
|
+
- use the Software to provide a competing SDK, library, or hosted service;
|
|
31
|
+
- remove or alter any copyright, trademark, or proprietary notices;
|
|
32
|
+
- claim that the Software is open source.
|
|
33
|
+
|
|
34
|
+
3. Ownership
|
|
35
|
+
|
|
36
|
+
The Software is licensed, not sold. Sylergy Digital and its licensors retain
|
|
37
|
+
all right, title, and interest in and to the Software, including all
|
|
38
|
+
intellectual property rights.
|
|
39
|
+
|
|
40
|
+
4. Feedback
|
|
41
|
+
|
|
42
|
+
If you provide feedback, suggestions, or ideas regarding the Software, Sylergy
|
|
43
|
+
Digital may use them without restriction or obligation to you.
|
|
44
|
+
|
|
45
|
+
5. No Warranty
|
|
46
|
+
|
|
47
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
48
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
49
|
+
FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
|
|
50
|
+
|
|
51
|
+
6. Limitation of Liability
|
|
52
|
+
|
|
53
|
+
TO THE MAXIMUM EXTENT PERMITTED BY LAW, SYLERGY DIGITAL WILL NOT BE LIABLE FOR
|
|
54
|
+
ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE
|
|
55
|
+
DAMAGES, OR FOR ANY LOSS OF DATA, PROFITS, REVENUE, OR BUSINESS OPPORTUNITY,
|
|
56
|
+
ARISING OUT OF OR RELATED TO THE SOFTWARE OR THIS LICENSE, EVEN IF ADVISED OF
|
|
57
|
+
THE POSSIBILITY OF SUCH DAMAGES. IN NO EVENT WILL SYLERGY DIGITAL'S TOTAL
|
|
58
|
+
LIABILITY EXCEED ONE HUNDRED U.S. DOLLARS (USD $100).
|
|
59
|
+
|
|
60
|
+
7. Termination
|
|
61
|
+
|
|
62
|
+
This license terminates automatically if you violate any term of this license.
|
|
63
|
+
Upon termination, you must stop using the Software and destroy all copies in
|
|
64
|
+
your possession or control, except where retention is required by law.
|
|
65
|
+
|
|
66
|
+
8. Governing Law
|
|
67
|
+
|
|
68
|
+
This license is governed by the laws of the jurisdiction in which Sylergy
|
|
69
|
+
Digital is established, excluding conflict-of-law rules.
|
|
70
|
+
|
|
71
|
+
9. Contact
|
|
72
|
+
|
|
73
|
+
For commercial licensing, redistribution, or other permissions, contact
|
|
74
|
+
Sylergy Digital.
|
package/README.md
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
# @sylergydigital/issue-pin-sdk
|
|
2
|
+
|
|
3
|
+
In-app visual feedback SDK for Issue Pin.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### 1. Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @sylergydigital/issue-pin-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### 2. Peer dependencies
|
|
14
|
+
|
|
15
|
+
Ensure these are installed in your app:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install react react-dom @supabase/supabase-js html2canvas lucide-react
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The public GitHub repo at `sylergydigital/issue-pin-sdk` is a read-only mirror. Source changes should be made in the private source repo and will sync across automatically.
|
|
22
|
+
|
|
23
|
+
`react-router-dom` is **not** required. The SDK tracks `window.location` for thread fetching and `?highlight_thread=` without router context.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
The SDK is **disabled by default**. Pass `enabled={true}` (or bind it to state) to activate the feedback UI.
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { useState } from "react";
|
|
31
|
+
import { IssuePin } from "@sylergydigital/issue-pin-sdk";
|
|
32
|
+
import { supabase } from "./lib/supabase"; // your app's Supabase client
|
|
33
|
+
|
|
34
|
+
function App() {
|
|
35
|
+
const [feedbackOn, setFeedbackOn] = useState(false);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<>
|
|
39
|
+
<nav>
|
|
40
|
+
<button onClick={() => setFeedbackOn(f => !f)}>
|
|
41
|
+
💬 Feedback
|
|
42
|
+
</button>
|
|
43
|
+
</nav>
|
|
44
|
+
|
|
45
|
+
<Home />
|
|
46
|
+
|
|
47
|
+
{/* Recommended: pass supabaseClient for automatic user identity */}
|
|
48
|
+
<IssuePin
|
|
49
|
+
apiKey="ew_live_..."
|
|
50
|
+
enabled={feedbackOn}
|
|
51
|
+
supabaseClient={supabase}
|
|
52
|
+
/>
|
|
53
|
+
</>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Props
|
|
59
|
+
|
|
60
|
+
| Prop | Type | Default | Description |
|
|
61
|
+
|------|------|---------|-------------|
|
|
62
|
+
| `apiKey` | `string` | — | **Required.** Your workspace API key |
|
|
63
|
+
| `enabled` | `boolean` | `false` | Toggle feedback UI visibility without unmounting |
|
|
64
|
+
| `supabaseClient` | `SupabaseClient` | — | **Recommended.** Your app's Supabase client — auto-extracts user identity from the auth session and auto-federates users into the workspace |
|
|
65
|
+
| `user.id` | `string` | — | Manual user ID for attribution (overrides auto-detect) |
|
|
66
|
+
| `user.email` | `string` | — | Manual user email (overrides auto-detect) |
|
|
67
|
+
| `user.displayName` | `string` | — | Name shown on comments (overrides auto-detect) |
|
|
68
|
+
| `allowPinOnPage` | `boolean` | `true` | Allow creating new element-based pins from the launcher |
|
|
69
|
+
| `allowScreenshot` | `boolean` | `true` | Allow screenshot capture from the launcher |
|
|
70
|
+
| `showHints` | `boolean` | `true` | Show SDK-owned instructional hints and coachmarks, including the "Open Issue Pin" toast |
|
|
71
|
+
| `scrollContainer` | `{ key: string; ref: RefObject<HTMLElement> }` | — | Store and render new element pins in a host-owned scroll container |
|
|
72
|
+
| `resolveAnchor` | `(target: Element) => { key: string; selector?: string \| null } \| null` | — | Resolve a stable logical anchor key for clicked elements |
|
|
73
|
+
| `pinsVisible` | `boolean` | `true` | Show historical thread pins independently from the rest of the SDK UI |
|
|
74
|
+
| `buttonPosition` | `"bottom-right" \| "bottom-left"` | `"bottom-right"` | Floating button position |
|
|
75
|
+
| `renderLauncher` | `(props) => ReactNode` | — | Replace the stock launcher while keeping SDK-owned state/actions |
|
|
76
|
+
| `renderModalCaptureButton` | `(props) => ReactNode` | — | Replace the default screenshot button injected into open dialogs |
|
|
77
|
+
| `showModalCaptureButton` | `boolean` | `true` | Control whether modal screenshot buttons are injected |
|
|
78
|
+
| `mode` | `"view" \| "annotate"` | — | **Controlled** annotate mode — use with `onModeChange` |
|
|
79
|
+
| `onModeChange` | `(mode: FeedbackMode) => void` | — | Called when annotate mode changes |
|
|
80
|
+
| `feedbackActive` | `boolean` | — | **Deprecated.** Use `mode`: `annotate` ↔ `true`, `view` ↔ `false` |
|
|
81
|
+
| `onFeedbackActiveChange` | `(active: boolean) => void` | — | **Deprecated.** Use `onModeChange` |
|
|
82
|
+
|
|
83
|
+
### Migration (v0.x → coordinate-first pins)
|
|
84
|
+
|
|
85
|
+
- `x_position` / `y_position` remain the persisted source of truth.
|
|
86
|
+
- Element pins now resolve in this order: `anchor_key` → `selector` → stored coordinates.
|
|
87
|
+
- Without `scrollContainer`, element pins stay document-relative. With `scrollContainer`, new element pins are stored and rendered in that container's coordinate space.
|
|
88
|
+
- Prefer **`mode` / `onModeChange`** instead of **`feedbackActive` / `onFeedbackActiveChange`** for clarity.
|
|
89
|
+
- No `<BrowserRouter>` wrapper is required for the SDK. The SDK listens to `history.pushState` / `replaceState` and `popstate` so thread fetching and `?highlight_thread=` stay in sync in SPAs without importing React Router.
|
|
90
|
+
|
|
91
|
+
### TypeScript
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import type { FeedbackMode } from "@sylergydigital/issue-pin-sdk";
|
|
95
|
+
// FeedbackMode = "view" | "annotate"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Logical anchors in a scroll container
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { useRef } from "react";
|
|
102
|
+
import { IssuePin, useIssuePinAnchor } from "@sylergydigital/issue-pin-sdk";
|
|
103
|
+
|
|
104
|
+
function VisitRow({ visit }: { visit: { id: string; name: string } }) {
|
|
105
|
+
const ref = useIssuePinAnchor(`visit-row:${visit.id}`);
|
|
106
|
+
return <div ref={ref} data-visit-id={visit.id}>{visit.name}</div>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function AppShell() {
|
|
110
|
+
const mainRef = useRef<HTMLDivElement>(null);
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<div ref={mainRef} style={{ overflow: "auto", height: 600 }}>
|
|
115
|
+
<VisitRow visit={{ id: "123", name: "Annual review" }} />
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<IssuePin
|
|
119
|
+
apiKey="ew_live_..."
|
|
120
|
+
enabled
|
|
121
|
+
scrollContainer={{ key: "dashboard-main", ref: mainRef }}
|
|
122
|
+
resolveAnchor={(target) => {
|
|
123
|
+
const row = target.closest("[data-visit-id]");
|
|
124
|
+
if (!row) return null;
|
|
125
|
+
return {
|
|
126
|
+
key: `visit-row:${row.getAttribute("data-visit-id")}`,
|
|
127
|
+
};
|
|
128
|
+
}}
|
|
129
|
+
/>
|
|
130
|
+
</>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Layer stacking (z-index)
|
|
136
|
+
|
|
137
|
+
UI layers use a single internal scale exported as **`Z`** (pins → overlay → popover → launcher → screenshot modal → flash). If your app uses very high `z-index` values (e.g. `99999` modals), raise them above `Z.launcher` (or import `Z` and position relative to it):
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { Z } from "@sylergydigital/issue-pin-sdk";
|
|
141
|
+
|
|
142
|
+
// Example: host modal above the SDK launcher
|
|
143
|
+
<div style={{ zIndex: Z.launcher + 1 }}>...</div>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Controlling visibility
|
|
147
|
+
|
|
148
|
+
Since `enabled` defaults to `false`, the SDK mounts its provider (preserving state and subscriptions) but renders no UI until activated. Common patterns:
|
|
149
|
+
|
|
150
|
+
### Navbar toggle (recommended)
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
const [feedbackOn, setFeedbackOn] = useState(false);
|
|
154
|
+
|
|
155
|
+
<button onClick={() => setFeedbackOn(f => !f)}>Feedback</button>
|
|
156
|
+
<IssuePin apiKey="ew_live_..." enabled={feedbackOn} />
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Role-based
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
{["qa", "admin"].includes(currentUser.role) && (
|
|
163
|
+
<IssuePin apiKey="ew_live_..." enabled={feedbackOn} user={currentUser} />
|
|
164
|
+
)}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Environment-based
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
{import.meta.env.VITE_APP_ENV !== "production" && (
|
|
171
|
+
<IssuePin apiKey="ew_live_..." enabled={feedbackOn} />
|
|
172
|
+
)}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Advanced usage
|
|
176
|
+
|
|
177
|
+
Use the built-in customization props first. Composing low-level SDK internals directly is no longer the recommended way to customize launcher behavior.
|
|
178
|
+
|
|
179
|
+
### Launcher action flags
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
<IssuePin
|
|
183
|
+
apiKey="ew_live_..."
|
|
184
|
+
supabaseClient={supabase}
|
|
185
|
+
enabled={feedbackOn}
|
|
186
|
+
allowPinOnPage={false}
|
|
187
|
+
/>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
If only one launcher action is enabled, the stock launcher triggers it directly instead of opening the menu.
|
|
191
|
+
|
|
192
|
+
### Compose feature flags
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<IssuePin
|
|
196
|
+
apiKey="ew_live_..."
|
|
197
|
+
enabled={feedbackEnabled}
|
|
198
|
+
allowPinOnPage={pinOnPageEnabled}
|
|
199
|
+
allowScreenshot={screenshotEnabled}
|
|
200
|
+
pinsVisible={pinsVisible}
|
|
201
|
+
showHints={false}
|
|
202
|
+
/>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Hide historical pins without disabling feedback
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
<IssuePin
|
|
209
|
+
apiKey="ew_live_..."
|
|
210
|
+
supabaseClient={supabase}
|
|
211
|
+
enabled
|
|
212
|
+
pinsVisible={showHistoricalPins}
|
|
213
|
+
/>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Custom launcher
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
<IssuePin
|
|
220
|
+
apiKey="ew_live_..."
|
|
221
|
+
supabaseClient={supabase}
|
|
222
|
+
enabled
|
|
223
|
+
renderLauncher={({ mode, canPinOnPage, canScreenshot, toggleMenu, enterPinMode, exitPinMode, startScreenshotCapture }) => (
|
|
224
|
+
<div style={{ position: "fixed", right: 24, bottom: 24 }}>
|
|
225
|
+
{mode === "annotate" ? (
|
|
226
|
+
<button onClick={exitPinMode}>Exit pin mode</button>
|
|
227
|
+
) : canPinOnPage ? (
|
|
228
|
+
<button onClick={enterPinMode}>Pin on page</button>
|
|
229
|
+
) : canScreenshot ? (
|
|
230
|
+
<button onClick={() => void startScreenshotCapture()}>Screenshot</button>
|
|
231
|
+
) : (
|
|
232
|
+
<button onClick={toggleMenu}>Feedback</button>
|
|
233
|
+
)}
|
|
234
|
+
</div>
|
|
235
|
+
)}
|
|
236
|
+
/>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Custom modal screenshot button
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
<IssuePin
|
|
243
|
+
apiKey="ew_live_..."
|
|
244
|
+
supabaseClient={supabase}
|
|
245
|
+
enabled
|
|
246
|
+
renderModalCaptureButton={({ captureScreenshot }) => (
|
|
247
|
+
<button
|
|
248
|
+
onClick={() => void captureScreenshot()}
|
|
249
|
+
style={{ position: "absolute", top: 12, right: 12 }}
|
|
250
|
+
>
|
|
251
|
+
Capture
|
|
252
|
+
</button>
|
|
253
|
+
)}
|
|
254
|
+
/>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Low-level composition (advanced only)
|
|
258
|
+
|
|
259
|
+
Import individual components only if you need full custom assembly:
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
import { FeedbackProvider, FeedbackButton, FeedbackOverlay, ThreadPins } from "@sylergydigital/issue-pin-sdk";
|
|
263
|
+
|
|
264
|
+
function App() {
|
|
265
|
+
return (
|
|
266
|
+
<FeedbackProvider apiKey="ew_live_...">
|
|
267
|
+
<YourApp />
|
|
268
|
+
<FeedbackOverlay />
|
|
269
|
+
<ThreadPins />
|
|
270
|
+
<FeedbackButton />
|
|
271
|
+
</FeedbackProvider>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`ThreadPins` renders markers at stored document-percentage coordinates and updates on scroll/resize (no CSS selector resolution).
|
|
277
|
+
|
|
278
|
+
## User identity & auto-federation
|
|
279
|
+
|
|
280
|
+
### Supabase auth (recommended — zero config)
|
|
281
|
+
|
|
282
|
+
If your app uses Supabase for authentication, just pass your existing client. The SDK:
|
|
283
|
+
1. **Auto-detects identity** from `auth.getSession()` — no manual user props needed
|
|
284
|
+
2. **Auto-federates** the user into the Issue Pin workspace as a `commenter` — they appear in the workspace member list and their comments are properly attributed
|
|
285
|
+
3. **Uses the dashboard org `site_url`** for thread deep-links when a user clicks an in-app pin
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
import { supabase } from "./lib/supabase";
|
|
289
|
+
|
|
290
|
+
<IssuePin apiKey="ew_live_..." supabaseClient={supabase} enabled={feedbackOn} />
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
No backend code, edge functions, or sync secrets needed. The SDK handles everything transparently.
|
|
294
|
+
|
|
295
|
+
### How auto-federation works
|
|
296
|
+
|
|
297
|
+
When a user is detected via `supabaseClient`:
|
|
298
|
+
1. The SDK calls the `sdk-federate` endpoint with the API key + user identity
|
|
299
|
+
2. The endpoint validates the API key, resolves the workspace, and upserts the user as a `client_federated` workspace member with `commenter` role
|
|
300
|
+
3. An identity source (`sdk:<workspace-slug>`) is auto-created if it doesn't exist
|
|
301
|
+
|
|
302
|
+
```mermaid
|
|
303
|
+
sequenceDiagram
|
|
304
|
+
participant App as Host App
|
|
305
|
+
participant SDK as IssuePin SDK
|
|
306
|
+
participant EF as sdk-federate (Edge Fn)
|
|
307
|
+
participant DB as Database
|
|
308
|
+
|
|
309
|
+
App->>SDK: Mount with apiKey + supabaseClient
|
|
310
|
+
SDK->>SDK: auth.getSession() → extract id, email, name
|
|
311
|
+
SDK->>EF: POST { apiKey, externalId, email, displayName }
|
|
312
|
+
EF->>DB: SHA-256(apiKey) → resolve_api_key()
|
|
313
|
+
DB-->>EF: workspace_id
|
|
314
|
+
EF->>DB: Upsert user_identities
|
|
315
|
+
EF->>DB: Upsert workspace_members (commenter role)
|
|
316
|
+
DB-->>EF: OK
|
|
317
|
+
EF-->>SDK: { success, user_id, workspace_id }
|
|
318
|
+
SDK-->>App: User can now post feedback
|
|
319
|
+
Note over SDK: Result cached — runs once per session
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Other auth providers (manual)
|
|
323
|
+
|
|
324
|
+
For non-Supabase auth systems, pass identity props directly:
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
// NextAuth / Auth.js
|
|
328
|
+
const { data: session } = useSession();
|
|
329
|
+
<IssuePin apiKey="ew_live_..." user={{ email: session?.user?.email, displayName: session?.user?.name }} />
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
// Clerk
|
|
334
|
+
const { user } = useUser();
|
|
335
|
+
<IssuePin apiKey="ew_live_..." user={{ id: user?.id, email: user?.primaryEmailAddress?.emailAddress, displayName: user?.fullName }} />
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
If neither `supabaseClient` nor `user` props are provided, a console warning will remind you during development.
|
|
339
|
+
|
|
340
|
+
### Server-to-server federation (advanced)
|
|
341
|
+
|
|
342
|
+
For bulk provisioning or non-Supabase apps, use the `federate-user` edge function with a `sync_secret`. See the [Federation docs](../docs/features/feature-poor-mans-federation.md) for details.
|
|
343
|
+
|
|
344
|
+
## Security
|
|
345
|
+
|
|
346
|
+
The API key is a **publishable key** — safe to include in client-side code. It only grants scoped access to create threads and comments for the associated workspace, enforced server-side via Row Level Security.
|
|
347
|
+
|
|
348
|
+
## AI Agent Prompt
|
|
349
|
+
|
|
350
|
+
Paste this into **Claude Code**, **Cursor**, or **Codex** to integrate the SDK automatically:
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
Add the Issue Pin feedback SDK to this React app.
|
|
354
|
+
|
|
355
|
+
Install: npm install @sylergydigital/issue-pin-sdk
|
|
356
|
+
Peer deps: npm install @supabase/supabase-js html2canvas lucide-react
|
|
357
|
+
|
|
358
|
+
.npmrc setup (required for GitHub Packages):
|
|
359
|
+
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
|
|
360
|
+
@sylergydigital:registry=https://npm.pkg.github.com
|
|
361
|
+
|
|
362
|
+
Integration:
|
|
363
|
+
1. Import { IssuePin } from "@sylergydigital/issue-pin-sdk"
|
|
364
|
+
2. Add a boolean state: const [feedbackOn, setFeedbackOn] = useState(false)
|
|
365
|
+
3. Place <IssuePin apiKey={import.meta.env.VITE_ISSUE_PIN_API_KEY} enabled={feedbackOn} supabaseClient={supabase} /> in your app tree (e.g. layout root)
|
|
366
|
+
4. Add a toggle button: <button onClick={() => setFeedbackOn(f => !f)}>💬 Feedback</button>
|
|
367
|
+
5. Import your app's Supabase client: import { supabase } from "./lib/supabase"
|
|
368
|
+
|
|
369
|
+
Constraints:
|
|
370
|
+
- enabled defaults to false; SDK mounts but renders no UI until true
|
|
371
|
+
- apiKey is a publishable key (safe for client-side code)
|
|
372
|
+
- Pass supabaseClient for automatic user identity (recommended for Supabase auth apps)
|
|
373
|
+
- Alternative: pass user={{ email: "user@example.com", displayName: "Name" }} for non-Supabase auth
|
|
374
|
+
- Optional feature flags: allowPinOnPage, allowScreenshot, pinsVisible, showHints
|
|
375
|
+
- Use showHints={false} if the host app wants to suppress SDK instructional coachmarks such as the "Open Issue Pin" toast
|
|
376
|
+
```
|