makethisbetter 1.0.0 → 1.1.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.
- package/LICENSE +1 -1
- package/README.md +329 -62
- package/dist/annotate/session.d.ts +5 -0
- package/dist/annotate/session.d.ts.map +1 -1
- package/dist/api/client.d.ts +9 -3
- package/dist/api/client.d.ts.map +1 -1
- package/dist/context/error-source.d.ts +1 -0
- package/dist/context/error-source.d.ts.map +1 -1
- package/dist/context/frustration.d.ts +25 -1
- package/dist/context/frustration.d.ts.map +1 -1
- package/dist/i18n/de.d.ts +54 -0
- package/dist/i18n/de.d.ts.map +1 -0
- package/dist/i18n/en.d.ts +7 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/es.d.ts +54 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/fr.d.ts +54 -0
- package/dist/i18n/fr.d.ts.map +1 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/ja.d.ts +54 -0
- package/dist/i18n/ja.d.ts.map +1 -0
- package/dist/i18n/ko.d.ts +54 -0
- package/dist/i18n/ko.d.ts.map +1 -0
- package/dist/i18n/zh-CN.d.ts +7 -0
- package/dist/i18n/zh-CN.d.ts.map +1 -1
- package/dist/makethisbetter.cjs.js +42 -27
- package/dist/makethisbetter.cjs.js.map +1 -1
- package/dist/makethisbetter.esm.js +1208 -636
- package/dist/makethisbetter.esm.js.map +1 -1
- package/dist/makethisbetter.js +42 -27
- package/dist/makethisbetter.js.map +1 -1
- package/dist/popup/clarify.d.ts +37 -0
- package/dist/popup/clarify.d.ts.map +1 -0
- package/dist/popup/clarify.test.d.ts +2 -0
- package/dist/popup/clarify.test.d.ts.map +1 -0
- package/dist/popup/success.d.ts +1 -1
- package/dist/popup/success.d.ts.map +1 -1
- package/dist/record/session.d.ts.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/widget/controller.d.ts +3 -6
- package/dist/widget/controller.d.ts.map +1 -1
- package/dist/widget/recording-manager.d.ts +15 -0
- package/dist/widget/recording-manager.d.ts.map +1 -0
- package/dist/widget/recording-manager.test.d.ts +2 -0
- package/dist/widget/recording-manager.test.d.ts.map +1 -0
- package/dist/widget/tab.d.ts +1 -1
- package/dist/widget/tab.d.ts.map +1 -1
- package/package.json +13 -3
- package/src/annotate/session.test.ts +117 -5
- package/src/annotate/session.ts +27 -8
- package/src/api/client.test.ts +144 -4
- package/src/api/client.ts +56 -5
- package/src/context/error-source.test.ts +13 -0
- package/src/context/error-source.ts +9 -2
- package/src/context/frustration.test.ts +336 -1
- package/src/context/frustration.ts +237 -1
- package/src/i18n/de.ts +54 -0
- package/src/i18n/en.ts +7 -0
- package/src/i18n/es.ts +54 -0
- package/src/i18n/fr.ts +54 -0
- package/src/i18n/index.test.ts +1 -1
- package/src/i18n/index.ts +10 -0
- package/src/i18n/ja.ts +54 -0
- package/src/i18n/ko.ts +54 -0
- package/src/i18n/zh-CN.ts +7 -0
- package/src/popup/clarify.test.ts +419 -0
- package/src/popup/clarify.ts +215 -0
- package/src/popup/success.ts +11 -1
- package/src/record/session.ts +8 -1
- package/src/styles/widget.css +218 -0
- package/src/types.ts +20 -0
- package/src/widget/controller.test.ts +6 -4
- package/src/widget/controller.ts +59 -52
- package/src/widget/recording-manager.test.ts +136 -0
- package/src/widget/recording-manager.ts +51 -0
- package/src/widget/tab.ts +4 -2
package/LICENSE
CHANGED
|
@@ -3,7 +3,7 @@ Business Source License 1.1
|
|
|
3
3
|
Parameters
|
|
4
4
|
|
|
5
5
|
Licensor: Make This Better, Inc.
|
|
6
|
-
Licensed Work:
|
|
6
|
+
Licensed Work: makethisbetter
|
|
7
7
|
The Licensed Work is (c) 2026 Make This Better, Inc.
|
|
8
8
|
Additional Use Grant: You may make production use of the Licensed Work,
|
|
9
9
|
provided your use does not include offering the Licensed
|
package/README.md
CHANGED
|
@@ -1,113 +1,380 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://makethisbetter.dev/icon.svg" width="80" height="80" alt="Make This Better">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">makethisbetter</h1>
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
From rage click to agent-ready fix.
|
|
9
|
+
</p>
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://makethisbetter.dev">makethisbetter.dev</a> ·
|
|
13
|
+
<a href="https://www.npmjs.com/package/makethisbetter"><img src="https://img.shields.io/npm/v/makethisbetter.svg" alt="npm version"></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/makethisbetter"><img src="https://img.shields.io/npm/dm/makethisbetter.svg" alt="npm downloads"></a>
|
|
15
|
+
<a href="https://bundlephobia.com/package/makethisbetter"><img src="https://img.shields.io/bundlephobia/minzip/makethisbetter" alt="bundle size"></a>
|
|
16
|
+
<a href="https://github.com/makethisbetter/makethisbetter-js/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-BUSL--1.1-blue.svg" alt="license"></a>
|
|
17
|
+
</p>
|
|
8
18
|
|
|
9
|
-
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Why
|
|
22
|
+
|
|
23
|
+
You ship with AI agents. Your users hit bugs you never see in dev. They leave. You never find out why.
|
|
24
|
+
|
|
25
|
+
This widget gives your users a way to report exactly what went wrong — annotated screenshot, console errors, DOM state, browser info — in two clicks. AI triage turns that into a structured task your coding agent (Claude Code, Cursor, Codex) picks up automatically. The agent ships the fix. The user gets notified.
|
|
26
|
+
|
|
27
|
+
No more "can you describe what happened?" No more lost screenshots in Slack. The full loop, from frustrated user to shipped fix, runs without you context-switching.
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### CDN (2 lines)
|
|
10
32
|
|
|
11
33
|
```html
|
|
12
|
-
<script src="https://unpkg.com
|
|
34
|
+
<script src="https://unpkg.com/makethisbetter@1"></script>
|
|
13
35
|
<script>
|
|
14
|
-
MakeThisBetter.init({ projectKey: 'mtb_proj_YOUR_KEY' })
|
|
36
|
+
MakeThisBetter.init({ projectKey: 'mtb_proj_YOUR_KEY' })
|
|
15
37
|
</script>
|
|
16
38
|
```
|
|
17
39
|
|
|
18
|
-
### npm
|
|
40
|
+
### npm
|
|
19
41
|
|
|
20
42
|
```bash
|
|
21
|
-
npm install
|
|
43
|
+
npm install makethisbetter
|
|
22
44
|
```
|
|
23
45
|
|
|
24
46
|
```js
|
|
25
|
-
import { MakeThisBetter } from '
|
|
47
|
+
import { MakeThisBetter } from 'makethisbetter'
|
|
48
|
+
|
|
49
|
+
MakeThisBetter.init({ projectKey: 'mtb_proj_YOUR_KEY' })
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
That's it. A feedback tab appears on your page.
|
|
53
|
+
|
|
54
|
+
## How It Works
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
User clicks feedback tab
|
|
58
|
+
-> Annotates the problem (click to pin, drag to draw)
|
|
59
|
+
-> Adds a comment
|
|
60
|
+
-> Submits
|
|
61
|
+
+-- Screenshot captured automatically
|
|
62
|
+
+-- Console errors collected
|
|
63
|
+
+-- Page context assembled (URL, browser, OS, selectors)
|
|
64
|
+
+-- Sent to Make This Better API
|
|
65
|
+
+-- AI asks a clarifying question if needed
|
|
66
|
+
-> Dashboard shows structured feedback
|
|
67
|
+
-> AI triage produces an agent-ready task
|
|
68
|
+
-> Your coding agent picks it up and ships the fix
|
|
69
|
+
-> User gets notified: the fix is live
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Framework Guides
|
|
73
|
+
|
|
74
|
+
<details>
|
|
75
|
+
<summary><strong>React / Next.js</strong></summary>
|
|
26
76
|
|
|
27
|
-
|
|
77
|
+
```tsx
|
|
78
|
+
// app/providers.tsx (App Router) or pages/_app.tsx (Pages Router)
|
|
79
|
+
'use client'
|
|
80
|
+
import { useEffect } from 'react'
|
|
81
|
+
|
|
82
|
+
export function FeedbackProvider() {
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
import('makethisbetter').then(({ MakeThisBetter }) => {
|
|
85
|
+
MakeThisBetter.init({
|
|
86
|
+
projectKey: process.env.NEXT_PUBLIC_MTB_KEY!,
|
|
87
|
+
user: { id: userId, email }
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
return () => {
|
|
91
|
+
import('makethisbetter').then(({ MakeThisBetter }) => MakeThisBetter.destroy())
|
|
92
|
+
}
|
|
93
|
+
}, [])
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
</details>
|
|
99
|
+
|
|
100
|
+
<details>
|
|
101
|
+
<summary><strong>Vue / Nuxt</strong></summary>
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// plugins/makethisbetter.client.ts (Nuxt) or main.ts (Vue)
|
|
105
|
+
import { MakeThisBetter } from 'makethisbetter'
|
|
106
|
+
|
|
107
|
+
export default defineNuxtPlugin(() => {
|
|
108
|
+
MakeThisBetter.init({
|
|
109
|
+
projectKey: useRuntimeConfig().public.mtbKey,
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
provide: { mtbDestroy: () => MakeThisBetter.destroy() }
|
|
114
|
+
}
|
|
115
|
+
})
|
|
28
116
|
```
|
|
29
117
|
|
|
118
|
+
</details>
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary><strong>Astro</strong></summary>
|
|
122
|
+
|
|
123
|
+
```astro
|
|
124
|
+
<!-- src/components/Feedback.astro -->
|
|
125
|
+
<script>
|
|
126
|
+
import { MakeThisBetter } from 'makethisbetter'
|
|
127
|
+
MakeThisBetter.init({ projectKey: import.meta.env.PUBLIC_MTB_KEY })
|
|
128
|
+
</script>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
</details>
|
|
132
|
+
|
|
133
|
+
<details>
|
|
134
|
+
<summary><strong>Rails</strong></summary>
|
|
135
|
+
|
|
136
|
+
```erb
|
|
137
|
+
<%# app/views/layouts/application.html.erb %>
|
|
138
|
+
<% if current_user&.admin? %>
|
|
139
|
+
<script src="https://unpkg.com/makethisbetter@1"></script>
|
|
140
|
+
<script>
|
|
141
|
+
MakeThisBetter.init({
|
|
142
|
+
projectKey: '<%= Rails.application.credentials.mtb_project_key %>',
|
|
143
|
+
user: { id: '<%= current_user.id %>', email: '<%= current_user.email %>' }
|
|
144
|
+
})
|
|
145
|
+
</script>
|
|
146
|
+
<% end %>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
</details>
|
|
150
|
+
|
|
151
|
+
<details>
|
|
152
|
+
<summary><strong>Plain HTML / Static Sites</strong></summary>
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
<script src="https://unpkg.com/makethisbetter@1"></script>
|
|
156
|
+
<script>
|
|
157
|
+
MakeThisBetter.init({ projectKey: 'mtb_proj_YOUR_KEY' })
|
|
158
|
+
</script>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
</details>
|
|
162
|
+
|
|
163
|
+
## Features
|
|
164
|
+
|
|
165
|
+
### Annotation
|
|
166
|
+
|
|
167
|
+
Click any element to pin it, or drag to draw a freeform highlight. The SDK captures the element's CSS selector, text content, and position.
|
|
168
|
+
|
|
169
|
+
### Screen Recording
|
|
170
|
+
|
|
171
|
+
Switch to **Record** mode in the toolbar to capture a screen recording (up to 60 seconds). Powered by [rrweb](https://github.com/rrweb-io/rrweb) with lazy loading — zero cost until the user hits Record.
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Optional: install rrweb for npm users (CDN users get it automatically)
|
|
175
|
+
npm install rrweb
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Frustration Detection
|
|
179
|
+
|
|
180
|
+
The SDK watches for signals that a user is struggling and proactively offers to collect feedback:
|
|
181
|
+
|
|
182
|
+
| Signal | Trigger |
|
|
183
|
+
|--------|---------|
|
|
184
|
+
| Rage click | 3+ clicks on the same element within 1 second |
|
|
185
|
+
| Dead click | Click on a non-interactive element (with console errors) |
|
|
186
|
+
| Dead click (DOM) | Click on interactive-looking element with no DOM response |
|
|
187
|
+
| Rapid navigation | 3+ page navigations within 5 seconds |
|
|
188
|
+
| Form failure | Form submission with validation errors |
|
|
189
|
+
| Error page | Landing on a 404/500 error page |
|
|
190
|
+
|
|
191
|
+
Disable with `frustrationDetection: false`.
|
|
192
|
+
|
|
193
|
+
### AI Clarification
|
|
194
|
+
|
|
195
|
+
After submitting feedback, an AI assistant may ask a short follow-up question to clarify the real need — avoiding [XY problems](https://xyproblem.info/) where users describe their attempted solution instead of the actual problem.
|
|
196
|
+
|
|
197
|
+
### Auto-Collected Context
|
|
198
|
+
|
|
199
|
+
Every submission automatically includes:
|
|
200
|
+
|
|
201
|
+
- Page URL, browser, OS, screen resolution
|
|
202
|
+
- Console errors (via `window.onerror` and `window.onunhandledrejection`)
|
|
203
|
+
- Target element selector and text
|
|
204
|
+
- Annotated screenshot (via `html-to-image`)
|
|
205
|
+
- Annotation coordinates and draw paths
|
|
206
|
+
|
|
207
|
+
### Internationalization
|
|
208
|
+
|
|
209
|
+
Built-in support for 7 languages:
|
|
210
|
+
|
|
211
|
+
| Code | Language |
|
|
212
|
+
|------|----------|
|
|
213
|
+
| `en` | English (default) |
|
|
214
|
+
| `zh-CN` | Chinese (Simplified) |
|
|
215
|
+
| `ja` | Japanese |
|
|
216
|
+
| `ko` | Korean |
|
|
217
|
+
| `es` | Spanish |
|
|
218
|
+
| `fr` | French |
|
|
219
|
+
| `de` | German |
|
|
220
|
+
|
|
30
221
|
## Configuration
|
|
31
222
|
|
|
32
223
|
```js
|
|
33
224
|
MakeThisBetter.init({
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
225
|
+
// Required
|
|
226
|
+
projectKey: 'mtb_proj_xxx',
|
|
227
|
+
|
|
228
|
+
// Optional
|
|
229
|
+
locale: 'en', // UI language
|
|
230
|
+
position: 'right', // Tab position: 'left' | 'right'
|
|
231
|
+
theme: 'auto', // 'light' | 'dark' | 'auto'
|
|
232
|
+
frustrationDetection: true, // Proactive frustration prompts
|
|
233
|
+
apiUrl: 'https://...', // Self-hosted API endpoint
|
|
234
|
+
|
|
235
|
+
// User identification (recommended)
|
|
39
236
|
user: {
|
|
40
|
-
id: 'usr_123',
|
|
41
|
-
email: 'alex@
|
|
237
|
+
id: 'usr_123',
|
|
238
|
+
email: 'alex@example.com',
|
|
42
239
|
name: 'Alex Chen',
|
|
43
240
|
},
|
|
44
|
-
})
|
|
241
|
+
})
|
|
45
242
|
```
|
|
46
243
|
|
|
47
|
-
|
|
244
|
+
### Identity Verification
|
|
48
245
|
|
|
49
|
-
|
|
50
|
-
2. Clicking the tab enters **Mark up mode** — a toolbar appears at the top
|
|
51
|
-
3. Hover over any element to highlight it (blue outline + tooltip)
|
|
52
|
-
4. Click to place a **pin** annotation
|
|
53
|
-
5. Or drag to **draw** a free-form highlight
|
|
54
|
-
6. A **Comment popup** appears — describe the issue and hit Submit
|
|
55
|
-
7. The SDK captures a screenshot, collects page context (URL, browser, OS, console errors), and sends everything to the Make This Better API
|
|
56
|
-
8. Your dashboard shows the structured feedback with AI triage
|
|
246
|
+
Identity verification links feedback to authenticated users and lets them view their own submissions on the feedback board.
|
|
57
247
|
|
|
58
|
-
|
|
248
|
+
**Level 0 -- Anonymous** (default): No user token. Feedback is anonymous.
|
|
59
249
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- Console errors (captured via `window.onerror`)
|
|
64
|
-
- Target element CSS selector and text content
|
|
65
|
-
- Page screenshot (via `html-to-image`, invisible to the user)
|
|
66
|
-
- Annotation coordinates
|
|
250
|
+
```js
|
|
251
|
+
MakeThisBetter.init({ projectKey: 'mtb_proj_xxx' })
|
|
252
|
+
```
|
|
67
253
|
|
|
68
|
-
|
|
254
|
+
**Level 1 -- Static token**: Pass a pre-generated JWT. Simple, but the token may expire during long sessions.
|
|
69
255
|
|
|
70
|
-
|
|
256
|
+
```js
|
|
257
|
+
MakeThisBetter.init({
|
|
258
|
+
projectKey: 'mtb_proj_xxx',
|
|
259
|
+
userToken: 'eyJhbGciOiJIUzI1NiIs...',
|
|
260
|
+
})
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Level 2 -- Dynamic token (recommended)**: Pass an async function that returns a fresh JWT. The SDK calls it before each API request, so tokens never go stale.
|
|
71
264
|
|
|
72
265
|
```js
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
266
|
+
MakeThisBetter.init({
|
|
267
|
+
projectKey: 'mtb_proj_xxx',
|
|
268
|
+
userTokenFn: async () => {
|
|
269
|
+
const res = await fetch('/api/mtb-token')
|
|
270
|
+
const { token } = await res.json()
|
|
271
|
+
return token
|
|
272
|
+
},
|
|
273
|
+
})
|
|
77
274
|
```
|
|
78
275
|
|
|
276
|
+
When `userToken` or `userTokenFn` is set, the widget sends an `X-User-Token` header with every request. After submitting feedback, a "View my feedback" link appears that opens the project board filtered to the user's submissions.
|
|
277
|
+
|
|
278
|
+
Generate tokens server-side using your project's HMAC secret (available in your project settings):
|
|
279
|
+
|
|
79
280
|
```ruby
|
|
80
281
|
# Rails example
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
MakeThisBetter.init({
|
|
84
|
-
projectKey: '<%= Rails.application.credentials.mtb_project_key %>',
|
|
85
|
-
user: { id: '<%= current_user.id %>', email: '<%= current_user.email %>' }
|
|
86
|
-
});
|
|
87
|
-
</script>
|
|
88
|
-
<% end %>
|
|
282
|
+
payload = { sub: current_user.id, email: current_user.email, exp: 1.hour.from_now.to_i }
|
|
283
|
+
JWT.encode(payload, project.widget_secret, 'HS256')
|
|
89
284
|
```
|
|
90
285
|
|
|
286
|
+
### Conditional Loading
|
|
287
|
+
|
|
288
|
+
```js
|
|
289
|
+
// Only show to beta users
|
|
290
|
+
if (user.isBetaTester) {
|
|
291
|
+
MakeThisBetter.init({ projectKey: 'mtb_proj_xxx', user: { id: user.id } })
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Self-Hosting
|
|
296
|
+
|
|
297
|
+
The Widget works with any backend — not just makethisbetter.dev. Implement one API endpoint and point `apiUrl` at your server:
|
|
298
|
+
|
|
299
|
+
```js
|
|
300
|
+
MakeThisBetter.init({
|
|
301
|
+
projectKey: 'your-key',
|
|
302
|
+
apiUrl: 'https://feedback.yoursite.com',
|
|
303
|
+
})
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Your backend needs to handle `POST /api/v1/widget/feedbacks` (multipart/form-data with description, screenshot, console errors, etc). The Widget takes care of everything else — annotation, recording, frustration detection, context collection.
|
|
307
|
+
|
|
308
|
+
The cloud platform at [makethisbetter.dev](https://makethisbetter.dev) adds AI triage, dashboard, GitHub/Linear sync, and email notifications on top.
|
|
309
|
+
|
|
310
|
+
## API
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
import { MakeThisBetter } from 'makethisbetter'
|
|
314
|
+
|
|
315
|
+
// Start the widget
|
|
316
|
+
MakeThisBetter.init(config: MakeThisBetterConfig): void
|
|
317
|
+
|
|
318
|
+
// Remove the widget and clean up all listeners
|
|
319
|
+
MakeThisBetter.destroy(): void
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Architecture
|
|
323
|
+
|
|
324
|
+
The widget runs inside a Shadow DOM container, isolating its styles from your page. No CSS conflicts, no z-index wars.
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
Shadow DOM Host (#mtb-widget-host)
|
|
328
|
+
+-- Feedback Tab (entry point)
|
|
329
|
+
+-- Annotation Toolbar (Mark up / Record toggle)
|
|
330
|
+
+-- Annotation Session (pin + draw overlays)
|
|
331
|
+
+-- Comment Popup (description + submit)
|
|
332
|
+
+-- AI Clarification Card (follow-up conversation)
|
|
333
|
+
+-- Success Card (confirmation)
|
|
334
|
+
+-- Frustration Prompt (proactive trigger)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Bundle Size
|
|
338
|
+
|
|
339
|
+
| Format | Size | gzip |
|
|
340
|
+
|--------|------|------|
|
|
341
|
+
| IIFE | ~80 KB | ~23 KB |
|
|
342
|
+
| ESM | ~94 KB | ~25 KB |
|
|
343
|
+
|
|
344
|
+
rrweb is loaded on demand and not included in these numbers.
|
|
345
|
+
|
|
91
346
|
## Development
|
|
92
347
|
|
|
93
348
|
```bash
|
|
349
|
+
git clone https://github.com/makethisbetter/makethisbetter-js.git
|
|
350
|
+
cd makethisbetter-js
|
|
94
351
|
npm install
|
|
95
|
-
npm run
|
|
96
|
-
|
|
97
|
-
npm
|
|
98
|
-
npm
|
|
99
|
-
npm run type-check # TypeScript check
|
|
352
|
+
npm run dev # Dev server at localhost:5173
|
|
353
|
+
npm run build # Build all formats to dist/
|
|
354
|
+
npm test # Run tests
|
|
355
|
+
npm run type-check # TypeScript validation
|
|
100
356
|
```
|
|
101
357
|
|
|
102
|
-
##
|
|
358
|
+
## Related
|
|
103
359
|
|
|
104
|
-
|
|
|
105
|
-
|
|
106
|
-
|
|
|
107
|
-
|
|
|
360
|
+
| Package | What it does |
|
|
361
|
+
|---------|-------------|
|
|
362
|
+
| [Make This Better](https://makethisbetter.dev) | The platform — dashboard, AI triage, feedback board |
|
|
363
|
+
| [@makethisbetter/mcp](https://github.com/makethisbetter/mcp) | MCP server — your coding agent reads feedback directly |
|
|
364
|
+
| [makethisbetter CLI](https://github.com/makethisbetter/cli) | Terminal tool for managing feedback |
|
|
365
|
+
| [makethisbetter Skills](https://github.com/makethisbetter/skills) | Claude Code skills — `/makethisbetter` in your editor |
|
|
108
366
|
|
|
109
367
|
## License
|
|
110
368
|
|
|
111
|
-
[Business Source License 1.1](LICENSE) — free for
|
|
369
|
+
[Business Source License 1.1](LICENSE) — free for any use that does not compete with Make This Better's commercial offering. Converts to MIT on 2029-01-01.
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
<details>
|
|
374
|
+
<summary><strong>GitHub repo settings</strong></summary>
|
|
375
|
+
|
|
376
|
+
- **Description**: Drop-in feedback widget — from rage click to agent-ready fix
|
|
377
|
+
- **Homepage**: https://makethisbetter.dev
|
|
378
|
+
- **Topics**: feedback, widget, ai, mcp, claude-code, cursor, vibe-coding
|
|
112
379
|
|
|
113
|
-
|
|
380
|
+
</details>
|
|
@@ -10,9 +10,14 @@ export declare class AnnotationSession {
|
|
|
10
10
|
private drawLayer;
|
|
11
11
|
private pinCount;
|
|
12
12
|
private interactionActive;
|
|
13
|
+
private pendingHighlightFrame;
|
|
14
|
+
private lastMoveX;
|
|
15
|
+
private lastMoveY;
|
|
13
16
|
constructor(shadow: ShadowContainer, messages: I18nMessages, onReady: (annotation: Annotation, targetName: string) => void);
|
|
14
17
|
private handleMouseDown;
|
|
15
18
|
private handleMouseMove;
|
|
19
|
+
private updateHighlight;
|
|
20
|
+
private cancelPendingHighlight;
|
|
16
21
|
private handleMouseUp;
|
|
17
22
|
dismissInteraction(): void;
|
|
18
23
|
destroy(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/annotate/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAQ1C,qBAAa,iBAAiB;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/annotate/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAQ1C,qBAAa,iBAAiB;IAa1B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;IAbjB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,iBAAiB,CAAO;IAChC,OAAO,CAAC,qBAAqB,CAAsB;IACnD,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,SAAS,CAAI;gBAGnB,MAAM,EAAE,eAAe,EACf,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI;IAmBvE,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,aAAa;IAyBrB,kBAAkB,IAAI,IAAI;IAQ1B,OAAO,IAAI,IAAI;CAKhB"}
|
package/dist/api/client.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import type { FeedbackPayload } from '../types';
|
|
1
|
+
import type { FeedbackPayload, FeedbackResponse, ClarifyResponse } from '../types';
|
|
2
2
|
export declare class ApiClient {
|
|
3
3
|
private apiUrl;
|
|
4
4
|
private projectKey;
|
|
5
5
|
private retryDelayMs;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
private userToken?;
|
|
7
|
+
private userTokenFn?;
|
|
8
|
+
constructor(projectKey: string, apiUrl?: string, retryDelayMs?: number, userToken?: string, userTokenFn?: () => Promise<string>);
|
|
9
|
+
resolveUserToken(): Promise<string | undefined>;
|
|
10
|
+
private authHeaders;
|
|
11
|
+
submitFeedback(payload: FeedbackPayload, screenshot: Blob | null): Promise<FeedbackResponse>;
|
|
12
|
+
startClarification(feedbackId: string, message?: string): Promise<ClarifyResponse>;
|
|
13
|
+
getClarification(feedbackId: string): Promise<ClarifyResponse>;
|
|
8
14
|
private fetchWithRetry;
|
|
9
15
|
}
|
|
10
16
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAIlF,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,SAAS,CAAC,CAAQ;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAuB;gBAE/B,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC;IAQvH,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAKvC,WAAW;IAQnB,cAAc,CAAC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2C5F,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAYlF,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;YAStD,cAAc;CAmB7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-source.d.ts","sourceRoot":"","sources":["../../src/context/error-source.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAA;AAEtD,cAAM,WAAW;IACf,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,gBAAgB,CAAoD;IAC5E,OAAO,CAAC,MAAM,CAAQ;IAEtB,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI;IAS9C,OAAO,CAAC,IAAI;
|
|
1
|
+
{"version":3,"file":"error-source.d.ts","sourceRoot":"","sources":["../../src/context/error-source.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAA;AAEtD,cAAM,WAAW;IACf,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,gBAAgB,CAAmC;IAC3D,OAAO,CAAC,gBAAgB,CAAoD;IAC5E,OAAO,CAAC,MAAM,CAAQ;IAEtB,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI;IAS9C,OAAO,CAAC,IAAI;IAqBZ,OAAO,CAAC,MAAM;IAgBd,OAAO,CAAC,IAAI;CAGb;AAED,eAAO,MAAM,WAAW,aAAoB,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type FrustrationSignal = 'rage_click' | 'dead_click' | 'console_error';
|
|
1
|
+
export type FrustrationSignal = 'rage_click' | 'dead_click' | 'console_error' | 'rapid_navigation' | 'form_failure' | 'error_page' | 'dead_click_dom';
|
|
2
2
|
export interface FrustrationEvent {
|
|
3
3
|
signal: FrustrationSignal;
|
|
4
4
|
target?: string;
|
|
@@ -15,10 +15,34 @@ export declare class FrustrationDetector {
|
|
|
15
15
|
private deadClickCount;
|
|
16
16
|
private cooldownUntil;
|
|
17
17
|
private static readonly COOLDOWN_MS;
|
|
18
|
+
private navTimestamps;
|
|
19
|
+
private boundPopstateHandler;
|
|
20
|
+
private originalPushState;
|
|
21
|
+
private originalReplaceState;
|
|
22
|
+
private installedPushState;
|
|
23
|
+
private installedReplaceState;
|
|
24
|
+
private boundSubmitHandler;
|
|
25
|
+
private boundInvalidHandler;
|
|
26
|
+
private errorPageUrls;
|
|
27
|
+
private domObserver;
|
|
28
|
+
private pendingDomChecks;
|
|
29
|
+
private lastMutationAt;
|
|
18
30
|
constructor(onFrustration: (event: FrustrationEvent) => void);
|
|
19
31
|
start(): void;
|
|
20
32
|
stop(): void;
|
|
33
|
+
private startNavigationTracking;
|
|
34
|
+
private stopNavigationTracking;
|
|
35
|
+
private startFormTracking;
|
|
36
|
+
private stopFormTracking;
|
|
21
37
|
private handleClick;
|
|
38
|
+
private handleNavigation;
|
|
39
|
+
private handleFormSubmit;
|
|
40
|
+
private handleFormInvalid;
|
|
41
|
+
private checkErrorPage;
|
|
42
|
+
private checkDeadClickDom;
|
|
43
|
+
private ensureDomObserver;
|
|
44
|
+
private disconnectDomObserver;
|
|
45
|
+
private looksInteractive;
|
|
22
46
|
private isInteractive;
|
|
23
47
|
private isWidgetElement;
|
|
24
48
|
private emitIfReady;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frustration.d.ts","sourceRoot":"","sources":["../../src/context/frustration.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"frustration.d.ts","sourceRoot":"","sources":["../../src/context/frustration.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,YAAY,GACZ,eAAe,GACf,kBAAkB,GAClB,cAAc,GACd,YAAY,GACZ,gBAAgB,CAAA;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,iBAAiB,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AA6BD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,iBAAiB,CAAyB;IAClD,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAS;IAE5C,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,oBAAoB,CAA4B;IACxD,OAAO,CAAC,iBAAiB,CAAoC;IAC7D,OAAO,CAAC,oBAAoB,CAAuC;IACnE,OAAO,CAAC,kBAAkB,CAAoC;IAC9D,OAAO,CAAC,qBAAqB,CAAuC;IACpE,OAAO,CAAC,kBAAkB,CAAoC;IAC9D,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,cAAc,CAAK;gBAEf,aAAa,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI;IAK5D,KAAK,IAAI,IAAI;IAab,IAAI,IAAI,IAAI;IAeZ,OAAO,CAAC,uBAAuB;IAsB/B,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,WAAW;IAuCnB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,cAAc;IAmCtB,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,gBAAgB;CAIzB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
tab: string;
|
|
3
|
+
toolbar: {
|
|
4
|
+
markup: string;
|
|
5
|
+
record: string;
|
|
6
|
+
hint: string;
|
|
7
|
+
hintRecord: string;
|
|
8
|
+
exit: string;
|
|
9
|
+
};
|
|
10
|
+
popup: {
|
|
11
|
+
about: string;
|
|
12
|
+
placeholder: string;
|
|
13
|
+
submit: string;
|
|
14
|
+
cancel: string;
|
|
15
|
+
quickOptions: {
|
|
16
|
+
emoji: string;
|
|
17
|
+
label: string;
|
|
18
|
+
}[];
|
|
19
|
+
};
|
|
20
|
+
success: {
|
|
21
|
+
title: string;
|
|
22
|
+
message: string;
|
|
23
|
+
view_feedback: string;
|
|
24
|
+
close: string;
|
|
25
|
+
};
|
|
26
|
+
error: {
|
|
27
|
+
submit: string;
|
|
28
|
+
};
|
|
29
|
+
pet: {
|
|
30
|
+
bubble: string;
|
|
31
|
+
};
|
|
32
|
+
frustration: {
|
|
33
|
+
prompt: string;
|
|
34
|
+
action: string;
|
|
35
|
+
dismiss: string;
|
|
36
|
+
};
|
|
37
|
+
annotation: {
|
|
38
|
+
drawing: string;
|
|
39
|
+
element: string;
|
|
40
|
+
};
|
|
41
|
+
record: {
|
|
42
|
+
stop: string;
|
|
43
|
+
timer_label: string;
|
|
44
|
+
max_reached: string;
|
|
45
|
+
};
|
|
46
|
+
clarify: {
|
|
47
|
+
thinking: string;
|
|
48
|
+
placeholder: string;
|
|
49
|
+
send: string;
|
|
50
|
+
skip: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export default _default;
|
|
54
|
+
//# sourceMappingURL=de.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"de.d.ts","sourceRoot":"","sources":["../../src/i18n/de.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAqDC"}
|
package/dist/i18n/en.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ declare const _default: {
|
|
|
20
20
|
success: {
|
|
21
21
|
title: string;
|
|
22
22
|
message: string;
|
|
23
|
+
view_feedback: string;
|
|
23
24
|
close: string;
|
|
24
25
|
};
|
|
25
26
|
error: {
|
|
@@ -42,6 +43,12 @@ declare const _default: {
|
|
|
42
43
|
timer_label: string;
|
|
43
44
|
max_reached: string;
|
|
44
45
|
};
|
|
46
|
+
clarify: {
|
|
47
|
+
thinking: string;
|
|
48
|
+
placeholder: string;
|
|
49
|
+
send: string;
|
|
50
|
+
skip: string;
|
|
51
|
+
};
|
|
45
52
|
};
|
|
46
53
|
export default _default;
|
|
47
54
|
//# sourceMappingURL=en.d.ts.map
|
package/dist/i18n/en.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAqDC"}
|