glitchgrab 1.58.3 → 1.59.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/index.js CHANGED
@@ -49,12 +49,14 @@ __export(index_exports, {
49
49
  computeSignature: () => computeSignature,
50
50
  enhanceText: () => enhanceText,
51
51
  fetchGlitchgrabFeedback: () => fetchGlitchgrabFeedback,
52
+ fetchGlitchgrabReportThread: () => fetchGlitchgrabReportThread,
52
53
  fetchGlitchgrabReports: () => fetchGlitchgrabReports,
53
54
  getAppContext: () => getAppContext,
54
55
  getBreadcrumbs: () => getBreadcrumbs,
55
56
  getShortcutLabel: () => getShortcutLabel2,
56
57
  initBreadcrumbs: () => initBreadcrumbs,
57
58
  matchesShortcut: () => matchesShortcut,
59
+ postGlitchgrabReportComment: () => postGlitchgrabReportComment,
58
60
  sanitizeUrl: () => sanitizeUrl,
59
61
  sendFeedback: () => sendFeedback,
60
62
  sendLead: () => sendLead,
@@ -65,6 +67,7 @@ __export(index_exports, {
65
67
  useGlitchgrab: () => useGlitchgrab,
66
68
  useGlitchgrabActions: () => useGlitchgrabActions,
67
69
  useGlitchgrabFeedback: () => useGlitchgrabFeedback,
70
+ useGlitchgrabReportThread: () => useGlitchgrabReportThread,
68
71
  useGlitchgrabReports: () => useGlitchgrabReports
69
72
  });
70
73
  module.exports = __toCommonJS(index_exports);
@@ -772,12 +775,21 @@ function signedFields(identity) {
772
775
  return { userId: identity.userId, signature: identity.signature };
773
776
  }
774
777
  async function enhanceText(text, token, baseUrl, screenshot, context, identity) {
778
+ try {
779
+ return await enhanceTextOrThrow(text, token, baseUrl, screenshot, context, identity);
780
+ } catch (e) {
781
+ return text;
782
+ }
783
+ }
784
+ async function enhanceTextOrThrow(text, token, baseUrl, screenshot, context, identity) {
775
785
  var _a;
786
+ const trimmed = text.trim();
787
+ if (!trimmed) return text;
788
+ const unavailable = "AI enhance isn't available right now \u2014 send your report as written.";
789
+ let response;
776
790
  try {
777
- const trimmed = text.trim();
778
- if (!trimmed) return text;
779
791
  const url = `${baseUrl != null ? baseUrl : DEFAULT_BASE_URL}/api/v1/ai/enhance-text`;
780
- const response = await fetch(url, {
792
+ response = await fetch(url, {
781
793
  method: "POST",
782
794
  headers: {
783
795
  "Content-Type": "application/json",
@@ -790,15 +802,20 @@ async function enhanceText(text, token, baseUrl, screenshot, context, identity)
790
802
  ...signedFields(identity)
791
803
  })
792
804
  });
793
- if (!response.ok) return text;
794
- const envelope = await response.json();
795
- if ((envelope == null ? void 0 : envelope.success) && typeof ((_a = envelope.data) == null ? void 0 : _a.text) === "string") {
796
- return envelope.data.text;
797
- }
798
- return text;
799
805
  } catch (e) {
800
- return text;
806
+ throw new Error(unavailable);
807
+ }
808
+ if (response.status === 429) {
809
+ throw new Error("Too many AI enhance tries \u2014 wait a while, or send your report as written.");
801
810
  }
811
+ const envelope = await response.json().catch(() => null);
812
+ if (response.ok && (envelope == null ? void 0 : envelope.success) && typeof ((_a = envelope.data) == null ? void 0 : _a.text) === "string") {
813
+ return envelope.data.text;
814
+ }
815
+ if (response.status === 400 && typeof (envelope == null ? void 0 : envelope.error) === "string") {
816
+ throw new Error(envelope.error);
817
+ }
818
+ throw new Error(unavailable);
802
819
  }
803
820
  async function findSimilarIssues(description, token, baseUrl, identity, files) {
804
821
  var _a;
@@ -5550,6 +5567,7 @@ function ReportDialog({
5550
5567
  const [aiConversationId, setAiConversationId] = (0, import_react.useState)(null);
5551
5568
  const [issueTitle, setIssueTitle] = (0, import_react.useState)("");
5552
5569
  const [isEnhanced, setIsEnhanced] = (0, import_react.useState)(false);
5570
+ const [enhanceError, setEnhanceError] = (0, import_react.useState)(null);
5553
5571
  const [originalDescription, setOriginalDescription] = (0, import_react.useState)(
5554
5572
  null
5555
5573
  );
@@ -6004,6 +6022,7 @@ function ReportDialog({
6004
6022
  setSelfReportFrom(null);
6005
6023
  setVoiceError(null);
6006
6024
  setIsEnhanced(false);
6025
+ setEnhanceError(null);
6007
6026
  setOriginalDescription(null);
6008
6027
  setAssistOpen(false);
6009
6028
  setAssistUsed(false);
@@ -6069,6 +6088,7 @@ function ReportDialog({
6069
6088
  const sep = base.trim() ? " " : "";
6070
6089
  setDescriptionFromVoice(base + sep + text.trim());
6071
6090
  setValidationError(null);
6091
+ setEnhanceError(null);
6072
6092
  setIsEnhanced(false);
6073
6093
  setOriginalDescription(null);
6074
6094
  }
@@ -7164,6 +7184,7 @@ function ReportDialog({
7164
7184
  notePushToTalkEdit();
7165
7185
  setDescription(e.target.value);
7166
7186
  if (validationError) setValidationError(null);
7187
+ if (enhanceError) setEnhanceError(null);
7167
7188
  if (isEnhanced) {
7168
7189
  setIsEnhanced(false);
7169
7190
  setOriginalDescription(null);
@@ -7285,6 +7306,7 @@ function ReportDialog({
7285
7306
  var _a2;
7286
7307
  if (!description.trim() || isEnhancing) return;
7287
7308
  setIsEnhancing(true);
7309
+ setEnhanceError(null);
7288
7310
  try {
7289
7311
  const polished = await enhanceText2(
7290
7312
  description,
@@ -7297,6 +7319,10 @@ function ReportDialog({
7297
7319
  if (validationError)
7298
7320
  setValidationError(null);
7299
7321
  }
7322
+ } catch (err) {
7323
+ setEnhanceError(
7324
+ err instanceof Error && err.message ? err.message : "AI enhance isn't available right now \u2014 send your report as written."
7325
+ );
7300
7326
  } finally {
7301
7327
  setIsEnhancing(false);
7302
7328
  }
@@ -7555,6 +7581,20 @@ function ReportDialog({
7555
7581
  children: voiceError
7556
7582
  }
7557
7583
  ),
7584
+ enhanceError && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
7585
+ "p",
7586
+ {
7587
+ role: "alert",
7588
+ style: {
7589
+ color: "#ef4444",
7590
+ fontSize: "11px",
7591
+ lineHeight: 1.5,
7592
+ marginTop: "4px",
7593
+ marginBottom: 0
7594
+ },
7595
+ children: enhanceError
7596
+ }
7597
+ ),
7558
7598
  assistNotice && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
7559
7599
  "p",
7560
7600
  {
@@ -8577,6 +8617,69 @@ function getTypeIcon(type, color, size = 24) {
8577
8617
 
8578
8618
  // src/booking-dialog.tsx
8579
8619
  var import_react5 = require("react");
8620
+
8621
+ // src/phone.ts
8622
+ var COUNTRIES = [
8623
+ { iso: "IN", name: "India", dial: "91" },
8624
+ { iso: "AE", name: "UAE", dial: "971" },
8625
+ { iso: "US", name: "US / Canada", dial: "1" },
8626
+ { iso: "GB", name: "UK", dial: "44" },
8627
+ { iso: "SG", name: "Singapore", dial: "65" },
8628
+ { iso: "AU", name: "Australia", dial: "61" },
8629
+ { iso: "SA", name: "Saudi Arabia", dial: "966" },
8630
+ { iso: "QA", name: "Qatar", dial: "974" },
8631
+ { iso: "NP", name: "Nepal", dial: "977" },
8632
+ { iso: "BD", name: "Bangladesh", dial: "880" },
8633
+ { iso: "LK", name: "Sri Lanka", dial: "94" },
8634
+ { iso: "DE", name: "Germany", dial: "49" }
8635
+ ];
8636
+ var ZONE_COUNTRY = {
8637
+ "Asia/Kolkata": "91",
8638
+ "Asia/Calcutta": "91",
8639
+ "Asia/Dubai": "971",
8640
+ "Europe/London": "44",
8641
+ "Asia/Singapore": "65",
8642
+ "Asia/Riyadh": "966",
8643
+ "Asia/Qatar": "974",
8644
+ "Asia/Kathmandu": "977",
8645
+ "Asia/Katmandu": "977",
8646
+ "Asia/Dhaka": "880",
8647
+ "Asia/Colombo": "94",
8648
+ "Europe/Berlin": "49"
8649
+ };
8650
+ function defaultDialForZone(zone) {
8651
+ if (ZONE_COUNTRY[zone]) return ZONE_COUNTRY[zone];
8652
+ if (zone.startsWith("America/")) return "1";
8653
+ if (zone.startsWith("Australia/")) return "61";
8654
+ return "91";
8655
+ }
8656
+ function splitTypedPhone(typed, dial) {
8657
+ const trimmed = typed.trim();
8658
+ const digits = trimmed.replace(/\D/g, "");
8659
+ if (trimmed.startsWith("+")) {
8660
+ const match = [...COUNTRIES].sort((a, b) => b.dial.length - a.dial.length).find((c) => digits.startsWith(c.dial));
8661
+ if (match) return { dial: match.dial, national: digits.slice(match.dial.length) };
8662
+ }
8663
+ if (dial === "91" && digits.length === 12 && digits.startsWith("91")) return { dial, national: digits.slice(2) };
8664
+ if (dial === "91" && digits.length === 11 && digits.startsWith("0")) return { dial, national: digits.slice(1) };
8665
+ return { dial, national: digits };
8666
+ }
8667
+ function phoneProblem(dial, national) {
8668
+ const digits = national.replace(/\D/g, "");
8669
+ if (!digits) return "Enter your mobile number";
8670
+ if (dial === "91") {
8671
+ if (digits.length !== 10) return `An Indian mobile has 10 digits \u2014 this has ${digits.length}`;
8672
+ if (!/^[6-9]/.test(digits)) return "An Indian mobile starts with 6, 7, 8 or 9";
8673
+ return null;
8674
+ }
8675
+ if (digits.length < 6 || digits.length > 12) return "Check the number \u2014 it looks too short or too long";
8676
+ return null;
8677
+ }
8678
+ function toE164(dial, national) {
8679
+ return `+${dial}${national.replace(/\D/g, "")}`;
8680
+ }
8681
+
8682
+ // src/booking-dialog.tsx
8580
8683
  var import_jsx_runtime6 = require("react/jsx-runtime");
8581
8684
  function readQuestions(value) {
8582
8685
  if (!Array.isArray(value)) return [];
@@ -8719,7 +8822,9 @@ function BookingDialog({
8719
8822
  open,
8720
8823
  onClose,
8721
8824
  preview = false,
8722
- availabilityUrl
8825
+ availabilityUrl,
8826
+ intro,
8827
+ onBooked
8723
8828
  }) {
8724
8829
  var _a, _b, _c, _d, _e;
8725
8830
  const wide = useMediaQuery(WIDE_QUERY, true);
@@ -8742,6 +8847,8 @@ function BookingDialog({
8742
8847
  const [name, setName] = (0, import_react5.useState)("");
8743
8848
  const [email, setEmail] = (0, import_react5.useState)("");
8744
8849
  const [phone, setPhone] = (0, import_react5.useState)("");
8850
+ const [dial, setDial] = (0, import_react5.useState)(() => defaultDialForZone(localZone()));
8851
+ const [phoneTouched, setPhoneTouched] = (0, import_react5.useState)(false);
8745
8852
  const [note, setNote] = (0, import_react5.useState)("");
8746
8853
  const [questions, setQuestions] = (0, import_react5.useState)([]);
8747
8854
  const [answers, setAnswers] = (0, import_react5.useState)({});
@@ -8852,8 +8959,12 @@ function BookingDialog({
8852
8959
  const view = step !== "slots" ? step : !pageLoaded ? "loading" : questions.length > 0 && !asked ? "questions" : "slots";
8853
8960
  const roomy = step === "slots";
8854
8961
  async function startBooking() {
8855
- var _a2, _b2;
8962
+ var _a2, _b2, _c2;
8856
8963
  if (!selected) return;
8964
+ if (phoneError) {
8965
+ setPhoneTouched(true);
8966
+ return;
8967
+ }
8857
8968
  if (preview) {
8858
8969
  setPreviewEnded(true);
8859
8970
  return;
@@ -8867,7 +8978,7 @@ function BookingDialog({
8867
8978
  body: JSON.stringify({
8868
8979
  name,
8869
8980
  email,
8870
- phone,
8981
+ phone: toE164(dial, phone),
8871
8982
  note,
8872
8983
  answers: Object.fromEntries(given.map((q) => [q.id, q.answer])),
8873
8984
  startsAt: selected.startsAt,
@@ -8886,6 +8997,7 @@ function BookingDialog({
8886
8997
  if (json.data.confirmed) {
8887
8998
  setMeetUrl((_b2 = json.data.meetUrl) != null ? _b2 : null);
8888
8999
  setStep("done");
9000
+ reportBooked(json.data.bookingId, (_c2 = json.data.meetUrl) != null ? _c2 : null);
8889
9001
  return;
8890
9002
  }
8891
9003
  setBookingId(json.data.bookingId);
@@ -8896,8 +9008,16 @@ function BookingDialog({
8896
9008
  setBusy(false);
8897
9009
  }
8898
9010
  }
9011
+ function reportBooked(id, meet) {
9012
+ if (!onBooked || !id || !selected) return;
9013
+ try {
9014
+ onBooked({ bookingId: id, startsAt: selected.startsAt, name, email, phone: toE164(dial, phone), meetUrl: meet });
9015
+ } catch (e) {
9016
+ console.error("[glitchgrab] onBooked threw", e);
9017
+ }
9018
+ }
8899
9019
  async function confirmBooking() {
8900
- var _a2;
9020
+ var _a2, _b2;
8901
9021
  if (!bookingId) return;
8902
9022
  setBusy(true);
8903
9023
  setError(null);
@@ -8914,13 +9034,15 @@ function BookingDialog({
8914
9034
  }
8915
9035
  setMeetUrl(json.data.meetUrl);
8916
9036
  setStep("done");
9037
+ reportBooked(bookingId, (_b2 = json.data.meetUrl) != null ? _b2 : null);
8917
9038
  } catch (e) {
8918
9039
  setError("Couldn't reach the booking service");
8919
9040
  } finally {
8920
9041
  setBusy(false);
8921
9042
  }
8922
9043
  }
8923
- const canSubmitDetails = name.trim() && email.includes("@") && phone.replace(/\D/g, "").length >= 8;
9044
+ const phoneError = phoneProblem(dial, phone);
9045
+ const canSubmitDetails = Boolean(name.trim() && email.includes("@"));
8924
9046
  const selectedEnd = selected ? (_e = selected.endsAt) != null ? _e : new Date(new Date(selected.startsAt).getTime() + page.slotMinutes * 6e4).toISOString() : null;
8925
9047
  const monthDate = new Date(keyToUtc(`${month}-01`));
8926
9048
  const muted = { color: t.muted };
@@ -9067,6 +9189,14 @@ function BookingDialog({
9067
9189
  children: "Preview \u2014 nothing is booked"
9068
9190
  }
9069
9191
  ),
9192
+ intro && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
9193
+ "div",
9194
+ {
9195
+ "data-glitchgrab-booking-intro": true,
9196
+ style: { fontSize: 13, fontWeight: 600, lineHeight: 1.45, paddingRight: 32, color: t.ink, overflowWrap: "anywhere" },
9197
+ children: intro
9198
+ }
9199
+ ),
9070
9200
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 20, fontWeight: 600, lineHeight: 1.25, paddingRight: 32, overflowWrap: "anywhere" }, children: page.title }),
9071
9201
  page.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 13, lineHeight: 1.5, overflowWrap: "anywhere", ...muted }, children: page.description }),
9072
9202
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10, marginTop: 4, fontSize: 14 }, children: [
@@ -9509,14 +9639,17 @@ function BookingDialog({
9509
9639
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Field, { t, label: "Your name", value: name, onChange: setName, autoComplete: "name" }),
9510
9640
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Field, { t, label: "Email", value: email, onChange: setEmail, type: "email", autoComplete: "email" }),
9511
9641
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
9512
- Field,
9642
+ PhoneField,
9513
9643
  {
9514
9644
  t,
9515
- label: "Mobile number",
9516
- value: phone,
9517
- onChange: setPhone,
9518
- type: "tel",
9519
- autoComplete: "tel",
9645
+ dial,
9646
+ national: phone,
9647
+ onChange: (nextDial, national) => {
9648
+ setDial(nextDial);
9649
+ setPhone(national);
9650
+ },
9651
+ onBlur: () => setPhoneTouched(true),
9652
+ error: phoneTouched ? phoneError : null,
9520
9653
  hint: requireOtp ? "We send a code here to confirm, then a reminder before the call." : "We send a reminder here before the call."
9521
9654
  }
9522
9655
  ),
@@ -9804,6 +9937,64 @@ function Field({
9804
9937
  hint && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { marginTop: 6, fontSize: 12, fontWeight: 400, color: t.muted }, children: hint })
9805
9938
  ] });
9806
9939
  }
9940
+ function PhoneField({
9941
+ t,
9942
+ dial,
9943
+ national,
9944
+ onChange,
9945
+ onBlur,
9946
+ error,
9947
+ hint
9948
+ }) {
9949
+ const box = {
9950
+ boxSizing: "border-box",
9951
+ padding: "9px 12px",
9952
+ background: t.input,
9953
+ color: t.text,
9954
+ border: `1px solid ${error ? t.danger : t.border}`,
9955
+ borderRadius: 8,
9956
+ fontFamily: "inherit",
9957
+ fontSize: 14,
9958
+ outline: "none"
9959
+ };
9960
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: { display: "block", fontSize: 13, fontWeight: 500, color: t.text }, children: [
9961
+ "Mobile number",
9962
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 8, marginTop: 6 }, children: [
9963
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
9964
+ "select",
9965
+ {
9966
+ "aria-label": "Country code",
9967
+ value: dial,
9968
+ onChange: (e) => onChange(e.target.value, national),
9969
+ style: { ...box, flex: "0 0 auto", paddingRight: 8, cursor: "pointer" },
9970
+ children: COUNTRIES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("option", { value: c.dial, children: [
9971
+ c.iso,
9972
+ " +",
9973
+ c.dial
9974
+ ] }, c.iso))
9975
+ }
9976
+ ),
9977
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
9978
+ "input",
9979
+ {
9980
+ type: "tel",
9981
+ autoComplete: "tel",
9982
+ inputMode: "numeric",
9983
+ placeholder: dial === "91" ? "98765 43210" : "Mobile number",
9984
+ value: national,
9985
+ "aria-invalid": Boolean(error),
9986
+ onChange: (e) => {
9987
+ const next = splitTypedPhone(e.target.value, dial);
9988
+ onChange(next.dial, next.national);
9989
+ },
9990
+ onBlur,
9991
+ style: { ...box, flex: "1 1 auto", minWidth: 0, width: "100%" }
9992
+ }
9993
+ )
9994
+ ] }),
9995
+ error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { role: "alert", style: { marginTop: 6, fontSize: 12, fontWeight: 500, color: t.danger }, children: error }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { marginTop: 6, fontSize: 12, fontWeight: 400, color: t.muted }, children: hint })
9996
+ ] });
9997
+ }
9807
9998
 
9808
9999
  // src/rejection.ts
9809
10000
  var DETAILS_MAX_LENGTH = 1e3;
@@ -10308,21 +10499,27 @@ function GlitchgrabProviderInner({
10308
10499
  () => ({ userId: signedUserId != null ? signedUserId : null, signature }),
10309
10500
  [signedUserId, signature]
10310
10501
  );
10502
+ const enhanceForDialog = (0, import_react6.useCallback)(
10503
+ async (text, screenshot) => {
10504
+ const ctx = captureContext(visitedPagesRef.current);
10505
+ const context = {
10506
+ url: ctx.url,
10507
+ visitedPages: ctx.visitedPages.slice(-5),
10508
+ breadcrumbs: ctx.breadcrumbs.slice(-10).map((b) => ({ type: b.type, message: b.message }))
10509
+ };
10510
+ return await enhanceTextOrThrow(text, token, baseUrl, screenshot, context, identity);
10511
+ },
10512
+ [token, baseUrl, identity]
10513
+ );
10311
10514
  const enhance = (0, import_react6.useCallback)(
10312
10515
  async (text, screenshot) => {
10313
10516
  try {
10314
- const ctx = captureContext(visitedPagesRef.current);
10315
- const context = {
10316
- url: ctx.url,
10317
- visitedPages: ctx.visitedPages.slice(-5),
10318
- breadcrumbs: ctx.breadcrumbs.slice(-10).map((b) => ({ type: b.type, message: b.message }))
10319
- };
10320
- return await enhanceText(text, token, baseUrl, screenshot, context, identity);
10517
+ return await enhanceForDialog(text, screenshot);
10321
10518
  } catch (e) {
10322
10519
  return text;
10323
10520
  }
10324
10521
  },
10325
- [token, baseUrl, identity]
10522
+ [enhanceForDialog]
10326
10523
  );
10327
10524
  const assist = (0, import_react6.useCallback)(
10328
10525
  async (params) => {
@@ -10388,8 +10585,18 @@ function GlitchgrabProviderInner({
10388
10585
  const [signedUsers, setSignedUsers] = (0, import_react6.useState)("OFF");
10389
10586
  const aiBlockedUnsigned = signedUsers === "ENFORCE" && !signature;
10390
10587
  const [booking, setBooking] = (0, import_react6.useState)("closed");
10391
- const openBookingDialog = (0, import_react6.useCallback)(() => setBooking("live"), []);
10392
- const openBookingPreview = (0, import_react6.useCallback)(() => setBooking("preview"), []);
10588
+ const [bookingOptions, setBookingOptions] = (0, import_react6.useState)({});
10589
+ const openBookingDialog = (0, import_react6.useCallback)((options) => {
10590
+ setBookingOptions({
10591
+ intro: typeof (options == null ? void 0 : options.intro) === "string" ? options.intro : void 0,
10592
+ onBooked: typeof (options == null ? void 0 : options.onBooked) === "function" ? options.onBooked : void 0
10593
+ });
10594
+ setBooking("live");
10595
+ }, []);
10596
+ const openBookingPreview = (0, import_react6.useCallback)(() => {
10597
+ setBookingOptions({});
10598
+ setBooking("preview");
10599
+ }, []);
10393
10600
  (0, import_react6.useEffect)(() => {
10394
10601
  let cancelled = false;
10395
10602
  fetch(`${baseUrl}/api/v1/sdk/project`, {
@@ -10488,6 +10695,8 @@ function GlitchgrabProviderInner({
10488
10695
  baseUrl: baseUrl != null ? baseUrl : DEFAULT_BASE_URL2,
10489
10696
  open: booking !== "closed",
10490
10697
  preview: booking === "preview",
10698
+ intro: bookingOptions.intro,
10699
+ onBooked: bookingOptions.onBooked,
10491
10700
  onClose: () => setBooking("closed")
10492
10701
  }
10493
10702
  ),
@@ -10496,7 +10705,7 @@ function GlitchgrabProviderInner({
10496
10705
  {
10497
10706
  report,
10498
10707
  sendFeedback: feedback,
10499
- enhanceText: aiAssist && !aiBlockedUnsigned ? enhance : void 0,
10708
+ enhanceText: aiAssist && !aiBlockedUnsigned ? enhanceForDialog : void 0,
10500
10709
  assist: aiAssist && !aiBlockedUnsigned ? assist : void 0,
10501
10710
  findSimilarIssues: aiAssist && !aiBlockedUnsigned ? findSimilar : void 0,
10502
10711
  readFile: aiAssist && !aiBlockedUnsigned ? readFile : void 0,
@@ -10629,12 +10838,13 @@ function BookDemoButton({
10629
10838
  children
10630
10839
  }) {
10631
10840
  const { openBookingDialog } = useGlitchgrab();
10632
- if (children) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: children({ onClick: openBookingDialog }) });
10841
+ const open = () => openBookingDialog();
10842
+ if (children) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: children({ onClick: open }) });
10633
10843
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
10634
10844
  "button",
10635
10845
  {
10636
10846
  type: "button",
10637
- onClick: openBookingDialog,
10847
+ onClick: open,
10638
10848
  className,
10639
10849
  style: className ? void 0 : {
10640
10850
  padding: "10px 18px",
@@ -10841,6 +11051,87 @@ function useGlitchgrabActions({
10841
11051
  error
10842
11052
  };
10843
11053
  }
11054
+ async function fetchGlitchgrabReportThread({
11055
+ token,
11056
+ reportId
11057
+ }) {
11058
+ var _a, _b, _c;
11059
+ const res = await fetch(`${BASE_URL}/api/v1/sdk/reports/${encodeURIComponent(reportId)}`, {
11060
+ headers: { Authorization: `Bearer ${token}` }
11061
+ });
11062
+ const data = await res.json().catch(() => ({}));
11063
+ if (!res.ok || !data.success) throw new Error((_a = data.error) != null ? _a : "Failed to fetch report");
11064
+ return { ...data.data, comments: (_c = (_b = data.data) == null ? void 0 : _b.comments) != null ? _c : [] };
11065
+ }
11066
+ async function postGlitchgrabReportComment({
11067
+ token,
11068
+ reportId,
11069
+ message,
11070
+ reporterName,
11071
+ reporterEmail
11072
+ }) {
11073
+ var _a;
11074
+ const res = await fetch(`${BASE_URL}/api/v1/sdk/reports/${encodeURIComponent(reportId)}/comments`, {
11075
+ method: "POST",
11076
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
11077
+ body: JSON.stringify({ message, reporterName, reporterEmail })
11078
+ });
11079
+ const data = await res.json().catch(() => ({}));
11080
+ if (!res.ok || !data.success) throw new Error((_a = data.error) != null ? _a : "Failed to post comment");
11081
+ return data.data;
11082
+ }
11083
+ function useGlitchgrabReportThread({
11084
+ token,
11085
+ reportId,
11086
+ reporterName,
11087
+ reporterEmail
11088
+ }) {
11089
+ var _a;
11090
+ const [report, setReport] = (0, import_react8.useState)(null);
11091
+ const [isLoading, setIsLoading] = (0, import_react8.useState)(!!reportId);
11092
+ const [isFetching, setIsFetching] = (0, import_react8.useState)(false);
11093
+ const [isPosting, setIsPosting] = (0, import_react8.useState)(false);
11094
+ const [error, setError] = (0, import_react8.useState)(null);
11095
+ const refetch = (0, import_react8.useCallback)(async () => {
11096
+ if (!token || !reportId) {
11097
+ setReport(null);
11098
+ setIsLoading(false);
11099
+ return;
11100
+ }
11101
+ setIsFetching(true);
11102
+ setError(null);
11103
+ try {
11104
+ setReport(await fetchGlitchgrabReportThread({ token, reportId }));
11105
+ } catch (e) {
11106
+ setError(e instanceof Error ? e.message : "Network error");
11107
+ } finally {
11108
+ setIsLoading(false);
11109
+ setIsFetching(false);
11110
+ }
11111
+ }, [token, reportId]);
11112
+ (0, import_react8.useEffect)(() => {
11113
+ refetch();
11114
+ }, [refetch]);
11115
+ const addComment = (0, import_react8.useCallback)(
11116
+ async (message) => {
11117
+ if (!token || !reportId || !message.trim()) return false;
11118
+ setIsPosting(true);
11119
+ setError(null);
11120
+ try {
11121
+ await postGlitchgrabReportComment({ token, reportId, message: message.trim(), reporterName, reporterEmail });
11122
+ await refetch();
11123
+ return true;
11124
+ } catch (e) {
11125
+ setError(e instanceof Error ? e.message : "Failed to post comment");
11126
+ return false;
11127
+ } finally {
11128
+ setIsPosting(false);
11129
+ }
11130
+ },
11131
+ [token, reportId, reporterName, reporterEmail, refetch]
11132
+ );
11133
+ return { report, comments: (_a = report == null ? void 0 : report.comments) != null ? _a : [], isLoading, isFetching, isPosting, error, refetch, addComment };
11134
+ }
10844
11135
 
10845
11136
  // src/use-feedback.ts
10846
11137
  var import_react9 = require("react");
@@ -10922,12 +11213,14 @@ function useGlitchgrabFeedback(query) {
10922
11213
  computeSignature,
10923
11214
  enhanceText,
10924
11215
  fetchGlitchgrabFeedback,
11216
+ fetchGlitchgrabReportThread,
10925
11217
  fetchGlitchgrabReports,
10926
11218
  getAppContext,
10927
11219
  getBreadcrumbs,
10928
11220
  getShortcutLabel,
10929
11221
  initBreadcrumbs,
10930
11222
  matchesShortcut,
11223
+ postGlitchgrabReportComment,
10931
11224
  sanitizeUrl,
10932
11225
  sendFeedback,
10933
11226
  sendLead,
@@ -10938,6 +11231,7 @@ function useGlitchgrabFeedback(query) {
10938
11231
  useGlitchgrab,
10939
11232
  useGlitchgrabActions,
10940
11233
  useGlitchgrabFeedback,
11234
+ useGlitchgrabReportThread,
10941
11235
  useGlitchgrabReports
10942
11236
  });
10943
11237
  //# sourceMappingURL=index.js.map