glitchgrab 1.55.0 → 1.57.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 CHANGED
@@ -288,6 +288,35 @@ curl "https://glitchgrab.dev/api/v1/sdk/feedback?approved=true&minRating=4&limit
288
288
 
289
289
  The repo is always derived from the token — there is no `repoId` to pass. Rate limit: 30 submissions per token per hour.
290
290
 
291
+ ## How do I save a lead without filing an issue?
292
+
293
+ A demo form or contact page asks a prospect for their details. Those are a **lead**, not a bug — `submitLead()` stores them in Glitchgrab and never creates a GitHub issue:
294
+
295
+ ```tsx
296
+ const { submitLead } = useGlitchgrab();
297
+
298
+ const result = await submitLead({
299
+ name: "Asha Mehta",
300
+ phone: "+91 98765 43210",
301
+ source: "demo", // which form — filter on it later
302
+ email: "asha@firm.in", // optional
303
+ company: "Mehta & Co", // optional
304
+ address: "Pune", // optional
305
+ metadata: { practice: "CA", language: "Hindi" }, // optional, flat values
306
+ });
307
+ // { success: true, id: "…", created: true } — or null on failure. Never throws.
308
+ ```
309
+
310
+ One lead per phone number per project: the same visitor submitting twice updates their lead (`created: false`) instead of adding a second row. Leads show under **Calls → Leads** in Glitchgrab, next to any demo the same phone or email went on to book. The owner can switch on a WhatsApp alert for each new lead there.
311
+
312
+ Read them in your own admin with the project's `ggc_` server key — the same key as `listBookings()`:
313
+
314
+ ```ts
315
+ import { listLeads } from "glitchgrab/server";
316
+
317
+ const leads = await listLeads({ source: "demo", since: "2026-09-01" }); // GLITCHGRAB_CALLS_KEY=ggc_…
318
+ ```
319
+
291
320
  ## What keyboard shortcuts are available?
292
321
 
293
322
  Once `GlitchgrabProvider` is mounted, these shortcuts work globally:
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { f as GlitchgrabProviderProps, U as UseGlitchgrabReturn, R as ReportButtonProps, F as FeedbackButtonProps, g as GlitchgrabSession, C as CaptureErrorOptions, e as GlitchgrabFeedback, a as BreadcrumbType, B as Breadcrumb, b as CapturedContext, D as DeviceInfo, c as FeedbackPayload, d as FeedbackResult, h as ReportPayload, i as ReportResult } from './types-DcDF1mXc.mjs';
3
- export { G as GlitchgrabConfig, j as ReportSeverity, k as ReportType, l as RuntimeInfo } from './types-DcDF1mXc.mjs';
2
+ import { f as GlitchgrabProviderProps, U as UseGlitchgrabReturn, R as ReportButtonProps, F as FeedbackButtonProps, g as GlitchgrabSession, C as CaptureErrorOptions, e as GlitchgrabFeedback, a as BreadcrumbType, B as Breadcrumb, b as CapturedContext, D as DeviceInfo, c as FeedbackPayload, d as FeedbackResult, L as LeadInput, h as LeadResult, i as ReportPayload, j as ReportResult } from './types-DfVtKZcp.mjs';
3
+ export { G as GlitchgrabConfig, k as ReportSeverity, l as ReportType, m as RuntimeInfo } from './types-DfVtKZcp.mjs';
4
4
  import * as React from 'react';
5
5
  import { ReactNode } from 'react';
6
6
 
@@ -356,6 +356,13 @@ declare function sendReport(payload: ReportPayload, baseUrl?: string): Promise<R
356
356
  * sendBeacon fallback: the caller needs a real result to show a thank-you.
357
357
  */
358
358
  declare function sendFeedback(payload: FeedbackPayload, baseUrl?: string): Promise<FeedbackResult | null>;
