Skip to content

Latest commit

 

History

History
86 lines (71 loc) · 22.2 KB

CHANGELOG.md

File metadata and controls

86 lines (71 loc) · 22.2 KB

Changelog

19.3.0

New capture experience: when Kai or a teammate asks for a screenshot or recording, the page shows a small frosted capture bar (Enter to capture, Esc to cancel) instead of the dark panel, with a shutter flash and the result flying into the chat card. Recordings can be paused and resumed, show a progress ring toward the time limit, and the default limit is now 2 minutes. The editor keeps all its tools with a cleaner toolbar. Works with older Messenger versions (no fly-in or pause there).

19.2.3

Fixed: the SDK closed its websocket without a close code, so the server logged every page leave and reconnect as an abnormal close (1005); it now closes with 1000. Going back online no longer drops a socket that is still connecting and opens a second one. The keepalive is a single PING text frame every 25 s (was two frames every 10 s), which still keeps idle proxies and load balancers from cutting the connection.

19.2.2

Fixed: a product tour or tooltip selector that is not valid CSS, such as an attribute typed without its brackets (data-test="x" instead of [data-test="x"]), no longer throws. The bracketless form now finds its element; anything else counts as not found, so the tour ends cleanly instead of stopping with an error, copilot tours stop waiting after 5 s, and tooltips keep working. Fixed: the tour editor's element picker no longer stores ids and classes that Mantine and React 19 generate at runtime (mantine-… ids, «r1» / _r_1_ from useId, Mantine 6 mantine-<hash>, Mantine 7 m_<hash> and __m__-… classes). They changed on the next page load, deploy or Mantine update and silently ended the tour; the picker now prefers stable names such as mantine-Button-root.

19.2.1

Surveys: questions asking for the email, name or phone of an identified contact are skipped (with the Surveys 2.0 "Skip when known" option). Gleap.showSurvey and every automatic survey now wait for an identify or updateContact still in flight (at most 5 s), so the survey starts with the identified contact instead of the guest. Fixed: a survey with nothing left to ask could leave an empty white card on screen (and Gleap.isOpened() true) when its frame was not loaded yet. A frame that was never shown now hides without the close animation and without a close event; Gleap.close() on a closed widget no longer fires close. Fixed: a survey card opened in a frame that had shown a card before was revealed before it had a height of its own; it now waits for its own content, like a fresh frame.

19.2.0

Surveys 2.0: Gleap.showSurvey(surveyId, options) takes { format: 'card' | 'full' | 'page', fields, personalToken, container, resume }; the old format strings ('survey', 'survey_full', 'survey_web') keep working. A survey shown before the SDK is ready now opens once it is. New survey containers: the card is a 348px corner popover with an enter animation and becomes a bottom sheet (handle, scrim, home-indicator inset) on phones; full screen covers the whole viewport and the survey draws its own background and close button (no more centred 640px card over a blurred backdrop); page renders inline in container (or document.body) and grows with the survey. Card and page surveys only appear once their height is known. Reduced motion is respected. Legacy surveys (not yet migrated) keep the previous card and full-screen look. New events survey-shown, survey-answered, survey-completed and survey-closed (with surveyId, version, responseId, …); outbound-sent keeps firing as before. New standalone survey-embed.js (~3 KB): <div data-gleap-survey="ID" data-api-key="KEY" data-mode="inline|popup|tab"> loads the SDK on demand and shows the survey inline, as a popup or behind a side tab. Fixed: inline banners did not show on pages whose body clips its overflow, the usual app-shell layout (html, body { height: 100%; overflow: hidden }). The banner sits in the body's top margin, which the body cut off, so only an empty strip appeared above the page. On such pages the banner now goes into the body's top padding (added to the page's own padding), which the body does not clip; with box-sizing: border-box the app also keeps fitting the viewport instead of losing the banner's height at the bottom. Pages that scroll normally and floating banners are unchanged.

19.1.1

Fixed: the product tour and tooltip visual editor now works from the dashboard on app.gleap.ai. The SDK accepts the editor's messages from exactly https://app.gleap.ai and https://app.gleap.io (no lookalike domains), loads the editor from the dashboard that opened it, and sends its replies only to that dashboard.

19.1.0

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. 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. 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). 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.

19.0.1

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.

19.0.0

