Skip to content

Commit 4f3bb58

Browse files
authored
Merge pull request #163 from GleapSDK/claude/capture-requests
feat: capture requests - screenshots, screen recordings and logs on the web
2 parents 190e944 + 982adb8 commit 4f3bb58

28 files changed

Lines changed: 6445 additions & 15 deletions

‎CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
Capture requests ("show me the issue"): when a workflow, an AI agent or a teammate asks the customer for a screenshot or a screen recording in a conversation, the customer answers from the card in the widget and the SDK captures the page. On desktop Chrome and Edge the screenshot is taken from the tab itself after one "Share this tab" confirmation (exact pixels); everywhere else, and when sharing is declined, the page is rendered in the browser. Screen recordings use the browser's screen sharing (MP4 where supported, otherwise WebM with its duration written in so players can seek), with the microphone if the project allows it; while recording, everything a screenshot masks is blurred on the page. When screen sharing doesn't start, and on phones and tablets, the customer can record the page instead (a page recording the dashboard plays like a session replay). Password, one-time-code and card fields, the `gl-mask` / `gl-block` / `rr-mask` / `rr-block` classes, the `setReplayOptions` privacy options, payment frames and the project's masked elements are left out. The capture bar is a small dark pill the customer can drag; all its texts come from the widget's translations.
5+
Logs requests: workflows, AI agents and teammates can collect the page's console logs, network logs, custom data, environment data and events in the background, the same data a bug report carries. They arrive over the SDK's websocket and are uploaded once, gzipped.
6+
New: `Gleap.setCaptureEnabled(enabled)` (off: the widget only offers a file upload) and `Gleap.setRemoteLogCollectionEnabled(enabled)` (off: logs requests are answered as unsupported and captures are sent without logs).
7+
The classic screen recording now stops cleanly when the customer clicks the browser's "Stop sharing" and is no longer uploaded twice. The browser bundle grows by about 34 KB gzipped.
8+
39
## 19.0.1
410
Fixed: the SDK's injected styles made the host page slower. The product tour rule `.gleap-tour-popover-navigation-btns button + button` ends in a bare `button`, so Chrome restyled sibling elements across the whole page whenever a node was inserted anywhere, not only inside Gleap's UI (on a page with ~19k nodes, ~2,000 extra elements per popover open or close). The tour buttons are now spaced with `gap` on their flex container, which looks the same; when the Previous button is hidden, the Next button no longer keeps a stray 4px margin.
511

‎babel.config.js‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,19 @@ module.exports = {
44
],
55
plugins: [
66
["@babel/plugin-transform-class-properties"]
7+
],
8+
overrides: [
9+
{
10+
// The capture request modules only run where the rest of the bundle does: its untranspiled
11+
// dependencies (modern-screenshot, rrweb) already need ES2020 (?. and ??). Compiled for those
12+
// browsers they stay smaller (no ES5 classes, arrows, spread or destructuring helpers).
13+
test: /[\\/]src[\\/](GleapCapture(Api|Manager|Recorder|Screenshot|Settings|Tasks|UI|Veil)|GleapWebmDuration)\.js$/,
14+
presets: [
15+
[
16+
"@babel/env",
17+
{ targets: { chrome: "80", edge: "80", firefox: "74", safari: "13.1", ios: "13.4", samsung: "13" } }
18+
]
19+
]
20+
}
721
]
8-
};
22+
};

‎index.d.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,19 @@ export namespace Gleap {
409409
}[];
410410
errorHandler?: (error: Error) => void;
411411
}): void;
412+
/**
413+
* Enables or disables screenshots and screen recordings that teammates, AI agents or workflows
414+
* ask the customer for in the widget (default: enabled). While disabled, the widget offers
415+
* uploading a file instead. Can be called before initialize.
416+
*/
417+
function setCaptureEnabled(enabled: boolean): void;
418+
/**
419+
* Enables or disables sending logs when teammates, AI agents or workflows request them, on their
420+
* own or along with a screenshot or screen recording (default: enabled). Only data a bug report
421+
* carries is sent: console and network logs, custom data, environment data and events. Can be
422+
* called before initialize.
423+
*/
424+
function setRemoteLogCollectionEnabled(enabled: boolean): void;
412425
}
413426
export default Gleap;
414427

