@insitue/sdk 0.1.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.
@@ -0,0 +1,300 @@
1
+ import {
2
+ IssueTrackerSink,
3
+ R,
4
+ beginPick,
5
+ buildBundle,
6
+ d,
7
+ installRuntimeCollectors,
8
+ k,
9
+ y
10
+ } from "./chunk-6SMY7D6U.js";
11
+
12
+ // src/capture-only.ts
13
+ var DEFAULT_INGEST = "https://www.insitue.com/api/v1/capture";
14
+ async function postCapture(endpoint, projectKey, draft) {
15
+ try {
16
+ await fetch(endpoint, {
17
+ method: "POST",
18
+ headers: {
19
+ "content-type": "application/json",
20
+ "x-insitue-key": projectKey
21
+ },
22
+ body: JSON.stringify({
23
+ bundle: draft.bundle,
24
+ note: draft.bundle.userNote ?? null,
25
+ projectKey
26
+ // belt + braces — header is the contract, body is the fallback
27
+ }),
28
+ credentials: "omit",
29
+ keepalive: true
30
+ });
31
+ } catch {
32
+ }
33
+ }
34
+ var C = {
35
+ ink: "#16161a",
36
+ sub: "#6b6c77",
37
+ faint: "#9a9aa4",
38
+ line: "#e9e9ee",
39
+ surface: "#ffffff",
40
+ surface2: "#f6f6f9",
41
+ accent: "linear-gradient(180deg,#6b63ff,#5751e6)",
42
+ accentRing: "rgba(91,91,240,.30)",
43
+ green: "#117a52",
44
+ sans: '-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,"Helvetica Neue",Arial,sans-serif',
45
+ mono: 'ui-monospace,"SF Mono",SFMono-Regular,Menlo,monospace',
46
+ shadow: "0 12px 40px rgba(20,20,35,.16),0 2px 8px rgba(20,20,35,.08)"
47
+ };
48
+ function defaultDeliver(draft) {
49
+ globalThis.__insitu_capture__ = {
50
+ title: draft.title,
51
+ body: draft.body,
52
+ bundle: draft.bundle
53
+ };
54
+ console.info("[insitue] capture:", draft.title);
55
+ try {
56
+ const blob = new Blob([JSON.stringify(draft, null, 2)], {
57
+ type: "application/json"
58
+ });
59
+ const a = document.createElement("a");
60
+ a.href = URL.createObjectURL(blob);
61
+ a.download = `insitue-capture-${Date.now()}.json`;
62
+ a.click();
63
+ URL.revokeObjectURL(a.href);
64
+ } catch {
65
+ }
66
+ }
67
+ function CaptureOnlyApp(props) {
68
+ const [phase, setPhase] = d("idle");
69
+ const [bundle, setBundle] = d(null);
70
+ const [note, setNote] = d("");
71
+ const sink = new IssueTrackerSink(async (draft) => {
72
+ globalThis.__insitu_capture__ = {
73
+ title: draft.title,
74
+ body: draft.body,
75
+ bundle: draft.bundle
76
+ };
77
+ if (props.onCapture) {
78
+ props.onCapture(draft, draft.bundle);
79
+ } else if (props.projectKey) {
80
+ await postCapture(
81
+ props.endpoint ?? DEFAULT_INGEST,
82
+ props.projectKey,
83
+ draft
84
+ );
85
+ } else {
86
+ defaultDeliver(draft);
87
+ }
88
+ });
89
+ y(() => {
90
+ if (phase !== "sent") return;
91
+ const t2 = setTimeout(() => {
92
+ setPhase("idle");
93
+ setBundle(null);
94
+ setNote("");
95
+ }, 2400);
96
+ return () => clearTimeout(t2);
97
+ }, [phase]);
98
+ const startPick = async () => {
99
+ setPhase("picking");
100
+ try {
101
+ const sel = await beginPick("element");
102
+ if (!sel) {
103
+ setPhase("idle");
104
+ return;
105
+ }
106
+ setBundle(await buildBundle(sel));
107
+ setPhase("compose");
108
+ } catch {
109
+ setPhase("idle");
110
+ }
111
+ };
112
+ const send = async () => {
113
+ if (!bundle) return;
114
+ setPhase("sending");
115
+ const withNote = note.trim() ? { ...bundle, userNote: note.trim() } : bundle;
116
+ try {
117
+ await sink.submit(withNote);
118
+ } finally {
119
+ setPhase("sent");
120
+ }
121
+ };
122
+ const reset = () => {
123
+ setPhase("idle");
124
+ setBundle(null);
125
+ setNote("");
126
+ };
127
+ const dot = k("span", {
128
+ style: `width:9px;height:9px;border-radius:3px;background:${C.accent};box-shadow:0 1px 4px ${C.accentRing}`
129
+ });
130
+ if (phase === "idle" || phase === "picking") {
131
+ const picking = phase === "picking";
132
+ return k(
133
+ "button",
134
+ {
135
+ onClick: picking ? void 0 : () => void startPick(),
136
+ 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`
137
+ },
138
+ picking ? [
139
+ k("span", {
140
+ style: `width:9px;height:9px;border-radius:50%;background:#5751e6;animation:ipulse 1.1s ${"ease-in-out"} infinite`
141
+ }),
142
+ k("span", { style: `color:${C.sub}` }, "Click the broken element"),
143
+ k("span", { style: `color:${C.faint}` }, "\xB7 Esc to cancel"),
144
+ k(
145
+ "style",
146
+ {},
147
+ "@keyframes ipulse{0%,100%{opacity:.35}50%{opacity:1}}"
148
+ )
149
+ ] : [dot, "Report a problem"]
150
+ );
151
+ }
152
+ const t = bundle?.target;
153
+ const targetLabel = t?.componentStack?.[0]?.name ?? t?.selector?.split(">").pop()?.trim() ?? "selection";
154
+ const card = (children) => k(
155
+ "div",
156
+ {
157
+ 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`
158
+ },
159
+ children
160
+ );
161
+ if (phase === "sent") {
162
+ return card([
163
+ k(
164
+ "div",
165
+ { style: "padding:30px 22px;text-align:center" },
166
+ [
167
+ k(
168
+ "div",
169
+ {
170
+ 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}`
171
+ },
172
+ "\u2713"
173
+ ),
174
+ k(
175
+ "div",
176
+ { style: "font-weight:680;font-size:15px;letter-spacing:-.01em" },
177
+ "Report sent"
178
+ ),
179
+ k(
180
+ "div",
181
+ { style: `color:${C.sub};margin-top:5px;font-size:13px` },
182
+ "The team has everything to reproduce and fix this \u2014 no follow-up needed."
183
+ )
184
+ ]
185
+ )
186
+ ]);
187
+ }
188
+ const sending = phase === "sending";
189
+ return card([
190
+ k(
191
+ "div",
192
+ {
193
+ style: `display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid ${C.line}`
194
+ },
195
+ [
196
+ k(
197
+ "div",
198
+ { style: "display:flex;align-items:center;gap:8px;font-weight:680;letter-spacing:-.01em" },
199
+ [dot, "Report a problem"]
200
+ ),
201
+ k(
202
+ "button",
203
+ {
204
+ onClick: reset,
205
+ style: `all:unset;cursor:pointer;color:${C.faint};font-size:16px;line-height:1;padding:2px 4px`
206
+ },
207
+ "\u2715"
208
+ )
209
+ ]
210
+ ),
211
+ k("div", { style: "padding:16px" }, [
212
+ k(
213
+ "div",
214
+ {
215
+ 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`
216
+ },
217
+ [
218
+ k("span", { style: `color:#5751e6` }, "\u25CE"),
219
+ k(
220
+ "span",
221
+ {
222
+ style: "overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
223
+ },
224
+ `${targetLabel}${t?.source ? ` \xB7 ${t.source.file.split("/").pop()}` : ""}`
225
+ )
226
+ ]
227
+ ),
228
+ bundle?.screenshot ? k("img", {
229
+ src: bundle.screenshot.dataUrl,
230
+ style: `width:100%;max-height:120px;object-fit:cover;border:1px solid ${C.line};border-radius:10px;margin-bottom:12px`
231
+ }) : bundle?.screenshotUnavailable ? k(
232
+ "div",
233
+ {
234
+ style: `font-size:12px;color:${C.faint};background:${C.surface2};border:1px solid ${C.line};border-radius:10px;padding:10px;margin-bottom:12px`
235
+ },
236
+ "Screenshot unavailable \u2014 sending the rest."
237
+ ) : null,
238
+ k("textarea", {
239
+ value: note,
240
+ rows: 3,
241
+ placeholder: "What's wrong? (optional but helps)",
242
+ disabled: sending,
243
+ onInput: (e) => setNote(e.target.value),
244
+ 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`
245
+ }),
246
+ k("div", { style: "display:flex;gap:8px;margin-top:12px" }, [
247
+ k(
248
+ "button",
249
+ {
250
+ onClick: sending ? void 0 : () => void send(),
251
+ disabled: sending,
252
+ 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"}`
253
+ },
254
+ sending ? "Sending\u2026" : "Send report"
255
+ ),
256
+ k(
257
+ "button",
258
+ {
259
+ onClick: () => void startPick(),
260
+ 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`
261
+ },
262
+ "Re-pick"
263
+ )
264
+ ])
265
+ ]),
266
+ k(
267
+ "div",
268
+ {
269
+ style: `display:flex;justify-content:space-between;padding:9px 16px;border-top:1px solid ${C.line};color:${C.faint};font-size:11px`
270
+ },
271
+ [k("span", {}, "\u{1F512} Secrets scrubbed automatically"), k("span", {}, "InSitue")]
272
+ )
273
+ ]);
274
+ }
275
+ function mountCaptureOnly(opts = {}) {
276
+ installRuntimeCollectors();
277
+ const host = document.createElement("div");
278
+ host.id = "insitu-capture-root";
279
+ host.setAttribute("data-insitu", "");
280
+ document.body.appendChild(host);
281
+ const shadow = host.attachShadow({ mode: "open" });
282
+ const mount = document.createElement("div");
283
+ shadow.appendChild(mount);
284
+ R(
285
+ k(CaptureOnlyApp, {
286
+ projectKey: opts.projectKey,
287
+ endpoint: opts.endpoint,
288
+ onCapture: opts.onCapture
289
+ }),
290
+ mount
291
+ );
292
+ return () => {
293
+ R(null, mount);
294
+ host.remove();
295
+ };
296
+ }
297
+
298
+ export {
299
+ mountCaptureOnly
300
+ };