359
+ /**
360
+ * POST a lead (#534). Retries transient failures like {@link sendFeedback}; a
361
+ * 4xx other than 429 is a caller mistake and returns null at once.
362
+ */
363
+ declare function sendLead(token: string, lead: LeadInput & {
364
+ pageUrl?: string;
365
+ }, baseUrl?: string): Promise<LeadResult | null>;
359
366
  interface EnhanceContext {
360
367
  url?: string;
361
368
  visitedPages?: string[];
@@ -397,4 +404,4 @@ declare function computeSignature(params: {
397
404
  declare function shouldSkipDuplicate(signature: string, windowMs?: number, now?: number): boolean;
398
405
  declare function clearDedupCache(): void;
399
406
 
400
- export { BookDemoButton, Breadcrumb, BreadcrumbType, CaptureErrorOptions, CapturedContext, DeviceInfo, FeedbackButton, FeedbackButtonProps, FeedbackPayload, type FeedbackQuery, FeedbackResult, GLITCHGRAB_SHORTCUT, GLITCHGRAB_SHORTCUT_MAC, GlitchgrabErrorBoundary, GlitchgrabFeedback, GlitchgrabProvider, GlitchgrabProviderProps, type GlitchgrabReport, GlitchgrabSession, ReportButton, ReportButtonProps, ReportPayload, ReportResult, UseGlitchgrabReturn, addBreadcrumb, captureContext, captureDeviceInfo, captureError, clearAppContext, clearBreadcrumbs, clearDedupCache, computeSignature, enhanceText, fetchGlitchgrabFeedback, fetchGlitchgrabReports, getAppContext, getBreadcrumbs, getShortcutLabel, initBreadcrumbs, matchesShortcut, sanitizeUrl, sendFeedback, sendReport, setContext, setContexts, shouldSkipDuplicate, useGlitchgrab, useGlitchgrabActions, useGlitchgrabFeedback, useGlitchgrabReports };
407
+ export { BookDemoButton, Breadcrumb, BreadcrumbType, CaptureErrorOptions, CapturedContext, DeviceInfo, FeedbackButton, FeedbackButtonProps, FeedbackPayload, type FeedbackQuery, FeedbackResult, GLITCHGRAB_SHORTCUT, GLITCHGRAB_SHORTCUT_MAC, GlitchgrabErrorBoundary, GlitchgrabFeedback, GlitchgrabProvider, GlitchgrabProviderProps, type GlitchgrabReport, GlitchgrabSession, LeadInput, LeadResult, ReportButton, ReportButtonProps, ReportPayload, ReportResult, UseGlitchgrabReturn, addBreadcrumb, captureContext, captureDeviceInfo, captureError, clearAppContext, clearBreadcrumbs, clearDedupCache, computeSignature, enhanceText, fetchGlitchgrabFeedback, fetchGlitchgrabReports, getAppContext, getBreadcrumbs, getShortcutLabel, initBreadcrumbs, matchesShortcut, sanitizeUrl, sendFeedback, sendLead, sendReport, setContext, setContexts, shouldSkipDuplicate, useGlitchgrab, useGlitchgrabActions, useGlitchgrabFeedback, useGlitchgrabReports };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { f as GlitchgrabProviderProps, U as UseGlitchgrabReturn, R as ReportButtonProps, F as FeedbackButtonProps, g as GlitchgrabSession, C as CaptureErrorOptions, e as GlitchgrabFeedback, a as BreadcrumbType, B as Breadcrumb, b as CapturedContext, D as DeviceInfo, c as FeedbackPayload, d as FeedbackResult, h as ReportPayload, i as ReportResult } from './types-DcDF1mXc.js';
3
- export { G as GlitchgrabConfig, j as ReportSeverity, k as ReportType, l as RuntimeInfo } from './types-DcDF1mXc.js';
2
+ import { f as GlitchgrabProviderProps, U as UseGlitchgrabReturn, R as ReportButtonProps, F as FeedbackButtonProps, g as GlitchgrabSession, C as CaptureErrorOptions, e as GlitchgrabFeedback, a as BreadcrumbType, B as Breadcrumb, b as CapturedContext, D as DeviceInfo, c as FeedbackPayload, d as FeedbackResult, L as LeadInput, h as LeadResult, i as ReportPayload, j as ReportResult } from './types-DfVtKZcp.js';
3
+ export { G as GlitchgrabConfig, k as ReportSeverity, l as ReportType, m as RuntimeInfo } from './types-DfVtKZcp.js';
4
4
  import * as React from 'react';
5
5
  import { ReactNode } from 'react';
6
6
 
@@ -356,6 +356,13 @@ declare function sendReport(payload: ReportPayload, baseUrl?: string): Promise<R
356
356
  * sendBeacon fallback: the caller needs a real result to show a thank-you.
357
357
  */
358
358
  declare function sendFeedback(payload: FeedbackPayload, baseUrl?: string): Promise<FeedbackResult | null>;
359
+ /**
360
+ * POST a lead (#534). Retries transient failures like {@link sendFeedback}; a
361
+ * 4xx other than 429 is a caller mistake and returns null at once.
362
+ */
363
+ declare function sendLead(token: string, lead: LeadInput & {
364
+ pageUrl?: string;
365
+ }, baseUrl?: string): Promise<LeadResult | null>;
359
366
  interface EnhanceContext {
360
367
  url?: string;
361
368
  visitedPages?: string[];
@@ -397,4 +404,4 @@ declare function computeSignature(params: {
397
404
  declare function shouldSkipDuplicate(signature: string, windowMs?: number, now?: number): boolean;
398
405
  declare function clearDedupCache(): void;
399
406
 
400
- export { BookDemoButton, Breadcrumb, BreadcrumbType, CaptureErrorOptions, CapturedContext, DeviceInfo, FeedbackButton, FeedbackButtonProps, FeedbackPayload, type FeedbackQuery, FeedbackResult, GLITCHGRAB_SHORTCUT, GLITCHGRAB_SHORTCUT_MAC, GlitchgrabErrorBoundary, GlitchgrabFeedback, GlitchgrabProvider, GlitchgrabProviderProps, type GlitchgrabReport, GlitchgrabSession, ReportButton, ReportButtonProps, ReportPayload, ReportResult, UseGlitchgrabReturn, addBreadcrumb, captureContext, captureDeviceInfo, captureError, clearAppContext, clearBreadcrumbs, clearDedupCache, computeSignature, enhanceText, fetchGlitchgrabFeedback, fetchGlitchgrabReports, getAppContext, getBreadcrumbs, getShortcutLabel, initBreadcrumbs, matchesShortcut, sanitizeUrl, sendFeedback, sendReport, setContext, setContexts, shouldSkipDuplicate, useGlitchgrab, useGlitchgrabActions, useGlitchgrabFeedback, useGlitchgrabReports };
407
+ export { BookDemoButton, Breadcrumb, BreadcrumbType, CaptureErrorOptions, CapturedContext, DeviceInfo, FeedbackButton, FeedbackButtonProps, FeedbackPayload, type FeedbackQuery, FeedbackResult, GLITCHGRAB_SHORTCUT, GLITCHGRAB_SHORTCUT_MAC, GlitchgrabErrorBoundary, GlitchgrabFeedback, GlitchgrabProvider, GlitchgrabProviderProps, type GlitchgrabReport, GlitchgrabSession, LeadInput, LeadResult, ReportButton, ReportButtonProps, ReportPayload, ReportResult, UseGlitchgrabReturn, addBreadcrumb, captureContext, captureDeviceInfo, captureError, clearAppContext, clearBreadcrumbs, clearDedupCache, computeSignature, enhanceText, fetchGlitchgrabFeedback, fetchGlitchgrabReports, getAppContext, getBreadcrumbs, getShortcutLabel, initBreadcrumbs, matchesShortcut, sanitizeUrl, sendFeedback, sendLead, sendReport, setContext, setContexts, shouldSkipDuplicate, useGlitchgrab, useGlitchgrabActions, useGlitchgrabFeedback, useGlitchgrabReports };
package/dist/index.js CHANGED
@@ -56,6 +56,7 @@ __export(index_exports, {
56
56
  matchesShortcut: () => matchesShortcut,
57
57
  sanitizeUrl: () => sanitizeUrl,
58
58
  sendFeedback: () => sendFeedback,
59
+ sendLead: () => sendLead,
59
60
  sendReport: () => sendReport,
60
61
  setContext: () => setContext,
61
62
  setContexts: () => setContexts,
@@ -724,6 +725,36 @@ async function sendFeedback(payload, baseUrl) {
724
725
  return null;
725
726
  }
726
727
  }
728
+ async function sendLead(token, lead, baseUrl) {
729
+ var _a, _b;
730
+ try {
731
+ const url = `${baseUrl != null ? baseUrl : DEFAULT_BASE_URL}/api/v1/sdk/leads`;
732
+ const body = JSON.stringify(lead);
733
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
734
+ try {
735
+ const response = await fetch(url, {
736
+ method: "POST",
737
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
738
+ body
739
+ });
740
+ if (response.ok) {
741
+ const envelope = await response.json();
742
+ return { success: envelope.success, id: (_a = envelope.data) == null ? void 0 : _a.leadId, created: (_b = envelope.data) == null ? void 0 : _b.created };
743
+ }
744
+ if (response.status >= 400 && response.status < 500 && response.status !== 429) {
745
+ return null;
746
+ }
747
+ } catch (e) {
748
+ }
749
+ if (attempt < MAX_RETRIES - 1) {
750
+ await new Promise((resolve) => setTimeout(resolve, RETRY_BASE_MS * Math.pow(2, attempt)));
751
+ }
752
+ }
753
+ return null;
754
+ } catch (e) {
755
+ return null;
756
+ }
757
+ }
727
758
  function signedFields(identity) {
728
759
  if (!(identity == null ? void 0 : identity.signature) || !identity.userId) return {};
729
760
  return { userId: identity.userId, signature: identity.signature };
@@ -7650,6 +7681,7 @@ var DARK = {
7650
7681
  input: "#1c1c1c",
7651
7682
  danger: "#f87171"
7652
7683
  };
7684
+ var GLITCHGRAB_LOGO = "data:image/webp;base64,UklGRp4EAABXRUJQVlA4WAoAAAAQAAAALwAALwAAQUxQSEUBAAABkKNt27FHd2y7tm0nG7Dt9FblLODfQVLZdjpVKm1nbDyjD+8zaiNiAqCiqU9eFxFRf0moLcTbVQ0efybJr9dzre5i3DRXpPjlSLR6TprnpOrnkUCVUg5J9ZeNWipo9XwmkaO2imzGSPBOsALbdRJ+ESxLZ4IYHjvI6SGWS/pSyR95UI+E4wkx/Rj/Kw2x3db/yfklH8r8SUOMtw0A21tOlABUEes+YJDXsbHpMa/PwT6feFF1JjF/7OL2H9jPLpdd+BdmHTa3vL7GYZ7XjTVaeM0ALq9Z5QAY5XRqCSDqE6MaANCa4HNm9RMCXrEpw68buIxqS+iO8ThyhLTtPofXYZAbdCHuYxHkBx+KelkEpU6LYg7CoVy/+4OAEUeomrSh1lG5FlTWz95U47DGGgL1E3uPvsq6msmxgmjTkOpHibYEWygHAFZQOCAyAwAA8A8AnQEqMAAwAD5RIo5FI6IhFVqtVDgFBLYAWI97RVZAlk7wMR3bb89h6MN4A3i/9zvSAphf3vH3/0HC3r5457/O2wKZ749Pc73B/1W60i0ECBPawzM4trUtZQ8JtnKZx+cwouZ82/u4HWY5VC5OeeUWNuV9ET+TBNS1eRgMFyatx05O0aeAAAD+/7CUHtrj64E06fYjHM7Q6zHWgTd3U0C4VJrAF/R5hdPSi82mSQBF5QsO33GBZY8X0rVCdP/z4T1K4kahIp5q2b7v5a27kvLvT6COf70O+y7YmvruHd6RDcU1mBjIlm8lam4muqzNQUzxqeGx/jHt+J9b9i8HosTQeKYzbaueiRUr4JsXe8LUT5L/a/f/yKVjFFWpgGv10mBFfcEOWZIh8E05Ycfl3+usHVjX3TPD8CV0qYDRUWvkEliByCh0+LlmWIx+hfITiHKBEMnfETAvxOR9mmb+uq6rxA8jM4co//NoOTG0t5aeCEFeay8OZB54GerisvQV9nQEPqaHCz11f/LqoYK741K+CrbQxFXH6J5eaWL/GeJTngtDoj+gA9j9xAtjY1KpSt3MVV49bD2yr7RQ11A8S1rx5/AKmEPnvuMe8rjGBOnNy/3NLI0Et/IIL90XbGd1iwnOBKQo4L+bILSQoJBkGlRVBzLnBbHhGD61nqtu6E+abGIujbnpSkr0CGTkADsl+GhZiKsDk+/rrkzlRXKETXRvEkEijwm9kSEEyvZih4Q3G83MyTTncMY/rK3kygpJrXRvct4f2jYwk9v7+XSypDagws/032dx8xxVJXSXJ39MjKKEIFn7OWNuQHgTMtjrOa/oqen5l2w4X9ExYGneasi4Qx+vFjUCcm2WmUocqcP2PYmXWCYvCb/9GTfej/GE0Ow9xM4Ig9NrrL8g/wNMKu3W3GWpCEXljm6jSM9G1/EAwjoAAwE3M4pBphXhZ3lptxATFI3PHvirASzzBxTQOZKTE8kD8+cUUTnpLznfoM4L9zihNIlJl4NfTbxtKZpeaNMuzqZGwOxPBGzt/oi2nkR2B9/7ciZxk3EhbqrxGpEZ0VB0DldyrzwuF/G80twAAAA=";
7653
7685
  var WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
7654
7686
  var DAY_MS = 864e5;
7655
7687
  var ZONE_PAD_MS = 14 * 36e5;
@@ -7924,6 +7956,55 @@ function BookingDialog({
7924
7956
  const selectedEnd = selected ? (_e = selected.endsAt) != null ? _e : new Date(new Date(selected.startsAt).getTime() + page.slotMinutes * 6e4).toISOString() : null;
7925
7957
  const monthDate = new Date(keyToUtc(`${month}-01`));
7926
7958
  const muted = { color: t.muted };
7959
+ const credit = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
7960
+ "div",
7961
+ {
7962
+ style: {
7963
+ marginTop: wide ? whatsappUrl && step === "slots" ? 14 : "auto" : 0,
7964
+ padding: wide ? 0 : "4px 20px 18px",
7965
+ display: "flex",
7966
+ justifyContent: wide ? "flex-start" : "center"
7967
+ },
7968
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
7969
+ "a",
7970
+ {
7971
+ href: "https://glitchgrab.dev/?utm_source=booking-dialog",
7972
+ target: "_blank",
7973
+ rel: "noreferrer",
7974
+ className: "gg-bk-ghost",
7975
+ style: {
7976
+ display: "inline-flex",
7977
+ alignItems: "center",
7978
+ gap: 7,
7979
+ padding: "4px 11px 4px 4px",
7980
+ borderRadius: 999,
7981
+ border: `1px solid ${t.border}`,
7982
+ fontSize: 11.5,
7983
+ letterSpacing: "0.01em",
7984
+ color: t.muted,
7985
+ textDecoration: "none",
7986
+ whiteSpace: "nowrap"
7987
+ },
7988
+ children: [
7989
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
7990
+ "img",
7991
+ {
7992
+ src: GLITCHGRAB_LOGO,
7993
+ alt: "",
7994
+ width: 18,
7995
+ height: 18,
7996
+ style: { width: 18, height: 18, borderRadius: 999, display: "block" }
7997
+ }
7998
+ ),
7999
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
8000
+ "Powered by ",
8001
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: t.text, fontWeight: 600 }, children: "Glitchgrab" })
8002
+ ] })
8003
+ ]
8004
+ }
8005
+ )
8006
+ }
8007
+ );
7927
8008
  const summary = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
