@loupekit/sdk 0.1.0 → 0.2.0-next.1
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 +130 -0
- package/dist/index.global.js +301 -47
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +301 -47
- package/dist/index.js.map +1 -1
- package/package.json +21 -6
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# @loupekit/sdk
|
|
2
|
+
|
|
3
|
+
**Embeddable visual-feedback widget for the web.** Drop it into any running product and
|
|
4
|
+
your team can inspect an element, pin a comment to it, and capture a screenshot. Comments
|
|
5
|
+
**persist and re-anchor across redeploys**, and are handed to **Claude Code via MCP** as a
|
|
6
|
+
fully-contextualized, actionable backlog.
|
|
7
|
+
|
|
8
|
+
Part of [**Loupe**](https://github.com/mohamed-ashraf-elsaed/loupe) — the loop is:
|
|
9
|
+
SDK (in your product) → backend API → Postgres + object storage → dashboard (human triage)
|
|
10
|
+
**and** MCP server (Claude reads it) → status flows back.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Why
|
|
15
|
+
|
|
16
|
+
Traditional feedback ("the header looks off on the pricing page") loses the one thing an
|
|
17
|
+
engineer needs: **which element, in what state, on which page.** Loupe captures all of it at
|
|
18
|
+
the moment of the comment — a multi-signal element fingerprint, the target's HTML + computed
|
|
19
|
+
styles, a screenshot, and the normalized URL — so the feedback stays actionable even after
|
|
20
|
+
the UI changes underneath it.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- 🎯 **Click-to-comment inspector** — hover-highlight any element, click to pin a comment.
|
|
25
|
+
- ▭ **Free-region screenshots** — drag a free-size box, screenshot exactly that area, and
|
|
26
|
+
comment on it. The region anchors to the element under its center, so it tracks responsive
|
|
27
|
+
reflow and scrolling.
|
|
28
|
+
- 🔁 **Redeploy-surviving re-anchoring** — a multi-signal fingerprint (stable id/testid, CSS
|
|
29
|
+
path, XPath, text, attributes, position) re-locates the element on the current page; if it
|
|
30
|
+
can't, the pin **detaches** rather than pointing at the wrong thing.
|
|
31
|
+
- 📸 **Screenshot capture** with `[data-loupe-redact]` regions blurred out before anything
|
|
32
|
+
leaves the browser.
|
|
33
|
+
- 🧩 **Shadow-DOM isolation** — the widget's CSS never leaks into your page and vice-versa.
|
|
34
|
+
- 🔌 **Pluggable storage** — talks to the Loupe backend, or persists to `localStorage` for
|
|
35
|
+
offline/demo use.
|
|
36
|
+
- 🤖 **Claude-ready** — every comment carries the context Claude Code needs to make the fix.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm i @loupekit/sdk
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> Also mirrored to **GitHub Packages** as `@mohamed-ashraf-elsaed/sdk`. To install from there,
|
|
45
|
+
> add `@mohamed-ashraf-elsaed:registry=https://npm.pkg.github.com` to your `.npmrc`.
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { init } from "@loupekit/sdk";
|
|
51
|
+
|
|
52
|
+
init({
|
|
53
|
+
projectKey: "pk_live_yourkey",
|
|
54
|
+
user: { id: "u_92", name: "Sara (PM)", email: "sara@acme.com" },
|
|
55
|
+
apiBase: "https://loupe.yourbackend.com",
|
|
56
|
+
// HMAC-SHA256(user.id, PROJECT_SECRET), computed on your server (see Auth below).
|
|
57
|
+
userHmac: "decb2c…",
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
That's it — a floating toolbar appears with **Inspect & comment**, **Region shot**, and
|
|
62
|
+
**Comments**. Call `destroy()` to tear it down.
|
|
63
|
+
|
|
64
|
+
### Offline mode (no backend)
|
|
65
|
+
|
|
66
|
+
Omit `apiBase` and comments persist to `localStorage`. Great for demos and local dev.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
init({ projectKey: "pk_demo", user: { id: "u_1", name: "You" } });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Configuration
|
|
73
|
+
|
|
74
|
+
`init(config: LoupeConfig)`:
|
|
75
|
+
|
|
76
|
+
| Option | Type | Description |
|
|
77
|
+
| ------------------ | ------------------------------------------------ | --------------------------------------------------------------------------------------------- |
|
|
78
|
+
| `projectKey` | `string` **(required)** | Public project key issued by the backend. |
|
|
79
|
+
| `user` | `{ id, name, email? }` **(required)** | The already-authenticated host-app user. |
|
|
80
|
+
| `apiBase` | `string` | Backend base URL. Omit → `localStorage` (offline mode). |
|
|
81
|
+
| `userHmac` | `string` | `HMAC-SHA256(user.id, PROJECT_SECRET)` computed server-side. Required in production for writes. |
|
|
82
|
+
| `autoOpen` | `boolean` | Start with the inspector already active. |
|
|
83
|
+
| `captureScreenshot`| `(el: Element) => Promise<string \| undefined>` | Override element screenshot capture (the extension backs this with `captureVisibleTab`). |
|
|
84
|
+
| `captureRegion` | `(rect: RegionRect) => Promise<string \| undefined>` | Override free-region capture. `rect` is in viewport coordinates. |
|
|
85
|
+
| `headers` | `Record<string, string>` | Extra headers merged into every backend request (e.g. a CSRF token). |
|
|
86
|
+
| `credentials` | `RequestCredentials` | `credentials` mode for backend requests — set `"include"` for cross-origin cookie auth. |
|
|
87
|
+
|
|
88
|
+
## Redaction
|
|
89
|
+
|
|
90
|
+
Any element marked `data-loupe-redact` is painted over in screenshots **before the pixels
|
|
91
|
+
ever leave the browser** — use it on PII, secrets, or anything sensitive:
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<input data-loupe-redact value="secret.person@acme.com" />
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Auth model
|
|
98
|
+
|
|
99
|
+
Each project has a secret. **Writes** require `X-Loupe-User` + `X-Loupe-Hmac`
|
|
100
|
+
(`= HMAC-SHA256(userId, PROJECT_SECRET)`), which your server computes and injects into the
|
|
101
|
+
page — users can't spoof identity. The dashboard and MCP server authenticate as admin with
|
|
102
|
+
the raw secret. See the [Auth docs](https://github.com/mohamed-ashraf-elsaed/loupe#auth-model).
|
|
103
|
+
|
|
104
|
+
## Extending the storage seam
|
|
105
|
+
|
|
106
|
+
Anything implementing `StorageAdapter` can back the widget — swap in your own transport:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
interface StorageAdapter {
|
|
110
|
+
list(projectKey: string, url: string): Promise<Comment[]>;
|
|
111
|
+
save(comment: Comment): Promise<Comment>;
|
|
112
|
+
update(id: string, patch: Partial<Comment>): Promise<void>;
|
|
113
|
+
remove(id: string): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Browser support
|
|
118
|
+
|
|
119
|
+
Modern evergreen browsers (Chromium, Firefox, Safari). Uses Shadow DOM, `MutationObserver`,
|
|
120
|
+
and the `crypto`/`clipboard` APIs.
|
|
121
|
+
|
|
122
|
+
## Related packages
|
|
123
|
+
|
|
124
|
+
- [`@loupekit/mcp`](https://www.npmjs.com/package/@loupekit/mcp) — MCP server that hands the
|
|
125
|
+
comments to Claude Code.
|
|
126
|
+
- [`@loupekit/shared`](https://www.npmjs.com/package/@loupekit/shared) — the canonical types.
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT © [Mohamed Ashraf Elsaed](https://github.com/mohamed-ashraf-elsaed)
|