@insitue/sdk 0.2.0 → 0.3.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.
@@ -1,400 +0,0 @@
1
- import {
2
- IssueTrackerSink,
3
- R,
4
- beginPick,
5
- buildBundle,
6
- d,
7
- installRuntimeCollectors,
8
- k,
9
- onDisplayMediaChange,
10
- retryDisplayMedia,
11
- setCaptureSettings,
12
- stopDisplayMedia,
13
- y
14
- } from "./chunk-62N4L6RA.js";
15
-
16
- // src/capture-only.ts
17
- var DEFAULT_INGEST = "https://www.insitue.com/api/v1/capture";
18
- async function postCapture(endpoint, projectKey, draft) {
19
- try {
20
- await fetch(endpoint, {
21
- method: "POST",
22
- headers: {
23
- "content-type": "application/json",
24
- "x-insitue-key": projectKey
25
- },
26
- body: JSON.stringify({
27
- bundle: draft.bundle,
28
- note: draft.bundle.userNote ?? null,
29
- projectKey
30
- // belt + braces — header is the contract, body is the fallback
31
- }),
32
- credentials: "omit",
33
- keepalive: true
34
- });
35
- } catch {
36
- }
37
- }
38
- var C = {
39
- ink: "#16161a",
40
- sub: "#6b6c77",
41
- faint: "#9a9aa4",
42
- line: "#e9e9ee",
43
- surface: "#ffffff",
44
- surface2: "#f6f6f9",
45
- accent: "linear-gradient(180deg,#6b63ff,#5751e6)",
46
- accentRing: "rgba(91,91,240,.30)",
47
- green: "#117a52",
48
- sans: '-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,"Helvetica Neue",Arial,sans-serif',
49
- mono: 'ui-monospace,"SF Mono",SFMono-Regular,Menlo,monospace',
50
- shadow: "0 12px 40px rgba(20,20,35,.16),0 2px 8px rgba(20,20,35,.08)"
51
- };
52
- function defaultDeliver(draft) {
53
- globalThis.__insitu_capture__ = {
54
- title: draft.title,
55
- body: draft.body,
56
- bundle: draft.bundle
57
- };
58
- console.info("[insitue] capture:", draft.title);
59
- try {
60
- const blob = new Blob([JSON.stringify(draft, null, 2)], {
61
- type: "application/json"
62
- });
63
- const a = document.createElement("a");
64
- a.href = URL.createObjectURL(blob);
65
- a.download = `insitue-capture-${Date.now()}.json`;
66
- a.click();
67
- URL.revokeObjectURL(a.href);
68
- } catch {
69
- }
70
- }
71
- function CaptureOnlyApp(props) {
72
- const [phase, setPhase] = d("idle");
73
- const [bundle, setBundle] = d(null);
74
- const [note, setNote] = d("");
75
- const [tabCaptureActive, setTabCaptureActive] = d(false);
76
- const [tabCaptureDenied, setTabCaptureDenied] = d(false);
77
- y(() => {
78
- return onDisplayMediaChange((active, reason) => {
79
- setTabCaptureActive(active);
80
- if (reason === "denied") setTabCaptureDenied(true);
81
- if (reason === "granted") setTabCaptureDenied(false);
82
- });
83
- }, []);
84
- const retryWithPixelPerfect = async () => {
85
- if (!bundle?.target?.selector) {
86
- await retryDisplayMedia();
87
- return;
88
- }
89
- const granted = await retryDisplayMedia();
90
- if (granted) {
91
- setPhase("picking");
92
- const sel = await beginPick("element").catch(() => null);
93
- if (sel) {
94
- setBundle(await buildBundle(sel));
95
- setPhase("compose");
96
- } else {
97
- setPhase("compose");
98
- }
99
- }
100
- };
101
- const sink = new IssueTrackerSink(async (draft) => {
102
- globalThis.__insitu_capture__ = {
103
- title: draft.title,
104
- body: draft.body,
105
- bundle: draft.bundle
106
- };
107
- if (props.onCapture) {
108
- props.onCapture(draft, draft.bundle);
109
- } else if (props.projectKey) {
110
- await postCapture(
111
- props.endpoint ?? DEFAULT_INGEST,
112
- props.projectKey,
113
- draft
114
- );
115
- } else {
116
- defaultDeliver(draft);
117
- }
118
- });
119
- y(() => {
120
- if (phase !== "sent") return;
121
- const t2 = setTimeout(() => {
122
- setPhase("idle");
123
- setBundle(null);
124
- setNote("");
125
- }, 2400);
126
- return () => clearTimeout(t2);
127
- }, [phase]);
128
- const startPick = async () => {
129
- setPhase("picking");
130
- try {
131
- const sel = await beginPick("element");
132
- if (!sel) {
133
- setPhase("idle");
134
- return;
135
- }
136
- setBundle(await buildBundle(sel));
137
- setPhase("compose");
138
- } catch {
139
- setPhase("idle");
140
- }
141
- };
142
- const send = async () => {
143
- if (!bundle) return;
144
- setPhase("sending");
145
- const withNote = note.trim() ? { ...bundle, userNote: note.trim() } : bundle;
146
- try {
147
- await sink.submit(withNote);
148
- } finally {
149
- setPhase("sent");
150
- }
151
- };
152
- const reset = () => {
153
- setPhase("idle");
154
- setBundle(null);
155
- setNote("");
156
- };
157
- const dot = k("span", {
158
- style: `width:9px;height:9px;border-radius:3px;background:${C.accent};box-shadow:0 1px 4px ${C.accentRing}`
159
- });
160
- if (phase === "idle" || phase === "picking") {
161
- const picking = phase === "picking";
162
- return k(
163
- "button",
164
- {
165
- onClick: picking ? void 0 : () => void startPick(),
166
- style: `all:unset;position:fixed;bottom:20px;right:20px;z-index:2147483000;display:flex;align-items:center;gap:9px;cursor:${picking ? "default" : "pointer"};padding:11px 16px;font:600 13.5px/1 ${C.sans};color:${C.ink};background:${C.surface};border:1px solid ${C.line};border-radius:999px;box-shadow:${C.shadow};letter-spacing:-.01em`
167
- },
168
- picking ? [
169
- k("span", {
170
- style: `width:9px;height:9px;border-radius:50%;background:#5751e6;animation:ipulse 1.1s ${"ease-in-out"} infinite`
171
- }),
172
- k("span", { style: `color:${C.sub}` }, "Click the broken element"),
173
- k("span", { style: `color:${C.faint}` }, "\xB7 Esc to cancel"),
174
- k(
175
- "style",
176
- {},
177
- "@keyframes ipulse{0%,100%{opacity:.35}50%{opacity:1}}"
178
- )
179
- ] : [dot, "Report a problem"]
180
- );
181
- }
182
- const t = bundle?.target;
183
- const targetLabel = t?.componentStack?.[0]?.name ?? t?.selector?.split(">").pop()?.trim() ?? "selection";
184
- const card = (children) => k(
185
- "div",
186
- {
187
- style: `position:fixed;bottom:20px;right:20px;z-index:2147483000;width:344px;font:14px/1.55 ${C.sans};color:${C.ink};background:${C.surface};border:1px solid ${C.line};border-radius:16px;box-shadow:${C.shadow};overflow:hidden`
188
- },
189
- children
190
- );
191
- if (phase === "sent") {
192
- return card([
193
- k(
194
- "div",
195
- { style: "padding:30px 22px;text-align:center" },
196
- [
197
- k(
198
- "div",
199
- {
200
- style: `width:46px;height:46px;border-radius:50%;margin:0 auto 12px;display:flex;align-items:center;justify-content:center;color:#fff;font-size:22px;background:${C.accent};box-shadow:0 6px 18px ${C.accentRing}`
201
- },
202
- "\u2713"
203
- ),
204
- k(
205
- "div",
206
- { style: "font-weight:680;font-size:15px;letter-spacing:-.01em" },
207
- "Report sent"
208
- ),
209
- k(
210
- "div",
211
- { style: `color:${C.sub};margin-top:5px;font-size:13px` },
212
- "The team has everything to reproduce and fix this \u2014 no follow-up needed."
213
- )
214
- ]
215
- )
216
- ]);
217
- }
218
- const sending = phase === "sending";
219
- return card([
220
- k(
221
- "div",
222
- {
223
- style: `display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid ${C.line}`
224
- },
225
- [
226
- k(
227
- "div",
228
- { style: "display:flex;align-items:center;gap:8px;font-weight:680;letter-spacing:-.01em" },
229
- [dot, "Report a problem"]
230
- ),
231
- k(
232
- "button",
233
- {
234
- onClick: reset,
235
- style: `all:unset;cursor:pointer;color:${C.faint};font-size:16px;line-height:1;padding:2px 4px`
236
- },
237
- "\u2715"
238
- )
239
- ]
240
- ),
241
- k("div", { style: "padding:16px" }, [
242
- k(
243
- "div",
244
- {
245
- style: `display:flex;align-items:center;gap:8px;padding:9px 11px;background:${C.surface2};border:1px solid ${C.line};border-radius:10px;font:12px/1.3 ${C.mono};color:${C.sub};margin-bottom:12px`
246
- },
247
- [
248
- k("span", { style: `color:#5751e6` }, "\u25CE"),
249
- k(
250
- "span",
251
- {
252
- style: "overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
253
- },
254
- `${targetLabel}${t?.source ? ` \xB7 ${t.source.file.split("/").pop()}` : ""}`
255
- )
256
- ]
257
- ),
258
- bundle?.screenshot ? k("img", {
259
- src: bundle.screenshot.dataUrl,
260
- style: `width:100%;max-height:120px;object-fit:cover;border:1px solid ${C.line};border-radius:10px;margin-bottom:12px`
261
- }) : bundle?.screenshotUnavailable ? k(
262
- "div",
263
- {
264
- style: `font-size:12px;color:${C.faint};background:${C.surface2};border:1px solid ${C.line};border-radius:10px;padding:10px;margin-bottom:12px;word-break:break-word`
265
- },
266
- // Surface the actual reason inline — helps diagnose
267
- // when a capture lands empty without expecting it.
268
- `Screenshot unavailable \u2014 ${bundle.screenshotUnavailable}`
269
- ) : null,
270
- // Nudge: the screenshot is structurally OK but some content
271
- // couldn't be embedded (non-CORS img / video / canvas). Offer
272
- // a one-tap upgrade to pixel-perfect mode + re-pick.
273
- bundle?.screenshot?.qualityNote && !tabCaptureActive ? k(
274
- "div",
275
- {
276
- style: `display:flex;align-items:center;gap:8px;padding:9px 11px;background:#fff7ed;border:1px solid #fbd9b1;color:#8a4b00;border-radius:10px;font-size:12px;margin-bottom:12px`
277
- },
278
- [
279
- k(
280
- "span",
281
- { style: "flex:1" },
282
- "Some content didn't capture cleanly. Enable tab capture for a pixel-perfect screenshot."
283
- ),
284
- k(
285
- "button",
286
- {
287
- onClick: () => void retryWithPixelPerfect(),
288
- style: `all:unset;cursor:pointer;color:#5751e6;font-weight:600;padding:2px 8px;border:1px solid #c5c2ff;border-radius:6px;background:#fff`
289
- },
290
- "Enable"
291
- )
292
- ]
293
- ) : null,
294
- // Active badge — tells the user why the browser's red "tab
295
- // sharing" indicator is on + lets them stop it.
296
- tabCaptureActive ? k(
297
- "div",
298
- {
299
- style: `display:flex;align-items:center;gap:8px;padding:7px 11px;background:#ecfdf5;border:1px solid #b6e6cf;color:#117a52;border-radius:10px;font-size:11.5px;margin-bottom:12px`
300
- },
301
- [
302
- k("span", {
303
- style: "width:8px;height:8px;border-radius:50%;background:#117a52;box-shadow:0 0 6px #117a52"
304
- }),
305
- k(
306
- "span",
307
- { style: "flex:1" },
308
- "Tab capture active \u2014 screenshots are pixel-perfect."
309
- ),
310
- k(
311
- "button",
312
- {
313
- onClick: () => stopDisplayMedia("user"),
314
- style: `all:unset;cursor:pointer;color:#117a52;font-weight:600;padding:2px 6px`
315
- },
316
- "Stop"
317
- )
318
- ]
319
- ) : null,
320
- k("textarea", {
321
- value: note,
322
- rows: 3,
323
- placeholder: "What's wrong? (optional but helps)",
324
- disabled: sending,
325
- onInput: (e) => setNote(e.target.value),
326
- style: `width:100%;box-sizing:border-box;font:14px/1.5 ${C.sans};color:${C.ink};background:${C.surface};border:1px solid #d8d8de;border-radius:10px;padding:10px 12px;resize:none;outline:none`
327
- }),
328
- k("div", { style: "display:flex;gap:8px;margin-top:12px" }, [
329
- k(
330
- "button",
331
- {
332
- onClick: sending ? void 0 : () => void send(),
333
- disabled: sending,
334
- style: `flex:1;all:unset;text-align:center;cursor:${sending ? "default" : "pointer"};padding:11px;font:680 13.5px/1 ${C.sans};color:#fff;background:${C.accent};border-radius:10px;box-shadow:0 2px 10px ${C.accentRing};opacity:${sending ? ".7" : "1"}`
335
- },
336
- sending ? "Sending\u2026" : "Send report"
337
- ),
338
- k(
339
- "button",
340
- {
341
- onClick: () => void startPick(),
342
- style: `all:unset;cursor:pointer;padding:11px 14px;font:600 13.5px/1 ${C.sans};color:${C.sub};background:${C.surface2};border:1px solid ${C.line};border-radius:10px`
343
- },
344
- "Re-pick"
345
- )
346
- ])
347
- ]),
348
- k(
349
- "div",
350
- {
351
- style: `display:flex;justify-content:space-between;padding:9px 16px;border-top:1px solid ${C.line};color:${C.faint};font-size:11px`
352
- },
353
- [
354
- // No "Secrets scrubbed" badge — the redaction is narrow
355
- // (regex on attr-names + console.log args; see
356
- // packages/sdk/src/runtime.ts safeArg, capture-core/dom.ts
357
- // serializeNode). Visible password VALUES, URL query
358
- // params, and any text rendered in the screenshot pixels
359
- // are NOT scrubbed — claiming otherwise misleads reporters
360
- // about what's safe to capture. If we ever build real
361
- // form-value redaction the badge can come back.
362
- k("span", {}, ""),
363
- k(
364
- "span",
365
- { title: `@insitue/sdk@${"0.2.0"}` },
366
- `InSitue \xB7 v${"0.2.0"}`
367
- )
368
- ]
369
- )
370
- ]);
371
- }
372
- function mountCaptureOnly(opts = {}) {
373
- installRuntimeCollectors();
374
- if (opts.defaultPixelPerfect === true) {
375
- setCaptureSettings({ alwaysPixelPerfect: true });
376
- }
377
- const host = document.createElement("div");
378
- host.id = "insitu-capture-root";
379
- host.setAttribute("data-insitu", "");
380
- document.body.appendChild(host);
381
- const shadow = host.attachShadow({ mode: "open" });
382
- const mount = document.createElement("div");
383
- shadow.appendChild(mount);
384
- R(
385
- k(CaptureOnlyApp, {
386
- projectKey: opts.projectKey,
387
- endpoint: opts.endpoint,
388
- onCapture: opts.onCapture
389
- }),
390
- mount
391
- );
392
- return () => {
393
- R(null, mount);
394
- host.remove();
395
- };
396
- }
397
-
398
- export {
399
- mountCaptureOnly
400
- };
package/dist/overlay.d.ts DELETED
@@ -1,6 +0,0 @@
1
- interface InSitueOptions {
2
- port?: number;
3
- }
4
- declare function mountInSitue(opts?: InSitueOptions): () => void;
5
-
6
- export { type InSitueOptions, mountInSitue };
package/dist/overlay.js DELETED
@@ -1,7 +0,0 @@
1
- import {
2
- mountInSitue
3
- } from "./chunk-VRINS2SK.js";
4
- import "./chunk-62N4L6RA.js";
5
- export {
6
- mountInSitue
7
- };