Skip to content

Commit 287e95e

Browse files
committed
Cleanup.
1 parent 382cbca commit 287e95e

File tree

6 files changed

+179
-124
lines changed

6 files changed

+179
-124
lines changed

build/cjs/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/esm/index.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

published/14.2.0/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

published/latest/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/GleapCopilotTours.js

Lines changed: 145 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ const styleId = "copilot-tour-styles";
44
const copilotInfoContainerId = "copilot-info-container";
55

66
function estimateReadTime(text) {
7-
const wordsPerSecond = 3.5; // Average reading speed
7+
const wordsPerSecond = 3.6; // Average reading speed
88
const wordCount = text.split(/\s+/).filter((word) => word.length > 0).length;
99
const readTimeInSeconds = Math.ceil(wordCount / wordsPerSecond);
10-
return readTimeInSeconds + 1.5;
10+
return readTimeInSeconds + 1;
1111
}
1212

1313
function htmlToPlainText(html) {
@@ -26,6 +26,26 @@ function scrollToElement(element) {
2626
}
2727
}
2828

29+
function performClickAnimation(posX, posY) {
30+
// Create a new div element to act as the wave
31+
const wave = document.createElement("div");
32+
33+
// Apply the CSS class for styling
34+
wave.className = "click-wave";
35+
36+
// Set the position dynamically
37+
wave.style.left = `${posX - 17}px`;
38+
wave.style.top = `${posY - 17}px`;
39+
40+
// Append the wave to the body
41+
document.body.appendChild(wave);
42+
43+
// Remove the wave after the animation ends
44+
setTimeout(() => {
45+
wave.remove();
46+
}, 800);
47+
}
48+
2949
function waitForElement(selector, timeout = 5000) {
3050
const pollInterval = 100;
3151
const maxAttempts = timeout / pollInterval;
@@ -62,6 +82,7 @@ export default class GleapCopilotTours {
6282
currentActiveIndex = undefined;
6383
lastArrowPositionX = undefined;
6484
lastArrowPositionY = undefined;
85+
onCompleteCallback = undefined;
6586

6687
// GleapReplayRecorder singleton
6788
static instance;
@@ -102,7 +123,7 @@ export default class GleapCopilotTours {
102123
});
103124
}
104125

