@loupekit/sdk 0.2.0-next.1 → 0.2.1-next.2

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.
Files changed (2) hide show
  1. package/README.md +107 -52
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,39 +1,67 @@
1
- # @loupekit/sdk
1
+ <div align="center">
2
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.
3
+ <a href="https://mohamed-ashraf-elsaed.github.io/loupe/">
4
+ <img src="https://raw.githubusercontent.com/mohamed-ashraf-elsaed/loupe/main/docs/store/promo-marquee-1400x560.jpg" alt="Loupe Pin feedback to the live UI. Hand it to Claude." width="100%" />
5
+ </a>
7
6
 
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.
7
+ <h1>@loupekit/sdk</h1>
8
+
9
+ <p><strong>The embeddable visual-feedback widget for the web.</strong><br />
10
+ Inspect any element on your live product, pin a comment to it, capture a screenshot —<br />
11
+ comments re-anchor across redeploys and flow to Claude Code as an actionable backlog.</p>
12
+
13
+ <p>
14
+ <a href="https://www.npmjs.com/package/@loupekit/sdk"><img src="https://img.shields.io/npm/v/@loupekit/sdk?color=4a55d6&label=npm" alt="npm version" /></a>
15
+ <a href="https://www.npmjs.com/package/@loupekit/sdk"><img src="https://img.shields.io/npm/dm/@loupekit/sdk?color=4a55d6" alt="npm downloads" /></a>
16
+ <img src="https://img.shields.io/npm/types/@loupekit/sdk?color=4a55d6" alt="TypeScript types" />
17
+ <img src="https://img.shields.io/npm/l/@loupekit/sdk?color=4a55d6" alt="MIT license" />
18
+ </p>
19
+
20
+ <p>
21
+ <a href="https://mohamed-ashraf-elsaed.github.io/loupe/"><b>Website</b></a> ·
22
+ <a href="https://github.com/mohamed-ashraf-elsaed/loupe"><b>GitHub</b></a> ·
23
+ <a href="https://github.com/mohamed-ashraf-elsaed/loupe/blob/main/CHANGELOG.md"><b>Changelog</b></a> ·
24
+ <a href="https://www.npmjs.com/package/@loupekit/mcp"><b>MCP server</b></a>
25
+ </p>
26
+
27
+ </div>
11
28
 
12
29
  ---
13
30
 
