glitchgrab 1.58.3 → 1.59.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
@@ -371,6 +371,42 @@ function MyReports() {
371
371
  }
372
372
  ```
373
373
 
374
+ ### Show the conversation on a report
375
+
376
+ Let your user see what the team said on their report and reply — no GitHub account needed. Replies land on the GitHub issue as the Glitchgrab app, with "Commented by: <name>" underneath.
377
+
378
+ ```tsx
379
+ import { useGlitchgrabReportThread } from "glitchgrab";
380
+
381
+ function ReportThread({ reportId }: { reportId: string }) {
382
+ const { report, comments, addComment, isPosting, error } = useGlitchgrabReportThread({
383
+ token: process.env.NEXT_PUBLIC_GLITCHGRAB_TOKEN!,
384
+ reportId, // from useGlitchgrabReports
385
+ reporterName: session.user.name, // optional, defaults to the name it was filed under
386
+ });
387
+ const [text, setText] = useState("");
388
+
389
+ return (
390
+ <div>
391
+ <h3>{report?.issue?.title} — {report?.issue?.githubState ?? "open"}</h3>
392
+ {comments.map((c, i) => (
393
+ <p key={i}><b>{c.author}</b>: {c.body}</p>
394
+ ))}
395
+ <textarea value={text} onChange={(e) => setText(e.target.value)} />
396
+ <button
397
+ disabled={isPosting || !text.trim()}
398
+ onClick={async () => { if (await addComment(text)) setText(""); }}
399
+ >
400
+ {isPosting ? "Sending…" : "Reply"}
401
+ </button>
402
+ {error && <p>{error}</p>}
403
+ </div>
404
+ );
405
+ }
406
+ ```
407
+
408
+ `addComment` never throws: it resolves `true` once the reply is posted and the thread reloaded, `false` with `error` set otherwise. Without the hook, use `fetchGlitchgrabReportThread` / `postGlitchgrabReportComment` (both throw) with your own data layer.
409
+
374
410
  ### With TanStack Query
375
411
 
376
412
  ```tsx