105-
startWithConfig(tourId, config, delay = 0) {
126+
startWithConfig(tourId, config, onCompleteCallback = undefined) {
106127
// Prevent multiple tours from being started.
107128
if (this.productTourId) {
108129
return;
@@ -111,27 +132,8 @@ export default class GleapCopilotTours {
111132
this.productTourId = tourId;
112133
this.productTourData = config;
113134
this.currentActiveIndex = 0;
114-
115-
const self = this;
116-
117-
if (delay > 0) {
118-
return setTimeout(() => {
119-
self.start();
120-
}, delay);
121-
} else {
122-
return this.start();
123-
}
124-
}
125-
126-
loadUncompletedTour() {
127-
try {
128-
const data = JSON.parse(localStorage.getItem(localStorageKey));
129-
if (data?.tourData && data?.tourId) {
130-
return data;
131-
}
132-
} catch (e) {}
133-
134-
return null;
135+
this.onCompleteCallback = onCompleteCallback;
136+
this.start();
135137
}
136138

137139
storeUncompletedTour() {
@@ -154,14 +156,8 @@ export default class GleapCopilotTours {
154156
localStorage.setItem(localStorageKey, JSON.stringify(data));
155157
} catch (e) {}
156158
} else {
157-
this.clearUncompletedTour();
158-
}
159-
}
160-
161-
clearUncompletedTour() {
162-
try {
163159
localStorage.removeItem(localStorageKey);
164-
} catch (e) {}
160+
}
165161
}
166162

167163
updatePointerPosition(anchor) {
@@ -199,7 +195,7 @@ export default class GleapCopilotTours {
199195
let anchorCenterY =
200196
anchorRect.top + anchorRect.height / 2 + window.scrollY;
201197

202-
let containerWidthSpace = 330;
198+
let containerWidthSpace = 350;
203199
if (containerWidthSpace > window.innerWidth - 40) {
204200
containerWidthSpace = window.innerWidth - 40;
205201
}
@@ -230,29 +226,24 @@ export default class GleapCopilotTours {
230226
}
231227

232228
cleanup() {
233-
this.removePointerUI();
234-
this.clearUncompletedTour();
235-
}
236-
237-
removePointerUI() {
238229
const container = document.getElementById(pointerContainerId);
239230
if (container) {
240231
container.remove();
241232
}
242233

243-
// Remove style node.
244-
const styleNode = document.getElementById(styleId);
245-
if (styleNode) {
246-
styleNode.remove();
247-
}
248-
249-
// Remove copilot info container.
250234
const copilotInfoContainer = document.getElementById(
251235
copilotInfoContainerId
252236
);
253237
if (copilotInfoContainer) {
254238
copilotInfoContainer.remove();
255239
}
240+
241+
setTimeout(() => {
242+
const styleNode = document.getElementById(styleId);
243+
if (styleNode) {
244+
styleNode.remove();
245+
}
246+
}, 1000);
256247
}
257248

258249
setupCopilotTour() {
@@ -279,7 +270,7 @@ export default class GleapCopilotTours {
279270
height: auto;
280271
fill: none;
281272
}
282-
273+
283274
.${pointerContainerId}-right {
284275
left: auto;
285276
right: 0;
@@ -307,7 +298,57 @@ export default class GleapCopilotTours {
307298
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
308299
}
309300
310-
body::before {
301+
.copilot-info-container {
302+
position: fixed;
303+
top: 20px;
304+
right: 20px;
305+
z-index: 2147483612;
306+
background: #fff;
307+
padding: 5px;
308+
padding-left: 10px;
309+
border-radius: 10px;
310+
box-shadow: 0 0 20px 0 #e721b263;
311+
font-family: sans-serif;
312+
font-size: 13px;
313+
color: #000;
314+
display: flex;
315+
align-items: center;
316+
gap: 10px;
317+
border: 1px solid #e721b3;
318+
max-width: min(330px, 100vw - 40px);
319+
}
320+
321+
.copilot-info-container svg {
322+
width: 24px;
323+
height: 24px;
324+
flex-shrink: 0;
325+
}
326+
327+
.click-wave {
328+
position: absolute;
329+
width: 34px;
330+
height: 34px;
331+
border-radius: 50%;
332+
background-color: rgba(0, 0, 0, 0.5);
333+
pointer-events: none;
334+
z-index: 2147483611;
335+
animation: click-wave-animation 0.8s ease forwards;
336+
}
337+
338+
@keyframes click-wave-animation {
339+
0% {
340+
transform: scale(0.2);
341+
opacity: 1;
342+
}
343+
100% {
344+
transform: scale(2);
345+
opacity: 0;
346+
}
347+
}
348+
349+
${
350+
this.productTourData.gradient
351+
? `body::before {
311352
content: "";
312353
position: fixed;
313354
top: 0;
@@ -317,11 +358,10 @@ export default class GleapCopilotTours {
317358
pointer-events: all;
318359
z-index: 2147483610;
319360
box-sizing: border-box;
320-
border: 8px solid transparent;
321-
filter: blur(20px);
361+
border: 20px solid transparent;
362+
filter: blur(28px);
322363
border-image-slice: 1;
323-
border-image-source: linear-gradient(45deg, #2142e7, #e721b3);
324-
animation: animateBorder 3s infinite alternate ease-in-out;
364+
border-image-source: linear-gradient(45deg, #ED5587, #FBE6A9, #a6e3f8, #C294F2);
325365
}
326366
327367
body::after {
@@ -337,46 +377,9 @@ export default class GleapCopilotTours {
337377
box-sizing: border-box;
338378
border: 2px solid transparent;
339379
border-image-slice: 1;
340-
border-image-source: linear-gradient(45deg, #2142e7, #e721b3);
341-
animation: animateBorder 3s infinite alternate ease-in-out;
342-
}
343-
344-
@keyframes animateBorder {
345-
0% {
346-
border-image-source: linear-gradient(45deg, #2142e7, #e721b3);
347-
}
348-
50% {
349-
border-image-source: linear-gradient(135deg, #e721b3, #ff8a00);
350-
}
351-
100% {
352-
border-image-source: linear-gradient(225deg, #ff8a00, #2142e7);
353-
}
354-
}
355-
356-
.copilot-info-container {
357-
position: fixed;
358-
top: 20px;
359-
right: 20px;
360-
z-index: 2147483610;
361-
background: #fff;
362-
padding: 5px;
363-
padding-left: 10px;
364-
border-radius: 10px;
365-
box-shadow: 0 0 20px 0 #e721b263;
366-
font-family: sans-serif;
367-
font-size: 13px;
368-
color: #000;
369-
display: flex;
370-
align-items: center;
371-
gap: 10px;
372-
border: 1px solid #e721b3;
373-
max-width: min(330px, 100vw - 40px);
374-
}
375-
376-
.copilot-info-container svg {
377-
width: 24px;
378-
height: 24px;
379-
flex-shrink: 0;
380+
border-image-source: linear-gradient(45deg, #ED5587, #FBE6A9, #a6e3f8, #C294F2);
381+
}`
382+
: ""
380383
}
381384
`;
382385
document.head.appendChild(styleNode);
@@ -416,6 +419,8 @@ export default class GleapCopilotTours {
416419
// Setup the copilot tour.
417420
this.setupCopilotTour();
418421

422+
// Show copilot joined info.
423+
419424
// Render the first step.
420425
this.renderNextStep();
421426
}
@@ -427,12 +432,37 @@ export default class GleapCopilotTours {
427432
// Check if we have reached the end of the tour.
428433
if (this.currentActiveIndex >= steps.length) {
429434
this.cleanup();
435+
if (this.onCompleteCallback) {
436+
this.onCompleteCallback();
437+
}
430438
return;
431439
}
432440

433441
const currentStep = steps[this.currentActiveIndex];
434442

435443
const handleStep = (element) => {
444+
const gotToNextStep = () => {
445+
this.currentActiveIndex++;
446+
this.storeUncompletedTour();
447+
448+
if (currentStep.mode === "CLICK" && element) {
449+
const rect = element.getBoundingClientRect();
450+
451+
// Get current scroll position.
452+
const scrollX = window.scrollX || 0;
453+
const scrollY = window.scrollY || 0;
454+
455+
performClickAnimation(
456+
rect.left + rect.width / 2 + scrollX,
457+
rect.top + rect.height / 2 + scrollY
458+
);
459+
460+
element.click();
461+
}
462+
463+
this.renderNextStep();
464+
};
465+
436466
// Update pointer position, even if element is null.
437467
this.updatePointerPosition(element);
438468

@@ -447,21 +477,32 @@ export default class GleapCopilotTours {
447477
// Estimate read time in seconds.
448478
const readTime = estimateReadTime(message);
449479

450-
// Automatically move to the next step after the estimated read time.
451-
setTimeout(() => {
452-
this.currentActiveIndex++;
453-
this.storeUncompletedTour();
454-
455-
if (currentStep.mode === "CLICK" && element) {
456-
try {
457-
element.click();
458-
} catch (e) {
459-
console.error("Error clicking the element:", e);
460-
}
480+
console.log("Read time:", currentStep);
481+
482+
// Read the message.
483+
if (currentStep.voice && currentStep.voice.length > 0) {
484+
try {
485+
const audio = new Audio(currentStep.voice);
486+
487+
// Add an event listener for the 'ended' event
488+
audio.addEventListener("ended", () => {
489+
setTimeout(() => {
490+
gotToNextStep();
491+
}, 1000);
492+
});
493+
494+
// Play the audio
495+
audio.play();
496+
} catch (error) {
497+
setTimeout(() => {
498+
gotToNextStep();
499+
}, readTime * 1000);
461500
}
462-
463-
this.renderNextStep();
464-
}, readTime * 1000);
501+
} else {
502+
setTimeout(() => {
503+
gotToNextStep();
504+
}, readTime * 1000);
505+
}
465506
};
466507

467508
const elementPromise = currentStep.selector

0 commit comments

Comments
 (0)