7928
8009
  "div",
7929
8010
  {
@@ -8071,7 +8152,8 @@ function BookingDialog({
8071
8152
  ] })
8072
8153
  ]
8073
8154
  }
8074
- )
8155
+ ),
8156
+ wide && credit
8075
8157
  ]
8076
8158
  }
8077
8159
  );
@@ -8461,7 +8543,8 @@ function BookingDialog({
8461
8543
  step === "slots" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
8462
8544
  calendar,
8463
8545
  times
8464
- ] }) : form
8546
+ ] }) : form,
8547
+ !wide && credit
8465
8548
  ]
8466
8549
  }
8467
8550
  )
@@ -9075,6 +9158,17 @@ function GlitchgrabProviderInner({
9075
9158
  },
9076
9159
  [token, baseUrl, session]
9077
9160
  );
9161
+ const submitLead = (0, import_react6.useCallback)(
9162
+ async (lead) => {
9163
+ try {
9164
+ const context = captureContext(visitedPagesRef.current);
9165
+ return await sendLead(token, { ...lead, pageUrl: context.url }, baseUrl);
9166
+ } catch (e) {
9167
+ return null;
9168
+ }
9169
+ },
9170
+ [token, baseUrl]
9171
+ );
9078
9172
  const openFeedbackDialog = (0, import_react6.useCallback)(() => {
9079
9173
  if (typeof window !== "undefined") {
9080
9174
  window.dispatchEvent(
@@ -9252,6 +9346,7 @@ function GlitchgrabProviderInner({
9252
9346
  setContext: setContext2,
9253
9347
  setContexts: setContexts2,
9254
9348
  sendFeedback: feedback,
9349
+ submitLead,
9255
9350
  openFeedbackDialog,
9256
9351
  addBreadcrumb: addBreadcrumb2,
9257
9352
  openReportDialog,
@@ -9717,6 +9812,7 @@ function useGlitchgrabFeedback(query) {
9717
9812
  matchesShortcut,
9718
9813
  sanitizeUrl,
9719
9814
  sendFeedback,
9815
+ sendLead,
9720
9816
  sendReport,
9721
9817
  setContext,
9722
9818
  setContexts,