package/dist/index.d.mts CHANGED
@@ -280,6 +280,82 @@ declare function useGlitchgrabActions({ token, onSuccess, onError, }: {
280
280
  isPending: boolean;
281
281
  error: string | null;
282
282
  };
283
+ interface GlitchgrabComment {
284
+ /** GitHub login. Replies from your users post as the Glitchgrab app, with their name in the body. */
285
+ author: string;
286
+ body: string;
287
+ createdAt: string;
288
+ }
289
+ interface GlitchgrabReportThread {
290
+ id: string;
291
+ status: string;
292
+ rawInput: string | null;
293
+ createdAt: string;
294
+ issue: {
295
+ githubNumber: number;
296
+ githubUrl: string;
297
+ title: string;
298
+ body: string;
299
+ labels: string[];
300
+ severity: string | null;
301
+ /** "open" | "closed", or null when GitHub did not answer. */
302
+ githubState: string | null;
303
+ } | null;
304
+ /** Oldest first. Empty when no issue is linked yet. */
305
+ comments: GlitchgrabComment[];
306
+ }
307
+ /**
308
+ * Fetch one report with its GitHub issue and every comment on it — the team's
309
+ * replies and your user's. Throws on failure; pair it with your own data layer.
310
+ */
311
+ declare function fetchGlitchgrabReportThread({ token, reportId, }: {
312
+ token: string;
313
+ reportId: string;
314
+ }): Promise<GlitchgrabReportThread>;
315
+ /**
316
+ * Post a reply on the report's GitHub issue. It lands as the Glitchgrab app
317
+ * with "Commented by: <name>" underneath, so the team knows who said it.
318
+ * Throws on failure.
319
+ */
320
+ declare function postGlitchgrabReportComment({ token, reportId, message, reporterName, reporterEmail, }: {
321
+ token: string;
322
+ reportId: string;
323
+ message: string;
324
+ /** Defaults to the name the report was filed under. */
325
+ reporterName?: string;
326
+ reporterEmail?: string;
327
+ }): Promise<GlitchgrabComment>;
328
+ /**
329
+ * The conversation on one report: where it stands, what the team said, and a
330
+ * way for your user to reply — without a GitHub account.
331
+ *
332
+ * ```tsx
333
+ * const { report, comments, addComment, isPosting } = useGlitchgrabReportThread({
334
+ * token: process.env.NEXT_PUBLIC_GLITCHGRAB_TOKEN!,
335
+ * reportId,
336
+ * reporterName: session.user.name,
337
+ * });
338
+ * ```
339
+ *
340
+ * Never throws. `addComment` resolves `true` once the reply is on the issue
341
+ * (and the thread has reloaded), `false` with `error` set otherwise.
342
+ */
343
+ declare function useGlitchgrabReportThread({ token, reportId, reporterName, reporterEmail, }: {
344
+ token: string;
345
+ /** `reportId` from `useGlitchgrabReports` or the report submit result. Null waits. */
346
+ reportId: string | null;
347
+ reporterName?: string;
348
+ reporterEmail?: string;
349
+ }): {
350
+ report: GlitchgrabReportThread | null;
351
+ comments: GlitchgrabComment[];
352
+ isLoading: boolean;
353
+ isFetching: boolean;
354
+ isPosting: boolean;
355
+ error: string | null;
356
+ refetch: () => Promise<void>;
357
+ addComment: (message: string) => Promise<boolean>;
358
+ };
283
359
 
284
360
  interface FeedbackQuery {
285
361
  token: string;
@@ -422,4 +498,4 @@ declare function computeSignature(params: {
422
498
  declare function shouldSkipDuplicate(signature: string, windowMs?: number, now?: number): boolean;
423
499
  declare function clearDedupCache(): void;
424
500
 
425
- export { BookDemoButton, BookingDialog, 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 };
501
+ export { BookDemoButton, BookingDialog, Breadcrumb, BreadcrumbType, CaptureErrorOptions, CapturedContext, DeviceInfo, FeedbackButton, FeedbackButtonProps, FeedbackPayload, type FeedbackQuery, FeedbackResult, GLITCHGRAB_SHORTCUT, GLITCHGRAB_SHORTCUT_MAC, type GlitchgrabComment, GlitchgrabErrorBoundary, GlitchgrabFeedback, GlitchgrabProvider, GlitchgrabProviderProps, type GlitchgrabReport, type GlitchgrabReportThread, GlitchgrabSession, LeadInput, LeadResult, ReportButton, ReportButtonProps, ReportPayload, ReportResult, UseGlitchgrabReturn, addBreadcrumb, captureContext, captureDeviceInfo, captureError, clearAppContext, clearBreadcrumbs, clearDedupCache, computeSignature, enhanceText, fetchGlitchgrabFeedback, fetchGlitchgrabReportThread, fetchGlitchgrabReports, getAppContext, getBreadcrumbs, getShortcutLabel, initBreadcrumbs, matchesShortcut, postGlitchgrabReportComment, sanitizeUrl, sendFeedback, sendLead, sendReport, setContext, setContexts, shouldSkipDuplicate, useGlitchgrab, useGlitchgrabActions, useGlitchgrabFeedback, useGlitchgrabReportThread, useGlitchgrabReports };
package/dist/index.d.ts CHANGED
@@ -280,6 +280,82 @@ declare function useGlitchgrabActions({ token, onSuccess, onError, }: {
280
280
  isPending: boolean;
281
281
  error: string | null;
282
282
  };
283
+ interface GlitchgrabComment {
284
+ /** GitHub login. Replies from your users post as the Glitchgrab app, with their name in the body. */
285
+ author: string;
286
+ body: string;
287
+ createdAt: string;
288
+ }
289
+ interface GlitchgrabReportThread {
290
+ id: string;
291
+ status: string;
292
+ rawInput: string | null;
293
+ createdAt: string;
294
+ issue: {
295
+ githubNumber: number;
296
+ githubUrl: string;
297
+ title: string;
298
+ body: string;
299
+ labels: string[];
300
+ severity: string | null;
301
+ /** "open" | "closed", or null when GitHub did not answer. */
302
+ githubState: string | null;
303
+ } | null;
304
+ /** Oldest first. Empty when no issue is linked yet. */
305
+ comments: GlitchgrabComment[];
306
+ }
307
+ /**
308
+ * Fetch one report with its GitHub issue and every comment on it — the team's
309
+ * replies and your user's. Throws on failure; pair it with your own data layer.
310
+ */
311
+ declare function fetchGlitchgrabReportThread({ token, reportId, }: {
312
+ token: string;
313
+ reportId: string;
314
+ }): Promise<GlitchgrabReportThread>;
315
+ /**
316
+ * Post a reply on the report's GitHub issue. It lands as the Glitchgrab app
317
+ * with "Commented by: <name>" underneath, so the team knows who said it.
318
+ * Throws on failure.
319
+ */
320
+ declare function postGlitchgrabReportComment({ token, reportId, message, reporterName, reporterEmail, }: {
321
+ token: string;
322
+ reportId: string;
323
+ message: string;
324
+ /** Defaults to the name the report was filed under. */
325
+ reporterName?: string;
326
+ reporterEmail?: string;
327
+ }): Promise<GlitchgrabComment>;
328
+ /**
329
+ * The conversation on one report: where it stands, what the team said, and a
330
+ * way for your user to reply — without a GitHub account.
331
+ *
332
+ * ```tsx
333
+ * const { report, comments, addComment, isPosting } = useGlitchgrabReportThread({
334
+ * token: process.env.NEXT_PUBLIC_GLITCHGRAB_TOKEN!,
335
+ * reportId,
336
+ * reporterName: session.user.name,
337
+ * });
338
+ * ```
339
+ *
340
+ * Never throws. `addComment` resolves `true` once the reply is on the issue
341
+ * (and the thread has reloaded), `false` with `error` set otherwise.
342
+ */
343
+ declare function useGlitchgrabReportThread({ token, reportId, reporterName, reporterEmail, }: {
344
+ token: string;
345
+ /** `reportId` from `useGlitchgrabReports` or the report submit result. Null waits. */
346
+ reportId: string | null;
347
+ reporterName?: string;
348
+ reporterEmail?: string;
349
+ }): {
350
+ report: GlitchgrabReportThread | null;
351
+ comments: GlitchgrabComment[];
352
+ isLoading: boolean;
353
+ isFetching: boolean;
354
+ isPosting: boolean;
355
+ error: string | null;
356
+ refetch: () => Promise<void>;
357
+ addComment: (message: string) => Promise<boolean>;
358
+ };
283
359
 
284
360
  interface FeedbackQuery {
285
361
  token: string;
@@ -422,4 +498,4 @@ declare function computeSignature(params: {
422
498
  declare function shouldSkipDuplicate(signature: string, windowMs?: number, now?: number): boolean;
423
499
  declare function clearDedupCache(): void;
424
500
 
425
- export { BookDemoButton, BookingDialog, 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 };
501
+ export { BookDemoButton, BookingDialog, Breadcrumb, BreadcrumbType, CaptureErrorOptions, CapturedContext, DeviceInfo, FeedbackButton, FeedbackButtonProps, FeedbackPayload, type FeedbackQuery, FeedbackResult, GLITCHGRAB_SHORTCUT, GLITCHGRAB_SHORTCUT_MAC, type GlitchgrabComment, GlitchgrabErrorBoundary, GlitchgrabFeedback, GlitchgrabProvider, GlitchgrabProviderProps, type GlitchgrabReport, type GlitchgrabReportThread, GlitchgrabSession, LeadInput, LeadResult, ReportButton, ReportButtonProps, ReportPayload, ReportResult, UseGlitchgrabReturn, addBreadcrumb, captureContext, captureDeviceInfo, captureError, clearAppContext, clearBreadcrumbs, clearDedupCache, computeSignature, enhanceText, fetchGlitchgrabFeedback, fetchGlitchgrabReportThread, fetchGlitchgrabReports, getAppContext, getBreadcrumbs, getShortcutLabel, initBreadcrumbs, matchesShortcut, postGlitchgrabReportComment, sanitizeUrl, sendFeedback, sendLead, sendReport, setContext, setContexts, shouldSkipDuplicate, useGlitchgrab, useGlitchgrabActions, useGlitchgrabFeedback, useGlitchgrabReportThread, useGlitchgrabReports };
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.");
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;
801
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
  {
@@ -10308,21 +10348,27 @@ function GlitchgrabProviderInner({
10308
10348
  () => ({ userId: signedUserId != null ? signedUserId : null, signature }),
10309
10349
  [signedUserId, signature]
10310
10350
  );
10351
+ const enhanceForDialog = (0, import_react6.useCallback)(
10352
+ async (text, screenshot) => {
10353
+ const ctx = captureContext(visitedPagesRef.current);
10354
+ const context = {
10355
+ url: ctx.url,
10356
+ visitedPages: ctx.visitedPages.slice(-5),
10357
+ breadcrumbs: ctx.breadcrumbs.slice(-10).map((b) => ({ type: b.type, message: b.message }))
10358
+ };
10359
+ return await enhanceTextOrThrow(text, token, baseUrl, screenshot, context, identity);
10360
+ },
10361
+ [token, baseUrl, identity]
10362
+ );
10311
10363
  const enhance = (0, import_react6.useCallback)(
10312
10364
  async (text, screenshot) => {
10313
10365
  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);
10366
+ return await enhanceForDialog(text, screenshot);
10321
10367
  } catch (e) {
10322
10368
  return text;
10323
10369
  }
10324
10370
  },
10325
- [token, baseUrl, identity]
10371
+ [enhanceForDialog]
10326
10372
  );
10327
10373
  const assist = (0, import_react6.useCallback)(
10328
10374
  async (params) => {
@@ -10496,7 +10542,7 @@ function GlitchgrabProviderInner({
10496
10542
  {
10497
10543
  report,
10498
10544
  sendFeedback: feedback,
10499
- enhanceText: aiAssist && !aiBlockedUnsigned ? enhance : void 0,
10545
+ enhanceText: aiAssist && !aiBlockedUnsigned ? enhanceForDialog : void 0,
10500
10546
  assist: aiAssist && !aiBlockedUnsigned ? assist : void 0,
10501
10547
  findSimilarIssues: aiAssist && !aiBlockedUnsigned ? findSimilar : void 0,
10502
10548
  readFile: aiAssist && !aiBlockedUnsigned ? readFile : void 0,
@@ -10841,6 +10887,87 @@ function useGlitchgrabActions({
10841
10887
  error
10842
10888
  };
10843
10889
  }
10890
+ async function fetchGlitchgrabReportThread({
10891
+ token,
10892
+ reportId
10893
+ }) {
10894
+ var _a, _b, _c;
10895
+ const res = await fetch(`${BASE_URL}/api/v1/sdk/reports/${encodeURIComponent(reportId)}`, {
10896
+ headers: { Authorization: `Bearer ${token}` }
10897
+ });
10898
+ const data = await res.json().catch(() => ({}));
10899
+ if (!res.ok || !data.success) throw new Error((_a = data.error) != null ? _a : "Failed to fetch report");
10900
+ return { ...data.data, comments: (_c = (_b = data.data) == null ? void 0 : _b.comments) != null ? _c : [] };
10901
+ }
10902
+ async function postGlitchgrabReportComment({
10903
+ token,
10904
+ reportId,
10905
+ message,
10906
+ reporterName,
10907
+ reporterEmail
10908
+ }) {
10909
+ var _a;
10910
+ const res = await fetch(`${BASE_URL}/api/v1/sdk/reports/${encodeURIComponent(reportId)}/comments`, {
10911
+ method: "POST",
10912
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
10913
+ body: JSON.stringify({ message, reporterName, reporterEmail })
10914
+ });
10915
+ const data = await res.json().catch(() => ({}));
10916
+ if (!res.ok || !data.success) throw new Error((_a = data.error) != null ? _a : "Failed to post comment");
10917
+ return data.data;
10918
+ }
10919
+ function useGlitchgrabReportThread({
10920
+ token,
10921
+ reportId,
10922
+ reporterName,
10923
+ reporterEmail
10924
+ }) {
10925
+ var _a;
10926
+ const [report, setReport] = (0, import_react8.useState)(null);
10927
+ const [isLoading, setIsLoading] = (0, import_react8.useState)(!!reportId);
10928
+ const [isFetching, setIsFetching] = (0, import_react8.useState)(false);
10929
+ const [isPosting, setIsPosting] = (0, import_react8.useState)(false);
10930
+ const [error, setError] = (0, import_react8.useState)(null);
10931
+ const refetch = (0, import_react8.useCallback)(async () => {
10932
+ if (!token || !reportId) {
10933
+ setReport(null);
10934
+ setIsLoading(false);
10935
+ return;
10936
+ }
10937
+ setIsFetching(true);
10938
+ setError(null);
10939
+ try {
10940
+ setReport(await fetchGlitchgrabReportThread({ token, reportId }));
10941
+ } catch (e) {
10942
+ setError(e instanceof Error ? e.message : "Network error");
10943
+ } finally {
10944
+ setIsLoading(false);
10945
+ setIsFetching(false);
10946
+ }
10947
+ }, [token, reportId]);
10948
+ (0, import_react8.useEffect)(() => {
10949
+ refetch();
10950
+ }, [refetch]);
10951
+ const addComment = (0, import_react8.useCallback)(
10952
+ async (message) => {
10953
+ if (!token || !reportId || !message.trim()) return false;
10954
+ setIsPosting(true);
10955
+ setError(null);
10956
+ try {
10957
+ await postGlitchgrabReportComment({ token, reportId, message: message.trim(), reporterName, reporterEmail });
10958
+ await refetch();
10959
+ return true;
10960
+ } catch (e) {
10961
+ setError(e instanceof Error ? e.message : "Failed to post comment");
10962
+ return false;
10963
+ } finally {
10964
+ setIsPosting(false);
10965
+ }
10966
+ },
10967
+ [token, reportId, reporterName, reporterEmail, refetch]
10968
+ );
10969
+ return { report, comments: (_a = report == null ? void 0 : report.comments) != null ? _a : [], isLoading, isFetching, isPosting, error, refetch, addComment };
10970
+ }
10844
10971
 
10845
10972
  // src/use-feedback.ts
10846
10973
  var import_react9 = require("react");
@@ -10922,12 +11049,14 @@ function useGlitchgrabFeedback(query) {
10922
11049
  computeSignature,
10923
11050
  enhanceText,
10924
11051
  fetchGlitchgrabFeedback,
11052
+ fetchGlitchgrabReportThread,
10925
11053
  fetchGlitchgrabReports,
10926
11054
  getAppContext,
10927
11055
  getBreadcrumbs,
10928
11056
  getShortcutLabel,
10929
11057
  initBreadcrumbs,
10930
11058
  matchesShortcut,
11059
+ postGlitchgrabReportComment,
10931
11060
  sanitizeUrl,
10932
11061
  sendFeedback,
10933
11062
  sendLead,
@@ -10938,6 +11067,7 @@ function useGlitchgrabFeedback(query) {
10938
11067
  useGlitchgrab,
10939
11068
  useGlitchgrabActions,
10940
11069
  useGlitchgrabFeedback,
11070
+ useGlitchgrabReportThread,
10941
11071
  useGlitchgrabReports
10942
11072
  });
10943
11073
  //# sourceMappingURL=index.js.map