Added widget color schemes, so the widget can match the host app's dark/light mode. Turn it on in the dashboard (Widget → Look & feel → Adapt to dark / light mode, delivered as colorScheme in the flow config) or at runtime with Gleap.setColorScheme("auto" | "light" | "dark", options?), which overrides the dashboard (until the first call, the dashboard setting applies): "auto" follows the host app and switches live when it changes. It looks at theme markers on <html>/<body> (classes like dark/light, attributes like data-theme, data-bs-theme, data-color-scheme, data-mode), then the page's CSS color-scheme, then the page background, and finally the OS prefers-color-scheme. "light" / "dark" force a scheme, e.g. from your app's own theme toggle. Any other value is treated as "auto". Gleap.setColorScheme only takes effect when "Adapt to dark / light mode" is enabled in the dashboard. While it is disabled (colorScheme missing or "default"), the widget is never themed, whatever the runtime scheme or backgrounds. The dashboard colors are the light palette. Dark mode uses the dark colors set in the dashboard: header colors (darkHeaderColor, darkHeaderColor2, darkHeaderColor3), UI color (darkColor) and background (darkBackgroundColor); a color without a dark value keeps its light one. Dark mode also uses the dark logo, header background image and composer glow set in the dashboard (darkLogo, darkBgImage, darkAurora); an empty dark logo/image means none in dark mode, and configs without these fields keep the light ones. Without any dark colors the widget keeps its normal colors, even when the host app is dark. The launcher, notifications, messenger, modals, AI chatbar and checklists all switch palettes, live. The runtime { lightBackgroundColor, darkBackgroundColor } options (#rgb/#rrggbb) override the background of the respective scheme; a runtime darkBackgroundColor also enables dark mode on its own. Button color is unchanged. The active scheme also applies to the AI chatbar (its style switches to the matching light/dark variant), to open modals, and to <gleap-checklist> elements without an explicit dark attribute. Product tour popovers now follow a dark widget background instead of staying white (their text was unreadable on dark backgrounds). Can be called before or after Gleap.initialize. Console logs are more complete and readable. The SDK no longer replaces window.console with a copy: it wraps log, info, warn, error, debug and assert (logged only when the assertion fails) in place and always calls the original method. Gleap.disableConsoleLogOverwrite() restores only the methods Gleap still owns, so a tool that wrapped the console after Gleap keeps working. Arguments are formatted instead of concatenated: objects and arrays are logged as compact JSON (four levels deep, circular references marked) instead of [object Object], errors with their stack, DOM elements as <tag#id.class>, Maps and Sets summarized, and %s, %d, %i, %f, %o, %O and %j substitutions are applied (%c styles are dropped). Uncaught errors and unhandled promise rejections are now logged as errors. An entry is cut at 1,000 characters (5,000 for errors) and marked … [truncated]. Network logs no longer hold responses in memory. The logger copied every text/* response and read the copy in full, so a server-sent events stream was buffered for the lifetime of the page and its connection stayed open after the app closed it. Streaming responses (text/event-stream, NDJSON, gRPC, multipart/x-mixed-replace) are now never read, other text responses are read from a copy up to 150 KB and the copy is then cancelled, and binary responses are not read at all ([binary body omitted]). Status, status text and response headers are recorded as soon as the headers arrive. Network log entries are more accurate. URLs are logged absolute and methods upper case. Request headers passed as a Headers object (logged as {} before), a list of pairs or a Request are recorded, and request bodies from URLSearchParams, FormData (text fields plus file names and sizes), blobs, typed arrays and Request objects are logged as text. Failed requests (network error, abort, timeout) carry the error, e.g. TypeError: Failed to fetch. XHR requests now record response headers, log json responses as JSON instead of <json>, report aborts and timeouts, and a reused XHR no longer adds another set of listeners on every open(). Resource timing entries have the correct time, and requests made with a relative URL are no longer listed twice. Requests to blacklisted URLs, the SDK's own included, no longer take one of the buffered request slots. Network log privacy settings apply more thoroughly. Props to ignore (from the dashboard and Gleap.setNetworkLogPropsToIgnore) are matched case-insensitively and removed from request and response headers, from JSON bodies at any depth (a dotted prop such as user.password also matches that path from the root), from form bodies and from URL query parameters. A body that is not valid JSON is no longer re-encoded as a JSON string. Authorization, Proxy-Authorization, Cookie and Set-Cookie header values are always replaced with [REDACTED]. The same rules and the URL blacklist now also apply to logs attached with Gleap.attachNetworkLogs. setNetworkLogPropsToIgnore and setNetworkLogsBlacklist replace their previous list instead of appending to it, the dashboard settings are replaced on every config refresh instead of piling up, and both lists apply together. Fixed: the event stream (/sessions/events, which delivers trackEvents, page views and session starts) could keep hitting an overloaded API and lost events. Each request carried the whole queue, which was emptied as soon as it went out, so the events of a failed request (429, 5xx, network error) were lost; after three failures it only paused until a fixed one-minute reset, a request that never answered blocked the stream, and an event whose data cannot be sent as JSON stopped it for good. Requests now only go out with a session, one at a time, give up after 30 s, and carry the oldest 100 events or about 256 KB at most; the rest follows right after a delivered request. After a 408, a 429, a 5xx, a network error or a timeout the events stay queued (any other error answer, e.g. 400 or 413, drops them, so they cannot hold back the queue) and the next request waits 3 s, then 6, 12, 24, 48 and at most 60 s (±20 % jitter), or longer when the answer has a Retry-After (seconds or an HTTP date, up to 5 minutes). Any 2xx answer delivers exactly the events it carried (events tracked in the meantime wait for the next request) and brings back the normal 2.5 s interval. The queue keeps at most 500 events; a session start is kept and the oldest other events make room. An event whose data cannot be sent as JSON is dropped instead of holding back the others. Fixed: the real-time connection (WebSocket) reconnected every 5 s after it dropped, forever and from every open tab in step, also while the browser was offline, and a reconnect still pending when the session changed (e.g. after identify) closed the new connection. Reconnects now wait 1 s, then 2, 4, 8, 16, 32 and at most 60 s (±20 % jitter). The wait starts over only after a connection stayed open for 10 s or received a message, so a server that accepts and closes connections right away is not retried every second. Only one reconnect is ever pending, and a new connection cancels it. While the browser is offline no reconnect is attempted; when it is back online the SDK reconnects right away. Gleap.destroy() now also closes the connection. The event stream only sends while connected, so its events stay queued until the connection is back.