14
- ## Why
31
+ ## Overview
32
+
33
+ Traditional feedback — _"the revenue card looks off on the dashboard"_ — loses the one thing
34
+ an engineer needs: **which element, in what state, on which page.** Loupe captures all of it at
35
+ the moment of the comment, so the feedback stays actionable even after the UI changes underneath it.
15
36
 
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.
37
+ <div align="center">
38
+ <img src="https://raw.githubusercontent.com/mohamed-ashraf-elsaed/loupe/main/docs/store/screenshot-1-inspect.jpg" alt="Pin a comment to any element" width="90%" />
39
+ </div>
40
+
41
+ ## Table of contents
42
+
43
+ - [Features](#features)
44
+ - [Install](#install)
45
+ - [Quick start](#quick-start)
46
+ - [Re-anchoring across redeploys](#re-anchoring-across-redeploys)
47
+ - [Configuration](#configuration)
48
+ - [Redaction](#redaction)
49
+ - [Auth model](#auth-model)
50
+ - [Storage seam](#storage-seam)
51
+ - [The loop](#the-loop)
52
+ - [Related packages](#related-packages)
21
53
 
22
54
  ## Features
23
55
 
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.
56
+ | | |
57
+ | --- | --- |
58
+ | 🎯 **Click-to-comment inspector** | Hover-highlight any element, click to pin a comment. |
59
+ | ▭ **Free-region screenshots** | Drag a free-size box, screenshot exactly that area, comment on it. The region anchors to the element under its center, so it tracks responsive reflow and scrolling. |
60
+ | 🔁 **Redeploy-surviving re-anchoring** | A multi-signal fingerprint (stable id/testid, CSS path, XPath, text, attributes, position) re-locates the element on the current page; if it can't, the pin **detaches** instead of pointing at the wrong thing. |
61
+ | 📸 **Screenshot capture** | `[data-loupe-redact]` regions are painted over **before any pixels leave the browser**. |
62
+ | 🧩 **Shadow-DOM isolation** | The widget's CSS never leaks into your page and vice-versa. |
63
+ | 🔌 **Pluggable storage** | Talks to the Loupe backend, or persists to `localStorage` for offline/demo use. |
64
+ | 🤖 **Claude-ready** | Every comment carries the element HTML + computed styles + screenshot Claude Code needs to make the fix. |
37
65
 
38
66
  ## Install
39
67
 
@@ -41,8 +69,8 @@ the UI changes underneath it.
41
69
  npm i @loupekit/sdk
42
70
  ```
43
71
 
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`.
72
+ > Also mirrored to **GitHub Packages** as `@mohamed-ashraf-elsaed/sdk` add
73
+ > `@mohamed-ashraf-elsaed:registry=https://npm.pkg.github.com` to your `.npmrc` to install from there.
46
74
 
47
75
  ## Quick start
48
76
 
@@ -53,37 +81,51 @@ init({
53
81
  projectKey: "pk_live_yourkey",
54
82
  user: { id: "u_92", name: "Sara (PM)", email: "sara@acme.com" },
55
83
  apiBase: "https://loupe.yourbackend.com",
56
- // HMAC-SHA256(user.id, PROJECT_SECRET), computed on your server (see Auth below).
84
+ // HMAC-SHA256(user.id, PROJECT_SECRET), computed on your server (see Auth model).
57
85
  userHmac: "decb2c…",
58
86
  });
59
87
  ```
60
88
 
61
- That's it — a floating toolbar appears with **Inspect & comment**, **Region shot**, and
62
- **Comments**. Call `destroy()` to tear it down.
89
+ A floating toolbar appears with **Inspect & comment**, **Region shot**, and **Comments**.
90
+ Call `destroy()` to tear it down. `init()` is idempotent — safe to call more than once.
63
91
 
64
92
  ### Offline mode (no backend)
65
93
 
66
- Omit `apiBase` and comments persist to `localStorage`. Great for demos and local dev.
94
+ Omit `apiBase` and comments persist to `localStorage` great for demos and local dev:
67
95
 
68
96
  ```ts
69
97
  init({ projectKey: "pk_demo", user: { id: "u_1", name: "You" } });
70
98
  ```
71
99
 
100
+ ## Re-anchoring across redeploys
101
+
102
+ The crown jewel. A comment records a multi-signal fingerprint of its target. On the next
103
+ page load — even after the UI is rebuilt, relabeled, or reordered — Loupe re-locates the
104
+ element and moves the pin to it. Below, the "Revenue" card was relabeled to "Total Revenue"
105
+ and reordered, yet the pin follows it:
106
+
107
+ <table>
108
+ <tr>
109
+ <td width="50%" align="center"><b>Before redeploy</b><br /><img src="https://raw.githubusercontent.com/mohamed-ashraf-elsaed/loupe/main/docs/before-redeploy.png" alt="Comment pinned to the revenue card" /></td>
110
+ <td width="50%" align="center"><b>After redeploy</b><br /><img src="https://raw.githubusercontent.com/mohamed-ashraf-elsaed/loupe/main/docs/after-redeploy.png" alt="Pin re-anchored after the layout changed" /></td>
111
+ </tr>
112
+ </table>
113
+
72
114
  ## Configuration
73
115
 
74
116
  `init(config: LoupeConfig)`:
75
117
 
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. |
118
+ | Option | Type | Description |
119
+ | --- | --- | --- |
120
+ | `projectKey` | `string` **(required)** | Public project key issued by the backend. |
121
+ | `user` | `{ id, name, email? }` **(required)** | The already-authenticated host-app user. |
122
+ | `apiBase` | `string` | Backend base URL. Omit → `localStorage` (offline mode). |
123
+ | `userHmac` | `string` | `HMAC-SHA256(user.id, PROJECT_SECRET)` computed server-side. Required in production for writes. |
124
+ | `autoOpen` | `boolean` | Start with the inspector already active. |
125
+ | `captureScreenshot` | `(el: Element) => Promise<string \| undefined>` | Override element screenshot capture (the extension backs this with `captureVisibleTab`). |
126
+ | `captureRegion` | `(rect: RegionRect) => Promise<string \| undefined>` | Override free-region capture. `rect` is in viewport coordinates. |
127
+ | `headers` | `Record<string, string>` | Extra headers merged into every backend request (e.g. a CSRF token). |
128
+ | `credentials` | `RequestCredentials` | `credentials` mode for backend requests — set `"include"` for cross-origin cookie auth. |
87
129
 
88
130
  ## Redaction
89
131
 
@@ -99,9 +141,9 @@ ever leave the browser** — use it on PII, secrets, or anything sensitive:
99
141
  Each project has a secret. **Writes** require `X-Loupe-User` + `X-Loupe-Hmac`
100
142
  (`= HMAC-SHA256(userId, PROJECT_SECRET)`), which your server computes and injects into the
101
143
  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).
144
+ the raw secret.
103
145
 
104
- ## Extending the storage seam
146
+ ## Storage seam
105
147
 
106
148
  Anything implementing `StorageAdapter` can back the widget — swap in your own transport:
107
149
 
@@ -114,16 +156,29 @@ interface StorageAdapter {
114
156
  }
115
157
  ```
116
158
 
117
- ## Browser support
159
+ ## The loop
118
160
 
119
- Modern evergreen browsers (Chromium, Firefox, Safari). Uses Shadow DOM, `MutationObserver`,
120
- and the `crypto`/`clipboard` APIs.
161
+ SDK (in your product) backend API → Postgres + object storage → **dashboard** (human triage)
162
+ **and** **MCP server** (Claude reads it) → status flows back.
163
+
164
+ <table>
165
+ <tr>
166
+ <td width="50%" align="center"><b>Triage board (dashboard)</b><br /><img src="https://raw.githubusercontent.com/mohamed-ashraf-elsaed/loupe/main/docs/store/screenshot-2-board.jpg" alt="Kanban triage board" /></td>
167
+ <td width="50%" align="center"><b>Claude Code reads it via MCP</b><br /><img src="https://raw.githubusercontent.com/mohamed-ashraf-elsaed/loupe/main/docs/store/screenshot-3-claude.jpg" alt="Claude Code reading comments through MCP" /></td>
168
+ </tr>
169
+ </table>
121
170
 
122
171
  ## Related packages
123
172
 
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.
173
+ | Package | Description |
174
+ | --- | --- |
175
+ | [`@loupekit/mcp`](https://www.npmjs.com/package/@loupekit/mcp) | MCP server that hands the comments to Claude Code. |
176
+ | [`@loupekit/shared`](https://www.npmjs.com/package/@loupekit/shared) | The canonical TypeScript types shared across the platform. |
177
+
178
+ ## Browser support
179
+
180
+ Modern evergreen browsers (Chromium, Firefox, Safari). Uses Shadow DOM, `MutationObserver`,
181
+ and the `crypto` / `clipboard` APIs.
127
182
 
128
183
  ## License
129
184
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loupekit/sdk",
3
- "version": "0.2.0-next.1",
3
+ "version": "0.2.1-next.2",
4
4
  "description": "Embeddable visual-feedback layer: pin comments to the live UI and hand them to Claude.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,7 +39,7 @@
39
39
  "access": "public"
40
40
  },
41
41
  "devDependencies": {
42
- "@loupekit/shared": "0.2.0-next.1",
42
+ "@loupekit/shared": "0.2.1-next.2",
43
43
  "modern-screenshot": "^4.6.6"
44
44
  }
45
45
  }