@sightspool/sdk 0.2.0 → 0.3.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.
- package/README.md +8 -8
- package/dist/index.cjs +159 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +40 -13
- package/dist/index.js.map +1 -1
- package/dist/probe-BNGMQQ5C.js +113 -0
- package/dist/probe-BNGMQQ5C.js.map +1 -0
- package/dist/sdk.global.js +25 -4
- package/dist/sdk.global.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ call for their analytics/support tools.
|
|
|
47
47
|
```html
|
|
48
48
|
<script
|
|
49
49
|
async
|
|
50
|
-
src="https://
|
|
50
|
+
src="https://www.sightspool.com/sdk.global.js"
|
|
51
51
|
data-sightspool-key="pk_live_…"
|
|
52
52
|
></script>
|
|
53
53
|
```
|
|
@@ -70,7 +70,7 @@ The script tag also reads these optional attributes (the no-build equivalent of
|
|
|
70
70
|
```html
|
|
71
71
|
<script
|
|
72
72
|
async
|
|
73
|
-
src="https://
|
|
73
|
+
src="https://www.sightspool.com/sdk.global.js"
|
|
74
74
|
data-sightspool-key="pk_live_…"
|
|
75
75
|
data-sightspool-block=".billing-panel, [data-private]"
|
|
76
76
|
data-sightspool-redact=".customer-name"
|
|
@@ -88,7 +88,7 @@ The script tag also reads these optional attributes (the no-build equivalent of
|
|
|
88
88
|
| option | type | default | purpose |
|
|
89
89
|
|---|---|---|---|
|
|
90
90
|
| `key` | `string` | — | **required.** Your publishable key (`pk_test_…` / `pk_live_…`), from the Connections → In-product SDK card. Publishable — safe to ship in client JS. See [Keys & environments](#keys--environments). |
|
|
91
|
-
| `endpoint` | `string` | the bundle's origin (script tag) / `https://
|
|
91
|
+
| `endpoint` | `string` | the bundle's origin (script tag) / `https://www.sightspool.com` (npm) | Ingest base URL. The `<script>` install auto-resolves it to wherever `sdk.global.js` was served from (your app), so the key alone is enough; override for a CDN-hosted bundle or dev. |
|
|
92
92
|
| `boundaryAsk` | `boolean` | `true` | Show the one-tap "did you do what you came to do?" ask at session boundaries. |
|
|
93
93
|
| `interventions` | `boolean` | `true` | Show human-approved **surveys** served by your Sightspool workspace (see [Interventions](#interventions-surveys)). Set `false` to capture only. |
|
|
94
94
|
| `consent` | `boolean` | `true` | Start capturing immediately. Set `false` to stay paused until you call `Sightspool.consent(true)` (or `start()`) after obtaining consent. |
|
|
@@ -164,7 +164,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
164
164
|
<body>
|
|
165
165
|
{children}
|
|
166
166
|
<Script
|
|
167
|
-
src="https://
|
|
167
|
+
src="https://www.sightspool.com/sdk.global.js"
|
|
168
168
|
data-sightspool-key={process.env.NEXT_PUBLIC_SIGHTSPOOL_KEY}
|
|
169
169
|
strategy="afterInteractive"
|
|
170
170
|
/>
|
|
@@ -237,15 +237,15 @@ from the app it ingests to), so it's one host in two directives:
|
|
|
237
237
|
**Script-tag install**
|
|
238
238
|
|
|
239
239
|
```
|
|
240
|
-
script-src https://
|
|
241
|
-
connect-src https://
|
|
240
|
+
script-src https://www.sightspool.com;
|
|
241
|
+
connect-src https://www.sightspool.com;
|
|
242
242
|
```
|
|
243
243
|
|
|
244
244
|
**npm / bundler install** — the SDK is bundled into your own first-party JS, so no
|
|
245
245
|
`script-src` host is needed; only the ingest origin:
|
|
246
246
|
|
|
247
247
|
```
|
|
248
|
-
connect-src https://
|
|
248
|
+
connect-src https://www.sightspool.com;
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
If you pass a custom `endpoint`, use *that* origin in `connect-src`. The beacon goes via
|
|
@@ -281,7 +281,7 @@ Sightspool.init({ key: "pk_live_…", interventions: false }); // capture only
|
|
|
281
281
|
|
|
282
282
|
```html
|
|
283
283
|
<!-- script-tag install: opt out with one attribute -->
|
|
284
|
-
<script src="https://
|
|
284
|
+
<script src="https://www.sightspool.com/sdk.global.js"
|
|
285
285
|
data-sightspool-key="pk_live_…"
|
|
286
286
|
data-sightspool-no-interventions></script>
|
|
287
287
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -199,6 +199,125 @@ input { font: inherit; font-size: 13px; width: 100%; box-sizing: border-box; pad
|
|
|
199
199
|
}
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
+
// src/probe.ts
|
|
203
|
+
var probe_exports = {};
|
|
204
|
+
__export(probe_exports, {
|
|
205
|
+
showProbe: () => showProbe
|
|
206
|
+
});
|
|
207
|
+
function findSlot(slot) {
|
|
208
|
+
try {
|
|
209
|
+
return document.querySelector(`[data-sightspool-slot="${slot}"]`);
|
|
210
|
+
} catch (e) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function showProbe(config) {
|
|
215
|
+
return new Promise((resolve) => {
|
|
216
|
+
if (typeof document === "undefined") {
|
|
217
|
+
resolve({ dismissed: true, mounted: false });
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const anchor = config.slot ? findSlot(config.slot) : null;
|
|
221
|
+
if (config.slot && !anchor) {
|
|
222
|
+
resolve({ mounted: false });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
let settled = false;
|
|
226
|
+
const inline = !!anchor;
|
|
227
|
+
const host = document.createElement(inline ? "span" : "div");
|
|
228
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
229
|
+
function teardown() {
|
|
230
|
+
try {
|
|
231
|
+
host.remove();
|
|
232
|
+
} catch (e) {
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function finish(result, disclose) {
|
|
236
|
+
if (settled) return;
|
|
237
|
+
settled = true;
|
|
238
|
+
resolve(result);
|
|
239
|
+
if (disclose) {
|
|
240
|
+
disclosure();
|
|
241
|
+
setTimeout(teardown, DISCLOSURE_MS);
|
|
242
|
+
} else {
|
|
243
|
+
teardown();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const style = document.createElement("style");
|
|
247
|
+
style.textContent = inline ? INLINE_STYLE : FLOATING_STYLE;
|
|
248
|
+
shadow.appendChild(style);
|
|
249
|
+
const wrap = document.createElement(inline ? "span" : "div");
|
|
250
|
+
if (!inline) wrap.className = "wrap";
|
|
251
|
+
shadow.appendChild(wrap);
|
|
252
|
+
function container() {
|
|
253
|
+
wrap.innerHTML = "";
|
|
254
|
+
if (inline) return wrap;
|
|
255
|
+
const c = document.createElement("div");
|
|
256
|
+
c.className = "card";
|
|
257
|
+
wrap.appendChild(c);
|
|
258
|
+
return c;
|
|
259
|
+
}
|
|
260
|
+
function probe() {
|
|
261
|
+
const c = container();
|
|
262
|
+
if (!inline) {
|
|
263
|
+
const x = document.createElement("button");
|
|
264
|
+
x.className = "x";
|
|
265
|
+
x.textContent = "\xD7";
|
|
266
|
+
x.setAttribute("aria-label", "Dismiss");
|
|
267
|
+
x.onclick = () => finish({ dismissed: true, mounted: true }, false);
|
|
268
|
+
c.appendChild(x);
|
|
269
|
+
}
|
|
270
|
+
const b = document.createElement("button");
|
|
271
|
+
b.className = "probe";
|
|
272
|
+
b.textContent = config.label;
|
|
273
|
+
b.onclick = () => finish({ voted: true, mounted: true }, true);
|
|
274
|
+
c.appendChild(b);
|
|
275
|
+
}
|
|
276
|
+
function disclosure() {
|
|
277
|
+
const c = container();
|
|
278
|
+
const p = document.createElement("p");
|
|
279
|
+
p.className = "done";
|
|
280
|
+
p.textContent = config.disclosure;
|
|
281
|
+
c.appendChild(p);
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
(anchor != null ? anchor : document.body).appendChild(host);
|
|
285
|
+
probe();
|
|
286
|
+
} catch (e) {
|
|
287
|
+
finish({ dismissed: true, mounted: false }, false);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
var FLOATING_STYLE, INLINE_STYLE, DISCLOSURE_MS;
|
|
292
|
+
var init_probe = __esm({
|
|
293
|
+
"src/probe.ts"() {
|
|
294
|
+
FLOATING_STYLE = `
|
|
295
|
+
:host { all: initial; }
|
|
296
|
+
.wrap { position: fixed; bottom: 20px; right: 20px; z-index: 2147483000;
|
|
297
|
+
width: 340px; max-width: calc(100vw - 32px); font-family: system-ui, -apple-system, sans-serif; }
|
|
298
|
+
.card { position: relative; background: #fff; color: #18181b; border: 1px solid #e4e4e7;
|
|
299
|
+
border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,.12); padding: 16px; }
|
|
300
|
+
.probe { font: inherit; font-size: 13px; cursor: pointer; display: block; width: 100%;
|
|
301
|
+
text-align: left; border: 1px dashed #a1a1aa; border-radius: 8px; padding: 10px 12px;
|
|
302
|
+
background: #fafafa; color: #3f3f46; }
|
|
303
|
+
.probe:hover { border-color: #18181b; color: #18181b; }
|
|
304
|
+
.x { position: absolute; top: 8px; right: 10px; border: none; background: none; font-size: 16px;
|
|
305
|
+
color: #a1a1aa; padding: 2px 6px; cursor: pointer; }
|
|
306
|
+
.done { font-size: 13px; color: #18181b; margin: 0; line-height: 1.4; }
|
|
307
|
+
`;
|
|
308
|
+
INLINE_STYLE = `
|
|
309
|
+
:host { all: initial; display: inline-block; max-width: 100%; }
|
|
310
|
+
.probe { font: inherit; font-family: system-ui, -apple-system, sans-serif; font-size: 13px;
|
|
311
|
+
cursor: pointer; display: inline-block; text-align: left; border: 1px dashed #a1a1aa;
|
|
312
|
+
border-radius: 8px; padding: 8px 12px; background: transparent; color: #52525b; }
|
|
313
|
+
.probe:hover { border-color: #18181b; color: #18181b; }
|
|
314
|
+
.done { font-family: system-ui, -apple-system, sans-serif; font-size: 13px; color: #52525b;
|
|
315
|
+
margin: 0; line-height: 1.4; border: 1px dashed #d4d4d8; border-radius: 8px; padding: 8px 12px; }
|
|
316
|
+
`;
|
|
317
|
+
DISCLOSURE_MS = 3200;
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
202
321
|
// src/survey.ts
|
|
203
322
|
var survey_exports = {};
|
|
204
323
|
__export(survey_exports, {
|
|
@@ -725,7 +844,7 @@ function canPrompt(state, now, caps = DEFAULT_CAPS) {
|
|
|
725
844
|
}
|
|
726
845
|
|
|
727
846
|
// src/intervention.ts
|
|
728
|
-
var RENDERABLE = /* @__PURE__ */ new Set(["survey"]);
|
|
847
|
+
var RENDERABLE = /* @__PURE__ */ new Set(["survey", "demand_probe"]);
|
|
729
848
|
function str(v, max) {
|
|
730
849
|
if (typeof v !== "string") return void 0;
|
|
731
850
|
const t = v.trim();
|
|
@@ -748,6 +867,14 @@ function normalizeServed(raw) {
|
|
|
748
867
|
const type = str(r.type, 40);
|
|
749
868
|
if (!id || !type || !RENDERABLE.has(type)) return null;
|
|
750
869
|
const cfg = r.config && typeof r.config === "object" ? r.config : {};
|
|
870
|
+
if (type === "demand_probe") {
|
|
871
|
+
const label = str(cfg.label, 200);
|
|
872
|
+
const disclosure = str(cfg.disclosure, 300);
|
|
873
|
+
if (!label || !disclosure) return null;
|
|
874
|
+
const rawSlot = str(cfg.slot, 64);
|
|
875
|
+
const slot = rawSlot && /^[a-z0-9][a-z0-9_-]*$/i.test(rawSlot) ? rawSlot : void 0;
|
|
876
|
+
return { id, type, config: { label, disclosure, ...slot ? { slot } : {} } };
|
|
877
|
+
}
|
|
751
878
|
const question = str(cfg.question, 500);
|
|
752
879
|
if (!question) return null;
|
|
753
880
|
const options = Array.isArray(cfg.options) ? cfg.options.map((o) => str(o, 200)).filter((o) => !!o).slice(0, 6) : void 0;
|
|
@@ -756,7 +883,7 @@ function normalizeServed(raw) {
|
|
|
756
883
|
...options && options.length ? { options } : {},
|
|
757
884
|
allow_text: cfg.allow_text === true
|
|
758
885
|
};
|
|
759
|
-
return { id, type, config };
|
|
886
|
+
return { id, type: "survey", config };
|
|
760
887
|
}
|
|
761
888
|
function pickRespondentKey(userId, persisted, sessionRef) {
|
|
762
889
|
const uid = str(userId, 200);
|
|
@@ -844,7 +971,7 @@ function isLocalhost(hostname) {
|
|
|
844
971
|
}
|
|
845
972
|
|
|
846
973
|
// src/index.ts
|
|
847
|
-
var DEFAULT_ENDPOINT = "https://
|
|
974
|
+
var DEFAULT_ENDPOINT = "https://www.sightspool.com";
|
|
848
975
|
var FRICTION_EMIT_COOLDOWN_MS = 3e4;
|
|
849
976
|
var EVENT_DEBOUNCE_MS = 600;
|
|
850
977
|
var SERVE_SETTLE_MS = 2500;
|
|
@@ -929,20 +1056,39 @@ async function maybeServeIntervention(c) {
|
|
|
929
1056
|
debug: c.config.debug
|
|
930
1057
|
});
|
|
931
1058
|
if (!served || !c.running) return;
|
|
1059
|
+
const prevShown = c.interventionsShown;
|
|
1060
|
+
const prevPromptAt = c.fatigue.lastPromptAt;
|
|
932
1061
|
c.surveyOnScreen = true;
|
|
933
1062
|
c.interventionsShown += 1;
|
|
934
1063
|
c.fatigue.lastPromptAt = Date.now();
|
|
935
1064
|
try {
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1065
|
+
if (served.type === "demand_probe") {
|
|
1066
|
+
const { showProbe: showProbe2 } = await Promise.resolve().then(() => (init_probe(), probe_exports));
|
|
1067
|
+
const result = await showProbe2(served.config);
|
|
1068
|
+
if (!result.mounted) {
|
|
1069
|
+
c.interventionsShown = prevShown;
|
|
1070
|
+
c.fatigue.lastPromptAt = prevPromptAt;
|
|
1071
|
+
} else if (result.voted) {
|
|
1072
|
+
submitResponse({
|
|
1073
|
+
endpoint: c.config.endpoint,
|
|
1074
|
+
key: c.config.key,
|
|
1075
|
+
interventionId: served.id,
|
|
1076
|
+
response: { choice: "vote" },
|
|
1077
|
+
respondentKey
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
} else {
|
|
1081
|
+
const { showSurvey: showSurvey2 } = await Promise.resolve().then(() => (init_survey(), survey_exports));
|
|
1082
|
+
const result = await showSurvey2(served.config);
|
|
1083
|
+
if (!result.dismissed && (result.choice || result.text)) {
|
|
1084
|
+
submitResponse({
|
|
1085
|
+
endpoint: c.config.endpoint,
|
|
1086
|
+
key: c.config.key,
|
|
1087
|
+
interventionId: served.id,
|
|
1088
|
+
response: { choice: result.choice, text: result.text },
|
|
1089
|
+
respondentKey
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
946
1092
|
}
|
|
947
1093
|
} catch (e) {
|
|
948
1094
|
} finally {
|