@guidelytech/sdk-core 0.4.1 → 0.6.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/dist/capture-C3faAYb7.d.cts +99 -0
- package/dist/capture-Dqd7up2n.d.ts +99 -0
- package/dist/capture.cjs +313 -0
- package/dist/capture.cjs.map +1 -0
- package/dist/capture.d.cts +3 -0
- package/dist/capture.d.ts +3 -0
- package/dist/capture.js +235 -0
- package/dist/capture.js.map +1 -0
- package/dist/{chunk-GJT7CFAC.js → chunk-AKNE7D5M.js} +14 -5
- package/dist/chunk-AKNE7D5M.js.map +1 -0
- package/dist/chunk-CYAG6ZYX.js +85 -0
- package/dist/chunk-CYAG6ZYX.js.map +1 -0
- package/dist/{chunk-HLR3W63Z.js → chunk-GDF6RCHQ.js} +6 -79
- package/dist/chunk-GDF6RCHQ.js.map +1 -0
- package/dist/headless.cjs +18 -5
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.cts +3 -49
- package/dist/headless.d.ts +3 -49
- package/dist/headless.js +6 -4
- package/dist/http-47tiAlOM.d.cts +86 -0
- package/dist/http-47tiAlOM.d.ts +86 -0
- package/dist/index.cjs +311 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -4
- package/dist/index.d.ts +23 -4
- package/dist/index.js +275 -39
- package/dist/index.js.map +1 -1
- package/dist/recorder.cjs +1 -1
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.js +2 -2
- package/dist/recorder.js.map +1 -1
- package/dist/{types-BZ9r3Fs9.d.cts → types-RyeRQd9f.d.ts} +9 -30
- package/dist/{types-BZ9r3Fs9.d.ts → types-d4eOerxi.d.cts} +9 -30
- package/package.json +8 -3
- package/dist/chunk-GJT7CFAC.js.map +0 -1
- package/dist/chunk-HLR3W63Z.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Emitter,
|
|
3
3
|
EventQueue,
|
|
4
|
-
KEYS,
|
|
5
4
|
areMutuallyExclusive,
|
|
6
|
-
createStorage,
|
|
7
5
|
isExclusiveGuide,
|
|
8
6
|
isFrequencyEligible,
|
|
9
|
-
isWithinSchedule
|
|
7
|
+
isWithinSchedule
|
|
8
|
+
} from "./chunk-GDF6RCHQ.js";
|
|
9
|
+
import {
|
|
10
|
+
KEYS,
|
|
11
|
+
createStorage,
|
|
10
12
|
readJson,
|
|
11
13
|
writeJson
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-CYAG6ZYX.js";
|
|
13
15
|
import {
|
|
14
16
|
Api,
|
|
15
17
|
__publicField,
|
|
16
18
|
postWithRetry
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-AKNE7D5M.js";
|
|
18
20
|
|
|
19
21
|
// src/client.ts
|
|
20
22
|
import { evaluateRules, randomId, rolloutBucket, sanitizeProperties, supportsPlatform } from "@guidelytech/shared/runtime";
|
|
@@ -25,7 +27,7 @@ import {
|
|
|
25
27
|
renderResourceCenter,
|
|
26
28
|
renderSurvey,
|
|
27
29
|
runGuide as runGuide2,
|
|
28
|
-
urlMatches,
|
|
30
|
+
urlMatches as urlMatches2,
|
|
29
31
|
waitForTarget
|
|
30
32
|
} from "@guidelytech/guide-runtime";
|
|
31
33
|
import { createScope } from "@guidelytech/guide-runtime";
|
|
@@ -338,6 +340,43 @@ var AuthoringBridge = class {
|
|
|
338
340
|
}
|
|
339
341
|
};
|
|
340
342
|
|
|
343
|
+
// src/capture-token.ts
|
|
344
|
+
import { CAPTURE_PARAM } from "@guidelytech/shared/runtime";
|
|
345
|
+
function takeCaptureToken(sessionStore) {
|
|
346
|
+
if (typeof window === "undefined") return null;
|
|
347
|
+
let token = null;
|
|
348
|
+
let authoringLink = false;
|
|
349
|
+
try {
|
|
350
|
+
const url = new URL(window.location.href);
|
|
351
|
+
token = url.searchParams.get(CAPTURE_PARAM);
|
|
352
|
+
authoringLink = url.searchParams.has(AUTHORING_PARAM);
|
|
353
|
+
if (token) {
|
|
354
|
+
url.searchParams.delete(CAPTURE_PARAM);
|
|
355
|
+
window.history.replaceState(window.history.state, "", url.toString());
|
|
356
|
+
}
|
|
357
|
+
} catch {
|
|
358
|
+
token = null;
|
|
359
|
+
}
|
|
360
|
+
const saved = readJson(sessionStore, KEYS.capture, null);
|
|
361
|
+
let marker = null;
|
|
362
|
+
if (token) marker = saved?.token === token ? saved : { token };
|
|
363
|
+
else if (saved?.token && !authoringLink) marker = saved;
|
|
364
|
+
if (!marker) {
|
|
365
|
+
if (saved) {
|
|
366
|
+
sessionStore.remove(KEYS.capture);
|
|
367
|
+
sessionStore.remove(KEYS.capturePending);
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
if (token && saved?.token !== token) sessionStore.remove(KEYS.capturePending);
|
|
372
|
+
writeJson(sessionStore, KEYS.capture, marker);
|
|
373
|
+
sessionStore.remove(KEYS.authoring);
|
|
374
|
+
return marker;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/load-capture.ts
|
|
378
|
+
var loadCapture = async () => (await import("./capture.js")).capture;
|
|
379
|
+
|
|
341
380
|
// src/autocapture.ts
|
|
342
381
|
import { isGuidelyElement, isStableId } from "@guidelytech/guide-runtime";
|
|
343
382
|
var INTERACTIVE_SELECTOR = "a,button,input[type=button],input[type=submit],input[type=reset],input[type=checkbox],input[type=radio],select,summary,label,[role=button],[role=link],[role=tab],[role=menuitem],[role=checkbox],[role=switch],[role=option],[onclick],[data-guidely-track]";
|
|
@@ -673,6 +712,15 @@ var ExceptionThrottle = class {
|
|
|
673
712
|
// src/messenger-api.ts
|
|
674
713
|
var MAX_STORED = 50;
|
|
675
714
|
var TOKEN_HEADER = "X-Guidely-Conversation";
|
|
715
|
+
function pagePath(url) {
|
|
716
|
+
if (!url) return void 0;
|
|
717
|
+
try {
|
|
718
|
+
const u = new URL(url);
|
|
719
|
+
return (u.pathname + (/^#!?\//.test(u.hash) ? u.hash.split("?")[0] : "")).slice(0, 2e3);
|
|
720
|
+
} catch {
|
|
721
|
+
return void 0;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
676
724
|
function storedConversations(store, projectId) {
|
|
677
725
|
const list = readJson(store, KEYS.conversations(projectId), []);
|
|
678
726
|
return Array.isArray(list) ? list.filter((c) => c && typeof c.id === "string" && typeof c.token === "string") : [];
|
|
@@ -747,7 +795,7 @@ function createMessengerTransport(opts) {
|
|
|
747
795
|
return exchange("/conversations", { anonymousId: opts.anonymousId(), userId: opts.userId(), message: text, pageUrl: opts.pageUrl() }, {}, h, (r) => remember(r.conversation.id, r.token));
|
|
748
796
|
},
|
|
749
797
|
send(id, text, h) {
|
|
750
|
-
return exchange(`/conversations/${encodeURIComponent(id)}/messages`, { message: text }, { [TOKEN_HEADER]: tokenFor(id) }, h);
|
|
798
|
+
return exchange(`/conversations/${encodeURIComponent(id)}/messages`, { message: text, pageUrl: opts.pageUrl() }, { [TOKEN_HEADER]: tokenFor(id) }, h);
|
|
751
799
|
},
|
|
752
800
|
async handoff(id) {
|
|
753
801
|
const res = await api.post(`/conversations/${encodeURIComponent(id)}/handoff`, {}, { headers: { [TOKEN_HEADER]: tokenFor(id) } });
|
|
@@ -767,13 +815,98 @@ function createMessengerTransport(opts) {
|
|
|
767
815
|
const res = await api.get(`/articles/${encodeURIComponent(id)}`);
|
|
768
816
|
return res.ok && res.data ? res.data.article : null;
|
|
769
817
|
},
|
|
818
|
+
async pageContent(path) {
|
|
819
|
+
const res = await api.post("/messenger/page", { anonymousId: opts.anonymousId(), userId: opts.userId(), path: path.split(/[?#]/)[0] || "/" });
|
|
820
|
+
if (!res.ok || !res.data) throw new HttpError(res.status);
|
|
821
|
+
return res.data;
|
|
822
|
+
},
|
|
770
823
|
async searchArticles(q) {
|
|
771
824
|
const res = await api.get("/resource-center/search", { params: { q } });
|
|
772
825
|
return res.ok && res.data ? res.data.articles : [];
|
|
826
|
+
},
|
|
827
|
+
async listHowTos() {
|
|
828
|
+
const res = await api.get("/how-tos", { params: { url: pagePath(opts.pageUrl()) } });
|
|
829
|
+
if (!res.ok || !res.data) throw new HttpError(res.status);
|
|
830
|
+
return res.data;
|
|
831
|
+
},
|
|
832
|
+
async getHowTo(id) {
|
|
833
|
+
const res = await api.get(`/how-tos/${encodeURIComponent(id)}`);
|
|
834
|
+
return res.ok && res.data ? res.data.howTo : null;
|
|
773
835
|
}
|
|
774
836
|
};
|
|
775
837
|
}
|
|
776
838
|
|
|
839
|
+
// src/messenger-visitor.ts
|
|
840
|
+
import { firstNameFromTraits } from "@guidelytech/shared/runtime";
|
|
841
|
+
import { urlMatches } from "@guidelytech/guide-runtime";
|
|
842
|
+
var PAGE_GUIDE_KINDS = /* @__PURE__ */ new Set(["flow", "tooltip", "modal"]);
|
|
843
|
+
var CATCH_ALL = /* @__PURE__ */ new Set(["", "*", "**", "/*", "/**", "/"]);
|
|
844
|
+
var RESUME_WITHIN_MS = 14 * 864e5;
|
|
845
|
+
var NEWS_WITHIN_MS = 90 * 864e5;
|
|
846
|
+
var MAX_NEWS = 10;
|
|
847
|
+
function pathOf(url) {
|
|
848
|
+
try {
|
|
849
|
+
return new URL(url, "http://x.invalid").pathname || "/";
|
|
850
|
+
} catch {
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
function targetsPage(guide, url) {
|
|
855
|
+
const c = guide.config;
|
|
856
|
+
const checks = [...c.urlPatterns, ...c.trigger.urlPattern ? [c.trigger.urlPattern] : [], ...c.trigger.type === "url_match" && c.trigger.value ? [c.trigger.value] : [], ...c.steps[0]?.url ? [c.steps[0].url] : []];
|
|
857
|
+
const specific = checks.filter((p) => !CATCH_ALL.has(p.trim()));
|
|
858
|
+
return specific.length > 0 && checks.every((p) => urlMatches(p, url));
|
|
859
|
+
}
|
|
860
|
+
function plainText(html, max = 280) {
|
|
861
|
+
const text = html.replace(/<(br|\/p|\/div|\/li|\/h[1-6])\s*\/?>/gi, " ").replace(/<[^>]*>/g, "").replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&").replace(/\s+/g, " ").trim();
|
|
862
|
+
return text.length > max ? `${text.slice(0, max - 1).trimEnd()}\u2026` : text;
|
|
863
|
+
}
|
|
864
|
+
function buildMessengerVisitor(input) {
|
|
865
|
+
const now = input.now ?? Date.now();
|
|
866
|
+
const { guides, state } = input;
|
|
867
|
+
const done = (g) => !!state.guides[g.id]?.completedAt;
|
|
868
|
+
const pageGuides = guides.filter((g) => PAGE_GUIDE_KINDS.has(g.kind) && g.config.steps.length > 0 && !done(g) && targetsPage(g, input.url) && input.isTargeted(g)).slice(0, 3).map((g) => ({ id: g.id, key: g.key, name: g.name, kind: g.kind }));
|
|
869
|
+
let resumeGuide = null;
|
|
870
|
+
let resumeAt = 0;
|
|
871
|
+
for (const g of guides) {
|
|
872
|
+
const s = state.guides[g.id];
|
|
873
|
+
if (g.kind !== "flow" || !s?.lastStepId || s.completedAt || g.config.steps.length < 2) continue;
|
|
874
|
+
const at = Date.parse(s.lastShownAt ?? "");
|
|
875
|
+
if (!Number.isFinite(at) || now - at > RESUME_WITHIN_MS || at <= resumeAt) continue;
|
|
876
|
+
const stepIndex = g.config.steps.findIndex((st) => st.id === s.lastStepId);
|
|
877
|
+
if (stepIndex < 1 || !isWithinSchedule(g)) continue;
|
|
878
|
+
resumeGuide = { id: g.id, name: g.name, stepId: s.lastStepId, stepIndex, stepCount: g.config.steps.length };
|
|
879
|
+
resumeAt = at;
|
|
880
|
+
}
|
|
881
|
+
let checklist = null;
|
|
882
|
+
for (const g of guides) {
|
|
883
|
+
const tasks = g.config.checklist?.tasks;
|
|
884
|
+
if (g.kind !== "checklist" || !tasks?.length) continue;
|
|
885
|
+
const p = state.checklists[g.id];
|
|
886
|
+
if (p?.completedAt || p?.dismissedAt || !input.isAudienceTargeted(g)) continue;
|
|
887
|
+
const completed = new Set(p?.completedTaskIds ?? []);
|
|
888
|
+
const doneCount = tasks.filter((t) => completed.has(t.id)).length;
|
|
889
|
+
if (doneCount >= tasks.length) continue;
|
|
890
|
+
checklist = { id: g.id, name: g.name, done: doneCount, total: tasks.length, nextTask: tasks.find((t) => !completed.has(t.id))?.title ?? null };
|
|
891
|
+
break;
|
|
892
|
+
}
|
|
893
|
+
const seen = new Set(input.newsSeen);
|
|
894
|
+
const news = guides.filter((g) => g.kind === "announcement" && g.config.steps.length > 0 && now - Date.parse(g.publishedAt) <= NEWS_WITHIN_MS && input.isAudienceTargeted(g)).sort((a, b) => b.publishedAt.localeCompare(a.publishedAt)).slice(0, MAX_NEWS).map((g) => {
|
|
895
|
+
const step = g.config.steps[0];
|
|
896
|
+
return {
|
|
897
|
+
id: g.id,
|
|
898
|
+
key: g.key,
|
|
899
|
+
title: step.title || g.name,
|
|
900
|
+
body: plainText(step.body ?? ""),
|
|
901
|
+
imageUrl: step.imageUrl || null,
|
|
902
|
+
publishedAt: g.publishedAt,
|
|
903
|
+
// Read once opened in the News tab, or once the announcement itself was shown.
|
|
904
|
+
unread: !seen.has(g.id) && !(state.guides[g.id]?.impressions ?? 0)
|
|
905
|
+
};
|
|
906
|
+
});
|
|
907
|
+
return { firstName: firstNameFromTraits(input.traits), path: pathOf(input.url), pageGuides, resumeGuide, checklist, news };
|
|
908
|
+
}
|
|
909
|
+
|
|
777
910
|
// src/navigation.ts
|
|
778
911
|
function watchNavigation(listener, debounceMs = 50) {
|
|
779
912
|
if (typeof window === "undefined") return () => {
|
|
@@ -879,7 +1012,7 @@ var loadRecorder = async () => (await import("./recorder.js")).recorder;
|
|
|
879
1012
|
|
|
880
1013
|
// src/client.ts
|
|
881
1014
|
var SDK_NAME = "@guidelytech/sdk-core";
|
|
882
|
-
var SDK_VERSION = "0.
|
|
1015
|
+
var SDK_VERSION = "0.6.1";
|
|
883
1016
|
var DEFAULT_API_URL = "https://api.guidely.app";
|
|
884
1017
|
var PAGE_SCOPED = /* @__PURE__ */ new Set(["page_load", "url_match", "element_click", "element_visible", "delay"]);
|
|
885
1018
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -930,6 +1063,9 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
930
1063
|
__publicField(this, "firstVisit");
|
|
931
1064
|
__publicField(this, "lastUrl");
|
|
932
1065
|
__publicField(this, "authoring", null);
|
|
1066
|
+
__publicField(this, "capture", null);
|
|
1067
|
+
/** A capture token was found and the (lazily loaded) capture bridge is starting. */
|
|
1068
|
+
__publicField(this, "captureStarting", false);
|
|
933
1069
|
__publicField(this, "lastAudiences", /* @__PURE__ */ new Set());
|
|
934
1070
|
/** Set once the app calls identify()/reset(); bootstrap then leaves identity alone instead of re-identifying. */
|
|
935
1071
|
__publicField(this, "identitySettled", false);
|
|
@@ -1038,10 +1174,11 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1038
1174
|
this.isReady = true;
|
|
1039
1175
|
if (this.config) this.emitter.emit("ready", this.config);
|
|
1040
1176
|
this.resolveReady?.();
|
|
1177
|
+
const capturing = !this.options.disableAuthoring && this.maybeStartCapture();
|
|
1041
1178
|
this.renderPersistentWidgets();
|
|
1042
1179
|
this.resumeActiveGuide();
|
|
1043
1180
|
this.evaluate("ready");
|
|
1044
|
-
if (!this.options.disableAuthoring) this.maybeStartAuthoring();
|
|
1181
|
+
if (!this.options.disableAuthoring && !capturing) this.maybeStartAuthoring();
|
|
1045
1182
|
this.pageTracker.start(location.href);
|
|
1046
1183
|
this.touchSession();
|
|
1047
1184
|
this.syncCapture();
|
|
@@ -1139,7 +1276,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1139
1276
|
const refresh = setInterval(() => void this.refresh(), this.options.configRefreshMs);
|
|
1140
1277
|
this.cleanups.push(() => clearInterval(refresh));
|
|
1141
1278
|
const poll = setInterval(() => {
|
|
1142
|
-
if (!this.isReady || !this.config || this.
|
|
1279
|
+
if (!this.isReady || !this.config || this.editing() || doc.visibilityState === "hidden") return;
|
|
1143
1280
|
const url = location.href;
|
|
1144
1281
|
const ctx = this.getContext();
|
|
1145
1282
|
for (const guide of this.config.guides) {
|
|
@@ -1197,6 +1334,8 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1197
1334
|
this.cleanups.splice(0).forEach((fn) => fn());
|
|
1198
1335
|
this.authoring?.stop();
|
|
1199
1336
|
this.authoring = null;
|
|
1337
|
+
this.capture?.destroy();
|
|
1338
|
+
this.capture = null;
|
|
1200
1339
|
this.replay?.destroy();
|
|
1201
1340
|
this.replay = null;
|
|
1202
1341
|
this.queue.flushSync();
|
|
@@ -1229,6 +1368,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1229
1368
|
for (const [id, s] of Object.entries(data.guideState || {})) {
|
|
1230
1369
|
const local = guides[id];
|
|
1231
1370
|
guides[id] = {
|
|
1371
|
+
...local,
|
|
1232
1372
|
impressions: Math.max(local?.impressions ?? 0, s.impressions ?? 0),
|
|
1233
1373
|
completedAt: local?.completedAt ?? s.completedAt ?? null,
|
|
1234
1374
|
dismissedAt: local?.dismissedAt ?? s.dismissedAt ?? null,
|
|
@@ -1257,6 +1397,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1257
1397
|
const g = this.findGuide(id);
|
|
1258
1398
|
if (w.checklist && g) w.checklist.setProgress(this.getChecklistProgress(g.id));
|
|
1259
1399
|
});
|
|
1400
|
+
this.messenger?.refresh();
|
|
1260
1401
|
}
|
|
1261
1402
|
/** Sets or replaces the account (company/organisation) for the current user. */
|
|
1262
1403
|
async setAccount(account) {
|
|
@@ -1378,16 +1519,17 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1378
1519
|
const st = this.active.controller.getState();
|
|
1379
1520
|
if (st.status === "running" && !this.active.manual) {
|
|
1380
1521
|
const g = this.active.guide;
|
|
1381
|
-
if (g.config.urlPatterns.length && !g.config.urlPatterns.some((p) =>
|
|
1522
|
+
if (g.config.urlPatterns.length && !g.config.urlPatterns.some((p) => urlMatches2(p, url))) this.active.controller.destroy();
|
|
1382
1523
|
}
|
|
1383
1524
|
}
|
|
1384
1525
|
this.widgets.forEach((w, id) => {
|
|
1385
1526
|
if (!w.controller || w.manual) return;
|
|
1386
1527
|
const g = this.findGuide(id);
|
|
1387
|
-
if (g?.config.urlPatterns.length && !g.config.urlPatterns.some((p) =>
|
|
1528
|
+
if (g?.config.urlPatterns.length && !g.config.urlPatterns.some((p) => urlMatches2(p, url))) w.controller.destroy();
|
|
1388
1529
|
});
|
|
1389
1530
|
this.firedDelays.clear();
|
|
1390
1531
|
if (this.isReady) this.evaluate("navigation");
|
|
1532
|
+
if (changed) this.messenger?.refresh();
|
|
1391
1533
|
if (changed) void this.queue.flush();
|
|
1392
1534
|
}
|
|
1393
1535
|
handleDocumentClick(e) {
|
|
@@ -1418,9 +1560,13 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1418
1560
|
if (this.replay?.activeSessionId) this.replay.stop();
|
|
1419
1561
|
this.syncReplay();
|
|
1420
1562
|
}
|
|
1421
|
-
/** Autocapture, error tracking and replay need analytics on, consent granted and no authoring session. */
|
|
1563
|
+
/** Autocapture, error tracking and replay need analytics on, consent granted and no authoring or how-to capture session. */
|
|
1422
1564
|
captureAllowed() {
|
|
1423
|
-
return !this.destroyed && this.analyticsEnabled() && !this.
|
|
1565
|
+
return !this.destroyed && this.analyticsEnabled() && !this.editing();
|
|
1566
|
+
}
|
|
1567
|
+
/** Authoring or how-to capture is running: no guides, widgets, replay or autocapture. */
|
|
1568
|
+
editing() {
|
|
1569
|
+
return !!this.authoring?.isActive() || this.captureStarting || !!this.capture?.isActive();
|
|
1424
1570
|
}
|
|
1425
1571
|
/** A capability is on when the project enables it (absent on old servers → off) and no option turns it off. */
|
|
1426
1572
|
autocaptureOn(key) {
|
|
@@ -1430,7 +1576,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1430
1576
|
/** Page views on SPA navigation predate autocapture: only an explicit opt-out stops them. */
|
|
1431
1577
|
pageviewsOn() {
|
|
1432
1578
|
const o = this.options.autocapture;
|
|
1433
|
-
return this.config?.settings?.autocapture?.pageviews !== false && o !== false && !(typeof o === "object" && o.pageviews === false) && !this.
|
|
1579
|
+
return this.config?.settings?.autocapture?.pageviews !== false && o !== false && !(typeof o === "object" && o.pageviews === false) && !this.editing();
|
|
1434
1580
|
}
|
|
1435
1581
|
trackPageView(url) {
|
|
1436
1582
|
if (url === this.pageViewUrl || !this.pageviewsOn()) return;
|
|
@@ -1440,7 +1586,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1440
1586
|
const device = detectDevice(navigator.userAgent, window.innerWidth);
|
|
1441
1587
|
const props = { deviceType: device.type, browser: device.browser, os: device.os, screenWidth: window.screen?.width, viewportWidth: window.innerWidth };
|
|
1442
1588
|
if (collect) {
|
|
1443
|
-
props.path =
|
|
1589
|
+
props.path = pathOf2(url);
|
|
1444
1590
|
props.title = (document.title || "").slice(0, 200);
|
|
1445
1591
|
const ref = !this.session.pageViews ? externalReferrer() : void 0;
|
|
1446
1592
|
if (ref) props.referrer = ref;
|
|
@@ -1457,7 +1603,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1457
1603
|
if (!this.pageLeft && this.autocaptureOn("pageLeave")) {
|
|
1458
1604
|
const info = t.leave();
|
|
1459
1605
|
const collect = this.collectUrls();
|
|
1460
|
-
this.enqueue("page_leave", { url: collect ? info.url : void 0, properties: { ...collect ? { path:
|
|
1606
|
+
this.enqueue("page_leave", { url: collect ? info.url : void 0, properties: { ...collect ? { path: pathOf2(info.url) } : {}, durationMs: info.durationMs, scrollDepth: info.scrollDepth } });
|
|
1461
1607
|
}
|
|
1462
1608
|
if (nextUrl !== null) {
|
|
1463
1609
|
t.start(nextUrl);
|
|
@@ -1494,7 +1640,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1494
1640
|
if (!batch || !this.captureAllowed()) return;
|
|
1495
1641
|
const collect = this.collectUrls();
|
|
1496
1642
|
const de = document.documentElement;
|
|
1497
|
-
const properties = { path: collect ?
|
|
1643
|
+
const properties = { path: collect ? pathOf2(batch.url) : void 0, viewportWidth: window.innerWidth, viewportHeight: window.innerHeight, pageHeight: Math.max(de.scrollHeight, document.body?.scrollHeight || 0), points: batch.points };
|
|
1498
1644
|
if (!collect) delete properties.path;
|
|
1499
1645
|
this.enqueue("heatmap", { url: collect ? batch.url : void 0, properties });
|
|
1500
1646
|
}
|
|
@@ -1668,8 +1814,8 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1668
1814
|
}
|
|
1669
1815
|
isTargeted(guide, ctx, url) {
|
|
1670
1816
|
const c = guide.config;
|
|
1671
|
-
if (c.urlPatterns.length && !c.urlPatterns.some((p) =>
|
|
1672
|
-
if (c.trigger.urlPattern && !
|
|
1817
|
+
if (c.urlPatterns.length && !c.urlPatterns.some((p) => urlMatches2(p, url))) return false;
|
|
1818
|
+
if (c.trigger.urlPattern && !urlMatches2(c.trigger.urlPattern, url)) return false;
|
|
1673
1819
|
if (!isWithinSchedule(guide)) return false;
|
|
1674
1820
|
if (guide.kind === "survey" && (this.state.surveysSubmitted.includes(guide.id) || this.state.surveysSubmitted.includes(guide.key))) return false;
|
|
1675
1821
|
ctx.rolloutSalt = guide.id;
|
|
@@ -1691,7 +1837,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1691
1837
|
if (t.delayMs > 0) return reason === "timer" && detail.guideId === guide.id;
|
|
1692
1838
|
return reason === "ready" || reason === "navigation" || reason === "identify";
|
|
1693
1839
|
case "url_match":
|
|
1694
|
-
return (reason === "ready" || reason === "navigation" || reason === "identify") &&
|
|
1840
|
+
return (reason === "ready" || reason === "navigation" || reason === "identify") && urlMatches2(t.value, location.href);
|
|
1695
1841
|
case "event":
|
|
1696
1842
|
return reason === "event" && !!t.value && detail.eventName === t.value;
|
|
1697
1843
|
case "element_click":
|
|
@@ -1715,7 +1861,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1715
1861
|
}
|
|
1716
1862
|
}
|
|
1717
1863
|
evaluate(reason, detail = {}) {
|
|
1718
|
-
if (!this.config || this.destroyed || this.
|
|
1864
|
+
if (!this.config || this.destroyed || this.editing()) return;
|
|
1719
1865
|
const url = location.href;
|
|
1720
1866
|
const ctx = this.getContext();
|
|
1721
1867
|
if (reason === "identify") {
|
|
@@ -1860,7 +2006,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1860
2006
|
}
|
|
1861
2007
|
}
|
|
1862
2008
|
showFlow(guide, manual, stepId) {
|
|
1863
|
-
if (this.
|
|
2009
|
+
if (this.editing()) return;
|
|
1864
2010
|
const exclusive = isExclusiveGuide(guide);
|
|
1865
2011
|
const entry = { guide, manual, startUrl: location.href };
|
|
1866
2012
|
const controller = runGuide2(guide, {
|
|
@@ -1878,6 +2024,10 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1878
2024
|
}
|
|
1879
2025
|
if (e.name === "step_viewed" && step) {
|
|
1880
2026
|
if (exclusive) writeJson(this.sessionStore, KEYS.active, { guideId: guide.id, stepId: step.id, manual });
|
|
2027
|
+
if (guide.kind === "flow" && guide.config.steps.length > 1) {
|
|
2028
|
+
this.guideState(guide.id).lastStepId = step.id;
|
|
2029
|
+
this.persistState();
|
|
2030
|
+
}
|
|
1881
2031
|
this.emitter.emit("guide:step", { guide, stepId: step.id, stepIndex: e.stepIndex ?? 0 });
|
|
1882
2032
|
}
|
|
1883
2033
|
if (e.name === "guide_completed") {
|
|
@@ -1917,7 +2067,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1917
2067
|
}
|
|
1918
2068
|
}
|
|
1919
2069
|
showChecklist(guide) {
|
|
1920
|
-
if (this.
|
|
2070
|
+
if (this.editing()) return;
|
|
1921
2071
|
const config = guide.config.checklist;
|
|
1922
2072
|
if (!config || this.widgets.has(guide.id)) return;
|
|
1923
2073
|
const scope = createScope(this.theme(), guide.config.appearance);
|
|
@@ -1939,7 +2089,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1939
2089
|
if (first) this.recordImpression(guide);
|
|
1940
2090
|
}
|
|
1941
2091
|
showSurvey(guide, manual) {
|
|
1942
|
-
if (this.
|
|
2092
|
+
if (this.editing()) return;
|
|
1943
2093
|
const config = guide.config.survey;
|
|
1944
2094
|
if (!config) return;
|
|
1945
2095
|
const scope = createScope(this.theme(), guide.config.appearance);
|
|
@@ -1990,7 +2140,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1990
2140
|
}
|
|
1991
2141
|
}
|
|
1992
2142
|
renderPersistentWidgets() {
|
|
1993
|
-
if (!this.config) return;
|
|
2143
|
+
if (!this.config || this.captureStarting || this.capture?.isActive()) return;
|
|
1994
2144
|
const rc = this.config.resourceCenter;
|
|
1995
2145
|
if (this.config.messenger?.enabled) {
|
|
1996
2146
|
if (this.resourceCenter) {
|
|
@@ -2010,6 +2160,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2010
2160
|
config: rc,
|
|
2011
2161
|
guides: this.config.guides.map((g) => ({ id: g.id, key: g.key, name: g.name, kind: g.kind })),
|
|
2012
2162
|
search: (q) => this.searchResourceCenter(q),
|
|
2163
|
+
audienceIds: () => this.state.audienceIds,
|
|
2013
2164
|
loadArticle: (id) => this.loadArticle(id),
|
|
2014
2165
|
onStartGuide: (id) => this.startGuide(id),
|
|
2015
2166
|
onOpenChecklist: (id) => this.openChecklist(id),
|
|
@@ -2053,7 +2204,15 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2053
2204
|
userId: () => this.user?.id || null,
|
|
2054
2205
|
pageUrl: () => this.collectUrls() ? location.href : void 0
|
|
2055
2206
|
}),
|
|
2207
|
+
visitor: () => this.messengerVisitor(),
|
|
2208
|
+
brand: { logoUrl: this.theme().logoUrl ?? null, name: this.theme().brandName ?? null },
|
|
2209
|
+
onResumeGuide: (guideId, stepId) => this.startGuide(guideId, { stepId }),
|
|
2210
|
+
onNewsSeen: (ids) => {
|
|
2211
|
+
const seen = readJson(this.store, KEYS.newsSeen(this.options.projectId), []);
|
|
2212
|
+
writeJson(this.store, KEYS.newsSeen(this.options.projectId), Array.from(/* @__PURE__ */ new Set([...ids, ...Array.isArray(seen) ? seen : []])).slice(0, 200));
|
|
2213
|
+
},
|
|
2056
2214
|
resourceCenter: this.config.resourceCenter,
|
|
2215
|
+
audienceIds: () => this.state.audienceIds,
|
|
2057
2216
|
guides: this.config.guides.map((g) => ({ id: g.id, key: g.key, name: g.name, kind: g.kind })),
|
|
2058
2217
|
onStartGuide: (idOrKey) => this.startGuide(idOrKey),
|
|
2059
2218
|
onOpenChecklist: (id) => this.openChecklist(id),
|
|
@@ -2073,6 +2232,25 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2073
2232
|
});
|
|
2074
2233
|
return this.messengerLoading;
|
|
2075
2234
|
}
|
|
2235
|
+
/** What the messenger personalises from, computed fresh on every render. */
|
|
2236
|
+
messengerVisitor() {
|
|
2237
|
+
const ctx = this.getContext();
|
|
2238
|
+
const url = location.href;
|
|
2239
|
+
const seen = readJson(this.store, KEYS.newsSeen(this.options.projectId), []);
|
|
2240
|
+
return buildMessengerVisitor({
|
|
2241
|
+
guides: this.config?.guides ?? [],
|
|
2242
|
+
state: this.state,
|
|
2243
|
+
traits: this.user?.traits,
|
|
2244
|
+
url,
|
|
2245
|
+
isTargeted: (g) => this.isTargeted(g, ctx, url),
|
|
2246
|
+
isAudienceTargeted: (g) => {
|
|
2247
|
+
if (!isWithinSchedule(g)) return false;
|
|
2248
|
+
ctx.rolloutSalt = g.id;
|
|
2249
|
+
return evaluateRules(g.config.targeting, ctx);
|
|
2250
|
+
},
|
|
2251
|
+
newsSeen: Array.isArray(seen) ? seen : []
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2076
2254
|
destroyMessenger() {
|
|
2077
2255
|
this.messenger?.destroy();
|
|
2078
2256
|
this.messengerScope?.destroy();
|
|
@@ -2142,7 +2320,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2142
2320
|
if (task.completion !== kind || !task.completionValue) continue;
|
|
2143
2321
|
let hit = false;
|
|
2144
2322
|
if (kind === "event") hit = value === task.completionValue;
|
|
2145
|
-
else if (kind === "url_visited") hit =
|
|
2323
|
+
else if (kind === "url_visited") hit = urlMatches2(task.completionValue, value);
|
|
2146
2324
|
else if (kind === "guide_completed") hit = value.id === task.completionValue || value.key === task.completionValue;
|
|
2147
2325
|
else if (kind === "click") {
|
|
2148
2326
|
try {
|
|
@@ -2286,7 +2464,8 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2286
2464
|
audienceIds: this.state.audienceIds,
|
|
2287
2465
|
session: this.session,
|
|
2288
2466
|
pendingEvents: this.queue.size,
|
|
2289
|
-
authoring: !!this.authoring?.isActive()
|
|
2467
|
+
authoring: !!this.authoring?.isActive(),
|
|
2468
|
+
capture: !!this.capture?.isActive()
|
|
2290
2469
|
};
|
|
2291
2470
|
}
|
|
2292
2471
|
// ---------------------------------------------------------------------------
|
|
@@ -2301,22 +2480,78 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2301
2480
|
ctx: () => this.getContext(),
|
|
2302
2481
|
maskSelectors: [...this.options.maskSelectors || [], ...this.config?.settings?.maskSelectors || []],
|
|
2303
2482
|
onError: (err) => this.reportError(err),
|
|
2304
|
-
onActive: (active) =>
|
|
2305
|
-
this.emitter.emit("authoring", { active });
|
|
2306
|
-
if (active) this.replay?.stop({ discard: true });
|
|
2307
|
-
else this.syncCapture();
|
|
2308
|
-
if (active) {
|
|
2309
|
-
this.stopGuide();
|
|
2310
|
-
this.widgets.forEach((w) => w.destroy());
|
|
2311
|
-
this.widgets.clear();
|
|
2312
|
-
} else this.evaluate("ready");
|
|
2313
|
-
}
|
|
2483
|
+
onActive: (active) => this.setEditing("authoring", active)
|
|
2314
2484
|
});
|
|
2315
2485
|
if (bridge.detect()) this.authoring = bridge;
|
|
2316
2486
|
} catch (err) {
|
|
2317
2487
|
this.reportError(err);
|
|
2318
2488
|
}
|
|
2319
2489
|
}
|
|
2490
|
+
/** Authoring or capture started / ended: pause everything a visitor would see or be recorded by. */
|
|
2491
|
+
setEditing(kind, active) {
|
|
2492
|
+
this.emitter.emit(kind, { active });
|
|
2493
|
+
if (active) {
|
|
2494
|
+
this.replay?.stop({ discard: true });
|
|
2495
|
+
this.stopGuide();
|
|
2496
|
+
this.widgets.forEach((w) => w.destroy());
|
|
2497
|
+
this.widgets.clear();
|
|
2498
|
+
if (kind === "capture") this.hidePersistentWidgets();
|
|
2499
|
+
} else {
|
|
2500
|
+
this.syncCapture();
|
|
2501
|
+
this.renderPersistentWidgets();
|
|
2502
|
+
this.evaluate("ready");
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
hidePersistentWidgets() {
|
|
2506
|
+
this.resourceCenter?.destroy();
|
|
2507
|
+
this.resourceCenterScope?.destroy();
|
|
2508
|
+
this.resourceCenter = null;
|
|
2509
|
+
this.resourceCenterScope = null;
|
|
2510
|
+
this.destroyMessenger();
|
|
2511
|
+
}
|
|
2512
|
+
// ---------------------------------------------------------------------------
|
|
2513
|
+
// How-to capture
|
|
2514
|
+
// ---------------------------------------------------------------------------
|
|
2515
|
+
/**
|
|
2516
|
+
* Starts how-to capture when the URL carries `guidely_capture` (or this tab is mid-recording).
|
|
2517
|
+
* The bridge, toolbar and screenshot code are a separate chunk loaded only then.
|
|
2518
|
+
*/
|
|
2519
|
+
maybeStartCapture() {
|
|
2520
|
+
let marker = null;
|
|
2521
|
+
try {
|
|
2522
|
+
marker = takeCaptureToken(this.sessionStore);
|
|
2523
|
+
} catch (err) {
|
|
2524
|
+
this.reportError(err);
|
|
2525
|
+
}
|
|
2526
|
+
if (!marker) return false;
|
|
2527
|
+
const found = marker;
|
|
2528
|
+
this.captureStarting = true;
|
|
2529
|
+
this.replay?.stop({ discard: true });
|
|
2530
|
+
loadCapture().then(async (capture) => {
|
|
2531
|
+
if (this.destroyed) return;
|
|
2532
|
+
const bridge = capture.createBridge({
|
|
2533
|
+
api: this.api,
|
|
2534
|
+
sessionStore: this.sessionStore,
|
|
2535
|
+
marker: found,
|
|
2536
|
+
maskSelector: this.maskSelector(),
|
|
2537
|
+
onError: (err) => this.reportError(err),
|
|
2538
|
+
onActive: (active) => {
|
|
2539
|
+
this.captureStarting = false;
|
|
2540
|
+
this.setEditing("capture", active);
|
|
2541
|
+
}
|
|
2542
|
+
});
|
|
2543
|
+
this.capture = bridge;
|
|
2544
|
+
await bridge.start();
|
|
2545
|
+
}).catch((err) => this.reportError(err)).finally(() => {
|
|
2546
|
+
if (!this.captureStarting) return;
|
|
2547
|
+
this.captureStarting = false;
|
|
2548
|
+
if (this.destroyed) return;
|
|
2549
|
+
this.syncCapture();
|
|
2550
|
+
this.renderPersistentWidgets();
|
|
2551
|
+
this.evaluate("ready");
|
|
2552
|
+
});
|
|
2553
|
+
return true;
|
|
2554
|
+
}
|
|
2320
2555
|
// ---------------------------------------------------------------------------
|
|
2321
2556
|
// Internals
|
|
2322
2557
|
// ---------------------------------------------------------------------------
|
|
@@ -2358,7 +2593,7 @@ var current = null;
|
|
|
2358
2593
|
function webOnly(config) {
|
|
2359
2594
|
return config.guides.every((g) => supportsPlatform(g.config, "web")) ? config : { ...config, guides: config.guides.filter((g) => supportsPlatform(g.config, "web")) };
|
|
2360
2595
|
}
|
|
2361
|
-
function
|
|
2596
|
+
function pathOf2(url) {
|
|
2362
2597
|
try {
|
|
2363
2598
|
return new URL(url, "http://localhost").pathname;
|
|
2364
2599
|
} catch {
|
|
@@ -2391,6 +2626,7 @@ var Guidely = {
|
|
|
2391
2626
|
var index_default = Guidely;
|
|
2392
2627
|
export {
|
|
2393
2628
|
AUTHORING_PARAM,
|
|
2629
|
+
CAPTURE_PARAM,
|
|
2394
2630
|
DEFAULT_API_URL,
|
|
2395
2631
|
EventQueue,
|
|
2396
2632
|
Guidely,
|