‎package-lock.json‎

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"@floating-ui/dom": "^1.7.5",
8787
"@rrweb/packer": "^2.1.0",
8888
"@rrweb/record": "^2.1.0",
89+
"modern-screenshot": "4.7.0",
8990
"unique-selector": "^0.5.0"
9091
}
9192
}

‎src/Gleap.js‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import GleapAgentToolManager from './GleapAgentToolManager';
77
import GleapAiChatbarManager from './GleapAiChatbarManager';
88
import GleapAudioManager from './GleapAudioManager';
99
import GleapBannerManager from './GleapBannerManager';
10+
import GleapCaptureManager from './GleapCaptureManager';
1011
import { registerGleapChecklist } from './GleapChecklist';
1112
import GleapClickListener from './GleapClickListener';
1213
import GleapConfigManager from './GleapConfigManager';
@@ -279,6 +280,11 @@ class Gleap {
279280
// Check for URL params.
280281
Gleap.checkForUrlParams();
281282

283+
// A capture request that was in progress before this page load.
284+
try {
285+
GleapCaptureManager.getInstance().resumeFromStorage();
286+
} catch (exp) {}
287+
282288
// Notify event.
283289
GleapEventManager.notifyEvent('initialized');
284290
});
@@ -354,6 +360,9 @@ class Gleap {
354360
* @returns
355361
*/
356362
static destroy() {
363+
try {
364+
GleapCaptureManager.getInstance().destroy();
365+
} catch (exp) {}
357366
GleapReplayRecorder.getInstance().stop();
358367
GleapStreamedEvent.getInstance().stop();
359368
GleapFrameManager.getInstance().destroy();
@@ -541,6 +550,27 @@ class Gleap {
541550
GleapReplayRecorder.getInstance().setOptions(options);
542551
}
543552

553+
/**
554+
* Enables or disables screenshots and screen recordings that teammates, AI agents or workflows
555+
* ask the customer for in the widget (default: enabled). While disabled, the widget offers
556+
* uploading a file instead. Can be called before initialize.
557+
* @param {boolean} enabled
558+
*/
559+
static setCaptureEnabled(enabled) {
560+
GleapCaptureManager.getInstance().setCaptureEnabled(enabled);
561+
}
562+
563+
/**
564+
* Enables or disables sending logs when teammates, AI agents or workflows request them, on their
565+
* own or along with a screenshot or screen recording (default: enabled). Only data a bug report
566+
* carries is sent (console and network logs, custom data, environment data, events). Can be
567+
* called before initialize.
568+
* @param {boolean} enabled
569+
*/
570+
static setRemoteLogCollectionEnabled(enabled) {
571+
GleapCaptureManager.getInstance().setRemoteLogCollectionEnabled(enabled);
572+
}
573+
544574
/**
545575
* Closes any open Gleap dialogs.
546576
* @param {boolean} resetRoutes
@@ -1667,6 +1697,7 @@ export {
16671697
GleapAiChatbarManager,
16681698
GleapAudioManager,
16691699
GleapBannerManager,
1700+
GleapCaptureManager,
16701701
GleapClickListener,
16711702
GleapConfigManager,
16721703
GleapConsoleLogManager,

‎src/GleapBannerManager.js‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Gleap, { GleapFrameManager } from './Gleap';
2+
import { isOutboundActionBlocked } from './GleapCaptureSettings';
23
import { bootstrapGleapFrame } from './GleapHelper';
34

45
export default class GleapBannerManager {
@@ -65,6 +66,10 @@ export default class GleapBannerManager {
6566
if (data.name === 'banner-close') {
6667
this.removeBannerUI();
6768
}
69+
// While the widget is hidden for a capture (screenshot or recording), actions open nothing.
70+
if (isOutboundActionBlocked(data.name)) {
71+
return;
72+
}
6873
if (data.name === 'start-conversation') {
6974
Gleap.startBot(data.data?.botId);
7075
}

0 commit comments

Comments
 (0)