18.1.2

Screenshots keep the layout around blocked elements. 18.1.1 replaced a blocked element with an empty placeholder of its size, and content after it could move, so drawings on the screenshot, which are in page coordinates, marked the wrong content: the margins of the element's first and last child that collapse through its edges were lost (a <p> in a blocked <div> moved everything after it up by its margins), a blocked display: contents element disappeared, a blocked table cell lost its colspan and rowspan, a blocked table row or row group took its cells' share of the column widths with it, and under a CSS transform or zoom the placeholder was scaled a second time. The placeholder now carries those margins and takes the element's size before transforms. A display: contents element keeps an empty box for each of its children, a blocked cell keeps its spans and a list item its number, and a blocked row or row group keeps an empty cell for each of its cells. A blocked inline-block, button, table cell, or flex or grid item that was aligned on the baseline of its text keeps that baseline (an empty box sits on its bottom edge, which moved the line). The ::before and ::after content of a blocked inline element is no longer rendered twice. The placeholder keeps the element's class but no longer its id, which rrweb leaves out of replays too and which can hold a name or an email address. The inner boxes of a blocked element keep only their tag, size and table spans: none of its content reaches the snapshot.

18.1.1

Screenshots no longer send the values of password fields to Gleap. The page snapshot behind a screenshot copied the value of every form field and masked only fields marked rr-mask or gleap-ignore="value", so passwords reached Gleap in clear text although the rendered image showed dots. In React apps the typed value was also copied through the field's value attribute (and a textarea's text), so even marked fields were not masked there. Screenshots and session replays now mask form fields by the same rule (except dropdowns: replays leave a <select>'s value visible). Always masked, whatever the replay options say: password fields (also after a "show password" toggle while replays run) and fields with autocomplete current-password, new-password, one-time-code, cc-number or cc-csc. Masked on request: fields marked rr-mask, gl-mask or gleap-ignore="value", on the field or on an element around it, and fields matching the maskTextClass / maskTextSelector replay options. Replays now honor these markers on inputs too; with rrweb 2 they only masked text. Gleap.setReplayOptions({ maskAllInputs: true }), maskInputOptions and maskInputFn now apply to screenshots as well. Passing maskInputOptions no longer turns off password masking in replays (rrweb replaced its { password: true } default with the object passed). In screenshots, a masked value keeps its length as * characters, in the field and in its value attribute; a masked textarea carries no text, and a masked select marks no option as selected. Screenshots no longer capture the content of blocked elements. An element marked rr-block or gl-block, or matching the blockClass / blockSelector replay options, was copied into the page snapshot in full (text, attributes, form fields, images, web component content) and only hidden when the screenshot was rendered. It is now replaced by an empty element of the same tag and size that keeps only its class and id, the way replays record an rr-block element; images inside it are not downloaded. The screenshot renders it blank, which now also holds for blocked elements inside web components and for blockClass / blockSelector elements, whose content used to show. Text inside elements marked rr-mask or gl-mask, or matching the maskTextClass / maskTextSelector replay options, is now masked in screenshots: every character except whitespace becomes *, or the site's maskTextFn formats it. Replays still block only rr-block elements and mask only rr-mask text (and the blockClass / blockSelector / maskTextClass / maskTextSelector options); gl-block and gl-mask apply to screenshots, and to form fields in replays. Comments inside these elements are left out. maskTextFn is typed (text, element) => string, as rrweb calls it.

18.1.0

Added control over the env data (device, browser and page details shown under the Env data tab of a ticket) the SDK collects: Gleap.setEnvDataPropsToIgnore(["currentUrl", "userAgent"]) removes individual env data keys from every ticket and conversation before it is sent. Each call replaces the previous list; an empty list resets it. Gleap.setDisableEnvData(true) stops collecting env data entirely (tickets arrive with an empty Env data tab); Gleap.setDisableEnvData(false) turns it back on. Both can be called before or after Gleap.initialize and apply to the next ticket. The per-form "Exclude data → Env data" switch in the dashboard keeps working as before.

18.0.1

The session replay buffer is now capped by size as well as by time (about 10 MB of events, on top of the existing three 5-minute checkpoints). When it grows past that, the oldest checkpoints are dropped first; if a single checkpoint outgrows the budget on a page that re-renders constantly, a fresh checkpoint replaces it (at most every 30 seconds), so the most recent activity is always kept. This bounds the memory the recorder holds in the host page and the size of the report upload. A report that the API (or a proxy in front of it) rejects as too large (HTTP 413) is no longer lost: it is resent without the session replay, and if needed without screenshot data and network logs. Added support for authenticated conversation files (opt-in per project): after a server-verified identify, the SDK hands a short-lived file session to the messenger, refreshes it while active, keeps it only for the same identity and revokes it on logout. Projects that have not enabled the setting are unaffected.

18.0.0

No breaking changes: major version aligned across all Gleap SDKs for the data-region release. Without Gleap.setRegion the SDK behaves exactly as 17.x (EU hosts).

Added data regions: Gleap.setRegion("eu" | "us") (case-insensitive, call before Gleap.initialize) sets the API url, the SDK streaming (WS) url and the messenger realtime host for the region at once; Gleap.getRegion() returns the active region. The default region stays eu. An unknown region logs a console warning and changes nothing. (eu: https://api.gleap.io, wss://ws.gleap.io, sockets.gleap.io; us: https://api.us.gleap.ai, wss://ws.us.gleap.ai, sockets.us.gleap.ai) Manual host setters are unchanged and keep working: setApiUrl, setWSApiUrl, setRealtimeHost, setFrameUrl, setBannerUrl, setModalUrl. The last call wins: a manual setter called after setRegion overrides that single host, setRegion called after manual setters overrides the three region hosts. The static widget hosts stay global for every region (messenger-app.gleap.io, outboundmedia.gleap.io, sdk.gleap.io, js.gleap.io); setRegion never changes the frame, banner or modal url. Added Gleap.setAgentConversationUrl to override the <gleap-agent-conversation> iframe url (same as setBannerUrl / setModalUrl). Added gleap.ai to the default network-log blacklist, so the SDK does not record its own US traffic. Added the GleapRegion type to the TypeScript definitions. Fixed the consentmanager allowlist (window.cmp_block_ignoredomains): messenger-app.gleap.io was never actually added because the result of concat was discarded.