@insitue/sdk 0.1.14 → 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.
- package/README.md +203 -0
- package/dist/capture-only.d.ts +26 -16
- package/dist/capture-only.js +1 -2
- package/dist/{chunk-HECY6THB.js → chunk-BCKDBCPG.js} +782 -69
- package/dist/index.d.ts +52 -25
- package/dist/index.js +26 -25
- package/package.json +2 -7
- package/dist/chunk-7EQRP6O4.js +0 -400
- package/dist/chunk-QTG3I7DC.js +0 -1188
- package/dist/overlay.d.ts +0 -6
- package/dist/overlay.js +0 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,49 +1,76 @@
|
|
|
1
1
|
import { IssueDraft, CaptureBundle } from '@insitue/capture-core';
|
|
2
|
-
|
|
2
|
+
import { CaptureSink } from './capture-only.js';
|
|
3
3
|
export { CaptureOnlyOptions, mountCaptureOnly } from './capture-only.js';
|
|
4
4
|
|
|
5
|
-
interface InSitueProps {
|
|
6
|
-
/** Companion loopback port (default 5747). */
|
|
7
|
-
port?: number;
|
|
8
|
-
}
|
|
9
|
-
declare function InSitue({ port }: InSitueProps): null;
|
|
10
5
|
interface InSitueCaptureProps {
|
|
11
6
|
/**
|
|
12
7
|
* Publishable project key (e.g. `pk_…`). When set, captures POST
|
|
13
8
|
* to the InSitue cloud automatically. Origin-pinned + quota-gated
|
|
14
|
-
* server-side, so safe to ship in your production bundle.
|
|
9
|
+
* server-side, so safe to ship in your production bundle. Auto-
|
|
10
|
+
* selects `sink: { kind: "cloud" }` unless `sink` is set
|
|
11
|
+
* explicitly.
|
|
15
12
|
*/
|
|
16
13
|
projectKey?: string;
|
|
17
|
-
/** Override the ingest endpoint (
|
|
14
|
+
/** Override the ingest endpoint (cloud sink). */
|
|
18
15
|
endpoint?: string;
|
|
19
16
|
/**
|
|
20
|
-
* Take over delivery yourself. Wins over `projectKey
|
|
21
|
-
* (
|
|
17
|
+
* Take over delivery yourself. Wins over `projectKey` AND
|
|
18
|
+
* `sink`. Default (none set + no companion reachable): console +
|
|
19
|
+
* JSON download + `window.__insitu_capture__`.
|
|
22
20
|
*/
|
|
23
21
|
onCapture?: (draft: IssueDraft, bundle: CaptureBundle) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Explicit sink override. Most callers leave this undefined and
|
|
24
|
+
* rely on auto-detection (projectKey → cloud, otherwise →
|
|
25
|
+
* companion).
|
|
26
|
+
*/
|
|
27
|
+
sink?: CaptureSink;
|
|
24
28
|
/**
|
|
25
29
|
* Default the user's "Always pixel-perfect screenshots" setting
|
|
26
|
-
* to `true` on mount — every capture uses
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
+
* to `true` on mount — every capture uses `getDisplayMedia`,
|
|
31
|
+
* paying a one-time tab-share permission per session in exchange
|
|
32
|
+
* for screenshots that are pixel-accurate across any content
|
|
33
|
+
* (next/image, video, canvas, cross-origin).
|
|
30
34
|
*
|
|
31
|
-
* Recommended for dev / dogfood
|
|
32
|
-
* more than the permission UX. Not the default — production
|
|
33
|
-
* end-users shouldn't see a permission dialog they didn't ask for.
|
|
35
|
+
* Recommended for dev / dogfood; not for production end-users.
|
|
34
36
|
*/
|
|
35
37
|
defaultPixelPerfect?: boolean;
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
40
|
+
* The canonical InSitue widget. Use this for both prod (with
|
|
41
|
+
* `projectKey`) and dev (without). One mount, one component, the
|
|
42
|
+
* UI and theme adapt to the sink.
|
|
43
|
+
*/
|
|
44
|
+
declare function InSitueCapture({ projectKey, endpoint, onCapture, sink, defaultPixelPerfect, }: InSitueCaptureProps): null;
|
|
45
|
+
interface InSitueProps {
|
|
46
|
+
/**
|
|
47
|
+
* Companion loopback port (default 5747). Only meaningful when
|
|
48
|
+
* not also passing `projectKey` — when `projectKey` is set, the
|
|
49
|
+
* widget ships to the cloud sink and this is ignored.
|
|
50
|
+
*/
|
|
51
|
+
port?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `<InSitue />` — backward-compat dev alias.
|
|
55
|
+
*
|
|
56
|
+
* Equivalent to `<InSitueCapture sink={{ kind: "companion", port }} />`.
|
|
57
|
+
* Kept so existing `<InSitue />` mounts (callers that imported the
|
|
58
|
+
* pre-0.3.0 chat overlay) keep compiling. The behaviour is the
|
|
59
|
+
* unified widget, NOT the removed chat overlay — the in-overlay
|
|
60
|
+
* thread, diff display, and SESSION history are gone in 0.3.0.
|
|
61
|
+
*
|
|
62
|
+
* For new code, prefer `<InSitueCapture />`.
|
|
63
|
+
*/
|
|
64
|
+
declare function InSitue({ port }: InSitueProps): null;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* `@insitue/sdk` — the InSitue capture widget for browser apps.
|
|
42
68
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
69
|
+
* One component, two sinks. `<InSitueCapture />` is the canonical
|
|
70
|
+
* mount; `<InSitue />` is a backward-compat dev alias (companion
|
|
71
|
+
* sink). The chat-style overlay that existed pre-0.3.0 has been
|
|
72
|
+
* removed in favour of the unified widget.
|
|
45
73
|
*/
|
|
46
|
-
declare function InSitueCapture({ projectKey, endpoint, onCapture, defaultPixelPerfect, }: InSitueCaptureProps): null;
|
|
47
74
|
|
|
48
75
|
/** Build-time-inlined version of `@insitue/sdk` (from package.json).
|
|
49
76
|
* Exposed so the host app can self-verify which SDK build is loaded
|
|
@@ -51,4 +78,4 @@ declare function InSitueCapture({ projectKey, endpoint, onCapture, defaultPixelP
|
|
|
51
78
|
* the capture widget footer so a screenshot proves the build. */
|
|
52
79
|
declare const SDK_VERSION: string;
|
|
53
80
|
|
|
54
|
-
export { InSitue, InSitueCapture, type InSitueCaptureProps, type InSitueProps, SDK_VERSION };
|
|
81
|
+
export { CaptureSink, InSitue, InSitueCapture, type InSitueCaptureProps, type InSitueProps, SDK_VERSION };
|
package/dist/index.js
CHANGED
|
@@ -1,45 +1,47 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mountCaptureOnly
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
mountInSitue
|
|
6
|
-
} from "./chunk-QTG3I7DC.js";
|
|
7
|
-
import "./chunk-HECY6THB.js";
|
|
3
|
+
} from "./chunk-BCKDBCPG.js";
|
|
8
4
|
|
|
9
5
|
// src/InSitue.tsx
|
|
10
6
|
import { useEffect } from "react";
|
|
11
|
-
function
|
|
7
|
+
function InSitueCapture({
|
|
8
|
+
projectKey,
|
|
9
|
+
endpoint,
|
|
10
|
+
onCapture,
|
|
11
|
+
sink,
|
|
12
|
+
defaultPixelPerfect
|
|
13
|
+
}) {
|
|
12
14
|
useEffect(() => {
|
|
13
|
-
const nodeEnv = typeof process !== "undefined" ? process.env?.NODE_ENV : void 0;
|
|
14
|
-
if (nodeEnv === "production") return;
|
|
15
15
|
let active = true;
|
|
16
16
|
let dispose;
|
|
17
|
-
void import("./
|
|
18
|
-
if (active)
|
|
17
|
+
void import("./capture-only.js").then((m) => {
|
|
18
|
+
if (active) {
|
|
19
|
+
dispose = m.mountCaptureOnly({
|
|
20
|
+
...projectKey ? { projectKey } : {},
|
|
21
|
+
...endpoint ? { endpoint } : {},
|
|
22
|
+
...onCapture ? { onCapture } : {},
|
|
23
|
+
...sink ? { sink } : {},
|
|
24
|
+
...defaultPixelPerfect === true ? { defaultPixelPerfect } : {}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
19
27
|
});
|
|
20
28
|
return () => {
|
|
21
29
|
active = false;
|
|
22
30
|
dispose?.();
|
|
23
31
|
};
|
|
24
|
-
}, [
|
|
32
|
+
}, [projectKey, endpoint, onCapture, sink, defaultPixelPerfect]);
|
|
25
33
|
return null;
|
|
26
34
|
}
|
|
27
|
-
function
|
|
28
|
-
projectKey,
|
|
29
|
-
endpoint,
|
|
30
|
-
onCapture,
|
|
31
|
-
defaultPixelPerfect
|
|
32
|
-
}) {
|
|
35
|
+
function InSitue({ port }) {
|
|
33
36
|
useEffect(() => {
|
|
37
|
+
const nodeEnv = typeof process !== "undefined" ? process.env?.NODE_ENV : void 0;
|
|
38
|
+
if (nodeEnv === "production") return;
|
|
34
39
|
let active = true;
|
|
35
40
|
let dispose;
|
|
36
41
|
void import("./capture-only.js").then((m) => {
|
|
37
42
|
if (active) {
|
|
38
43
|
dispose = m.mountCaptureOnly({
|
|
39
|
-
|
|
40
|
-
endpoint,
|
|
41
|
-
onCapture,
|
|
42
|
-
defaultPixelPerfect
|
|
44
|
+
sink: port === void 0 ? { kind: "companion" } : { kind: "companion", port }
|
|
43
45
|
});
|
|
44
46
|
}
|
|
45
47
|
});
|
|
@@ -47,16 +49,15 @@ function InSitueCapture({
|
|
|
47
49
|
active = false;
|
|
48
50
|
dispose?.();
|
|
49
51
|
};
|
|
50
|
-
}, [
|
|
52
|
+
}, [port]);
|
|
51
53
|
return null;
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
// src/index.ts
|
|
55
|
-
var SDK_VERSION = "0.1
|
|
57
|
+
var SDK_VERSION = "0.3.1";
|
|
56
58
|
export {
|
|
57
59
|
InSitue,
|
|
58
60
|
InSitueCapture,
|
|
59
61
|
SDK_VERSION,
|
|
60
|
-
mountCaptureOnly
|
|
61
|
-
mountInSitue
|
|
62
|
+
mountCaptureOnly
|
|
62
63
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@insitue/sdk",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "InSitue capture SDK — drop one snippet into your deployed app; your users point at a bug, InSitue opens a verified pull request.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,11 +13,6 @@
|
|
|
13
13
|
"import": "./dist/index.js",
|
|
14
14
|
"default": "./dist/index.js"
|
|
15
15
|
},
|
|
16
|
-
"./overlay": {
|
|
17
|
-
"types": "./dist/overlay.d.ts",
|
|
18
|
-
"import": "./dist/overlay.js",
|
|
19
|
-
"default": "./dist/overlay.js"
|
|
20
|
-
},
|
|
21
16
|
"./capture-only": {
|
|
22
17
|
"types": "./dist/capture-only.d.ts",
|
|
23
18
|
"import": "./dist/capture-only.js",
|
|
@@ -56,7 +51,7 @@
|
|
|
56
51
|
"tsup": "^8.3.5",
|
|
57
52
|
"typescript": "^5.6.3",
|
|
58
53
|
"vitest": "^3.2.4",
|
|
59
|
-
"@insitue/capture-core": "0.
|
|
54
|
+
"@insitue/capture-core": "0.2.0"
|
|
60
55
|
},
|
|
61
56
|
"repository": {
|
|
62
57
|
"type": "git",
|
package/dist/chunk-7EQRP6O4.js
DELETED
|
@@ -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-HECY6THB.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.1.14"}` },
|
|
366
|
-
`InSitue \xB7 v${"0.1.14"}`
|
|
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
|
-
};
|