hale-commenting-system 1.0.7 → 2.0.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 +154 -150
- package/cli/dist/index.js +100 -825
- package/cli/dist/index.js.map +1 -1
- package/dist/index.d.mts +6 -95
- package/dist/index.d.ts +6 -95
- package/dist/index.js +140 -1640
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +127 -1639
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -10
package/dist/index.mjs
CHANGED
|
@@ -4,431 +4,36 @@ import { useLocation } from "react-router-dom";
|
|
|
4
4
|
|
|
5
5
|
// src/contexts/CommentContext.tsx
|
|
6
6
|
import * as React from "react";
|
|
7
|
-
|
|
8
|
-
// src/services/githubAdapter.ts
|
|
9
|
-
import axios from "axios";
|
|
10
|
-
var getApiBase = () => {
|
|
11
|
-
if (typeof window === "undefined") return "/api/github-api";
|
|
12
|
-
const isNetlify = window.location.hostname.includes("netlify.app");
|
|
13
|
-
const apiBase = isNetlify ? "/.netlify/functions/github-api" : "/api/github-api";
|
|
14
|
-
console.log("\u{1F50D} API detection:", { hostname: window.location.hostname, isNetlify, apiBase });
|
|
15
|
-
return apiBase;
|
|
16
|
-
};
|
|
17
|
-
var getStoredToken = () => {
|
|
18
|
-
if (typeof window === "undefined") return null;
|
|
19
|
-
const token = localStorage.getItem("github_access_token");
|
|
20
|
-
console.log("\u{1F511} getStoredToken:", token ? "Token found" : "No token found");
|
|
21
|
-
return token;
|
|
22
|
-
};
|
|
23
|
-
var getStoredUser = () => {
|
|
24
|
-
if (typeof window === "undefined") return null;
|
|
25
|
-
const userStr = localStorage.getItem("github_user");
|
|
26
|
-
return userStr ? JSON.parse(userStr) : null;
|
|
27
|
-
};
|
|
28
|
-
var storeGitHubAuth = (token, login, avatar) => {
|
|
29
|
-
localStorage.setItem("github_access_token", token);
|
|
30
|
-
localStorage.setItem("github_user", JSON.stringify({ login, avatar }));
|
|
31
|
-
};
|
|
32
|
-
var clearGitHubAuth = () => {
|
|
33
|
-
localStorage.removeItem("github_access_token");
|
|
34
|
-
localStorage.removeItem("github_user");
|
|
35
|
-
};
|
|
36
|
-
var getAuthenticatedUser = () => {
|
|
37
|
-
return getStoredUser();
|
|
38
|
-
};
|
|
39
|
-
var isGitHubConfigured = () => {
|
|
40
|
-
const token = getStoredToken();
|
|
41
|
-
const owner = process.env.VITE_GITHUB_OWNER;
|
|
42
|
-
const repo = process.env.VITE_GITHUB_REPO;
|
|
43
|
-
console.log("\u{1F50D} isGitHubConfigured check:", { hasToken: !!token, owner, repo });
|
|
44
|
-
return !!(token && owner && repo);
|
|
45
|
-
};
|
|
46
|
-
async function makeGitHubRequest(method, endpoint, data) {
|
|
47
|
-
const token = getStoredToken();
|
|
48
|
-
if (!token) {
|
|
49
|
-
throw new Error("Not authenticated with GitHub");
|
|
50
|
-
}
|
|
51
|
-
const response = await axios.post(getApiBase(), {
|
|
52
|
-
token,
|
|
53
|
-
method,
|
|
54
|
-
endpoint,
|
|
55
|
-
data
|
|
56
|
-
});
|
|
57
|
-
return response.data;
|
|
58
|
-
}
|
|
59
|
-
var githubAdapter = {
|
|
60
|
-
/**
|
|
61
|
-
* Create a new GitHub Issue for a comment thread
|
|
62
|
-
*/
|
|
63
|
-
async createIssue(title, body, route, x, y, version) {
|
|
64
|
-
console.log("\u{1F535} createIssue called", { title, route, x, y, version });
|
|
65
|
-
if (!isGitHubConfigured()) {
|
|
66
|
-
console.warn("\u26A0\uFE0F GitHub not configured. Skipping issue creation.");
|
|
67
|
-
return { success: false, error: "Please sign in with GitHub" };
|
|
68
|
-
}
|
|
69
|
-
const owner = process.env.VITE_GITHUB_OWNER;
|
|
70
|
-
const repo = process.env.VITE_GITHUB_REPO;
|
|
71
|
-
console.log("\u{1F535} GitHub config:", { owner, repo, hasToken: !!getStoredToken() });
|
|
72
|
-
try {
|
|
73
|
-
const metadata = [
|
|
74
|
-
`- Route: \`${route}\``,
|
|
75
|
-
version ? `- Version: \`${version}\`` : null,
|
|
76
|
-
`- Coordinates: \`(${Math.round(x)}, ${Math.round(y)})\``
|
|
77
|
-
].filter(Boolean).join("\n");
|
|
78
|
-
const issueBody = {
|
|
79
|
-
title,
|
|
80
|
-
body: `${body}
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
**Metadata:**
|
|
84
|
-
${metadata}`
|
|
85
|
-
};
|
|
86
|
-
console.log("\u{1F535} Calling makeGitHubRequest...");
|
|
87
|
-
const issueData = await makeGitHubRequest("POST", `/repos/${owner}/${repo}/issues`, issueBody);
|
|
88
|
-
console.log("\u2705 Created GitHub Issue #", issueData.number);
|
|
89
|
-
try {
|
|
90
|
-
const labels = [
|
|
91
|
-
"apollo-comment",
|
|
92
|
-
`route:${route}`,
|
|
93
|
-
`coords:${Math.round(x)},${Math.round(y)}`
|
|
94
|
-
];
|
|
95
|
-
if (version) labels.push(`version:${version}`);
|
|
96
|
-
await makeGitHubRequest(
|
|
97
|
-
"POST",
|
|
98
|
-
`/repos/${owner}/${repo}/issues/${issueData.number}/labels`,
|
|
99
|
-
{ labels }
|
|
100
|
-
);
|
|
101
|
-
console.log("\u2705 Added labels to Issue #", issueData.number);
|
|
102
|
-
} catch (labelError) {
|
|
103
|
-
console.warn("\u26A0\uFE0F Could not add labels (labels may not exist in repo)");
|
|
104
|
-
}
|
|
105
|
-
return { success: true, data: issueData };
|
|
106
|
-
} catch (error) {
|
|
107
|
-
const errorMessage = error?.response?.data?.message || error?.message || "Failed to create issue";
|
|
108
|
-
console.error("\u274C Failed to create GitHub Issue:", {
|
|
109
|
-
message: errorMessage,
|
|
110
|
-
error: error?.response?.data || error,
|
|
111
|
-
status: error?.response?.status
|
|
112
|
-
});
|
|
113
|
-
return { success: false, error: errorMessage };
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
/**
|
|
117
|
-
* Add a comment to an existing GitHub Issue
|
|
118
|
-
*/
|
|
119
|
-
async createComment(issueNumber, body) {
|
|
120
|
-
if (!isGitHubConfigured()) {
|
|
121
|
-
return { success: false, error: "Please sign in with GitHub" };
|
|
122
|
-
}
|
|
123
|
-
const owner = process.env.VITE_GITHUB_OWNER;
|
|
124
|
-
const repo = process.env.VITE_GITHUB_REPO;
|
|
125
|
-
try {
|
|
126
|
-
const commentData = await makeGitHubRequest(
|
|
127
|
-
"POST",
|
|
128
|
-
`/repos/${owner}/${repo}/issues/${issueNumber}/comments`,
|
|
129
|
-
{ body }
|
|
130
|
-
);
|
|
131
|
-
console.log(`\u2705 Added comment to Issue #${issueNumber}`);
|
|
132
|
-
return { success: true, data: commentData };
|
|
133
|
-
} catch (error) {
|
|
134
|
-
const errorMessage = error?.response?.data?.message || error?.message || "Failed to create comment";
|
|
135
|
-
console.error(`\u274C Failed to add comment to Issue #${issueNumber}:`, errorMessage);
|
|
136
|
-
return { success: false, error: errorMessage };
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
/**
|
|
140
|
-
* Fetch all issues for a specific route
|
|
141
|
-
*/
|
|
142
|
-
async fetchIssues(route) {
|
|
143
|
-
if (!isGitHubConfigured()) {
|
|
144
|
-
console.warn("GitHub not configured. Skipping issue fetch.");
|
|
145
|
-
return [];
|
|
146
|
-
}
|
|
147
|
-
const owner = process.env.VITE_GITHUB_OWNER;
|
|
148
|
-
const repo = process.env.VITE_GITHUB_REPO;
|
|
149
|
-
try {
|
|
150
|
-
const issues = await makeGitHubRequest(
|
|
151
|
-
"GET",
|
|
152
|
-
`/repos/${owner}/${repo}/issues?state=open`
|
|
153
|
-
);
|
|
154
|
-
const filteredIssues = issues.filter((issue) => {
|
|
155
|
-
if (issue.body && issue.body.includes(`Route: \`${route}\``)) {
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
if (issue.labels && issue.labels.some((l) => {
|
|
159
|
-
const labelName = typeof l === "string" ? l : l.name;
|
|
160
|
-
return labelName === `route:${route}`;
|
|
161
|
-
})) {
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
165
|
-
});
|
|
166
|
-
console.log(`\u2705 Fetched ${filteredIssues.length} issues for route: ${route}`);
|
|
167
|
-
return filteredIssues;
|
|
168
|
-
} catch (error) {
|
|
169
|
-
console.error(`\u274C Failed to fetch issues for route ${route}:`, error);
|
|
170
|
-
return [];
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
/**
|
|
174
|
-
* Fetch all comments for a specific issue
|
|
175
|
-
*/
|
|
176
|
-
async fetchComments(issueNumber) {
|
|
177
|
-
if (!isGitHubConfigured()) {
|
|
178
|
-
return [];
|
|
179
|
-
}
|
|
180
|
-
const owner = process.env.VITE_GITHUB_OWNER;
|
|
181
|
-
const repo = process.env.VITE_GITHUB_REPO;
|
|
182
|
-
try {
|
|
183
|
-
const comments = await makeGitHubRequest(
|
|
184
|
-
"GET",
|
|
185
|
-
`/repos/${owner}/${repo}/issues/${issueNumber}/comments`
|
|
186
|
-
);
|
|
187
|
-
console.log(`\u2705 Fetched ${comments.length} comments for Issue #${issueNumber}`);
|
|
188
|
-
return comments;
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.error(`\u274C Failed to fetch comments for Issue #${issueNumber}:`, error);
|
|
191
|
-
return [];
|
|
192
|
-
}
|
|
193
|
-
},
|
|
194
|
-
/**
|
|
195
|
-
* Close a GitHub Issue (when deleting a thread)
|
|
196
|
-
*/
|
|
197
|
-
async closeIssue(issueNumber) {
|
|
198
|
-
if (!isGitHubConfigured()) {
|
|
199
|
-
return { success: false, error: "Please sign in with GitHub" };
|
|
200
|
-
}
|
|
201
|
-
const owner = process.env.VITE_GITHUB_OWNER;
|
|
202
|
-
const repo = process.env.VITE_GITHUB_REPO;
|
|
203
|
-
try {
|
|
204
|
-
const issueData = await makeGitHubRequest(
|
|
205
|
-
"PATCH",
|
|
206
|
-
`/repos/${owner}/${repo}/issues/${issueNumber}`,
|
|
207
|
-
{ state: "closed" }
|
|
208
|
-
);
|
|
209
|
-
console.log(`\u2705 Closed Issue #${issueNumber}`);
|
|
210
|
-
return { success: true, data: issueData };
|
|
211
|
-
} catch (error) {
|
|
212
|
-
const errorMessage = error?.response?.data?.message || error?.message || "Failed to close issue";
|
|
213
|
-
console.error(`\u274C Failed to close Issue #${issueNumber}:`, errorMessage);
|
|
214
|
-
return { success: false, error: errorMessage };
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
/**
|
|
218
|
-
* Update an existing comment on a GitHub Issue
|
|
219
|
-
*/
|
|
220
|
-
async updateComment(commentId, body) {
|
|
221
|
-
if (!isGitHubConfigured()) {
|
|
222
|
-
return { success: false, error: "Please sign in with GitHub" };
|
|
223
|
-
}
|
|
224
|
-
const owner = process.env.VITE_GITHUB_OWNER;
|
|
225
|
-
const repo = process.env.VITE_GITHUB_REPO;
|
|
226
|
-
try {
|
|
227
|
-
const commentData = await makeGitHubRequest(
|
|
228
|
-
"PATCH",
|
|
229
|
-
`/repos/${owner}/${repo}/issues/comments/${commentId}`,
|
|
230
|
-
{ body }
|
|
231
|
-
);
|
|
232
|
-
console.log(`\u2705 Updated comment #${commentId}`);
|
|
233
|
-
return { success: true, data: commentData };
|
|
234
|
-
} catch (error) {
|
|
235
|
-
const errorMessage = error?.response?.data?.message || error?.message || "Failed to update comment";
|
|
236
|
-
console.error(`\u274C Failed to update comment #${commentId}:`, errorMessage);
|
|
237
|
-
return { success: false, error: errorMessage };
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
/**
|
|
241
|
-
* Delete a comment on a GitHub Issue
|
|
242
|
-
*/
|
|
243
|
-
async deleteComment(commentId) {
|
|
244
|
-
if (!isGitHubConfigured()) {
|
|
245
|
-
return { success: false, error: "Please sign in with GitHub" };
|
|
246
|
-
}
|
|
247
|
-
const owner = process.env.VITE_GITHUB_OWNER;
|
|
248
|
-
const repo = process.env.VITE_GITHUB_REPO;
|
|
249
|
-
try {
|
|
250
|
-
await makeGitHubRequest(
|
|
251
|
-
"DELETE",
|
|
252
|
-
`/repos/${owner}/${repo}/issues/comments/${commentId}`
|
|
253
|
-
);
|
|
254
|
-
console.log(`\u2705 Deleted comment #${commentId}`);
|
|
255
|
-
return { success: true };
|
|
256
|
-
} catch (error) {
|
|
257
|
-
const errorMessage = error?.response?.data?.message || error?.message || "Failed to delete comment";
|
|
258
|
-
console.error(`\u274C Failed to delete comment #${commentId}:`, errorMessage);
|
|
259
|
-
return { success: false, error: errorMessage };
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
// src/services/gitlabAdapter.ts
|
|
265
|
-
import axios2 from "axios";
|
|
266
|
-
var getApiBase2 = () => {
|
|
267
|
-
if (typeof window === "undefined") return "/api/gitlab-api";
|
|
268
|
-
const isNetlify = window.location.hostname.includes("netlify.app");
|
|
269
|
-
return isNetlify ? "/.netlify/functions/gitlab-api" : "/api/gitlab-api";
|
|
270
|
-
};
|
|
271
|
-
var getStoredToken2 = () => {
|
|
272
|
-
if (typeof window === "undefined") return null;
|
|
273
|
-
return localStorage.getItem("gitlab_access_token");
|
|
274
|
-
};
|
|
275
|
-
var isGitLabConfigured = () => {
|
|
276
|
-
const token = getStoredToken2();
|
|
277
|
-
const projectPath = process.env.VITE_GITLAB_PROJECT_PATH;
|
|
278
|
-
const baseUrl = process.env.VITE_GITLAB_BASE_URL || "https://gitlab.com";
|
|
279
|
-
return !!(token && projectPath && baseUrl);
|
|
280
|
-
};
|
|
281
|
-
async function makeGitLabRequest(method, endpoint, data) {
|
|
282
|
-
const token = getStoredToken2();
|
|
283
|
-
if (!token) {
|
|
284
|
-
throw new Error("Not authenticated with GitLab");
|
|
285
|
-
}
|
|
286
|
-
const baseUrl = process.env.VITE_GITLAB_BASE_URL || "https://gitlab.com";
|
|
287
|
-
const response = await axios2.post(getApiBase2(), {
|
|
288
|
-
token,
|
|
289
|
-
method,
|
|
290
|
-
endpoint,
|
|
291
|
-
data,
|
|
292
|
-
baseUrl
|
|
293
|
-
});
|
|
294
|
-
return response.data;
|
|
295
|
-
}
|
|
296
|
-
var encodeProject = (p) => encodeURIComponent(p);
|
|
297
|
-
var gitlabAdapter = {
|
|
298
|
-
async createIssue(title, body, route, x, y, version) {
|
|
299
|
-
if (!isGitLabConfigured()) {
|
|
300
|
-
return { success: false, error: "Please sign in with GitLab" };
|
|
301
|
-
}
|
|
302
|
-
try {
|
|
303
|
-
const projectPath = process.env.VITE_GITLAB_PROJECT_PATH;
|
|
304
|
-
const labels = ["apollo-comment", `route:${route}`, `coords:${Math.round(x)},${Math.round(y)}`];
|
|
305
|
-
if (version) labels.push(`version:${version}`);
|
|
306
|
-
const issue = await makeGitLabRequest(
|
|
307
|
-
"POST",
|
|
308
|
-
`/projects/${encodeProject(projectPath)}/issues`,
|
|
309
|
-
{ title, description: body, labels: labels.join(",") }
|
|
310
|
-
);
|
|
311
|
-
return { success: true, data: { ...issue, number: issue.iid } };
|
|
312
|
-
} catch (error) {
|
|
313
|
-
const message = error?.response?.data?.message || error?.message || "Failed to create GitLab issue";
|
|
314
|
-
return { success: false, error: message };
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
async createComment(issueNumber, body) {
|
|
318
|
-
if (!isGitLabConfigured()) {
|
|
319
|
-
return { success: false, error: "Please sign in with GitLab" };
|
|
320
|
-
}
|
|
321
|
-
try {
|
|
322
|
-
const projectPath = process.env.VITE_GITLAB_PROJECT_PATH;
|
|
323
|
-
const note = await makeGitLabRequest(
|
|
324
|
-
"POST",
|
|
325
|
-
`/projects/${encodeProject(projectPath)}/issues/${issueNumber}/notes`,
|
|
326
|
-
{ body }
|
|
327
|
-
);
|
|
328
|
-
return { success: true, data: note };
|
|
329
|
-
} catch (error) {
|
|
330
|
-
const message = error?.response?.data?.message || error?.message || "Failed to create GitLab comment";
|
|
331
|
-
return { success: false, error: message };
|
|
332
|
-
}
|
|
333
|
-
},
|
|
334
|
-
async updateComment(commentId, body) {
|
|
335
|
-
if (!isGitLabConfigured()) {
|
|
336
|
-
return { success: false, error: "Please sign in with GitLab" };
|
|
337
|
-
}
|
|
338
|
-
try {
|
|
339
|
-
const projectPath = process.env.VITE_GITLAB_PROJECT_PATH;
|
|
340
|
-
const note = await makeGitLabRequest(
|
|
341
|
-
"PUT",
|
|
342
|
-
`/projects/${encodeProject(projectPath)}/notes/${commentId}`,
|
|
343
|
-
{ body }
|
|
344
|
-
);
|
|
345
|
-
return { success: true, data: note };
|
|
346
|
-
} catch (error) {
|
|
347
|
-
const message = error?.response?.data?.message || error?.message || "Failed to update GitLab comment";
|
|
348
|
-
return { success: false, error: message };
|
|
349
|
-
}
|
|
350
|
-
},
|
|
351
|
-
async deleteComment(commentId) {
|
|
352
|
-
if (!isGitLabConfigured()) {
|
|
353
|
-
return { success: false, error: "Please sign in with GitLab" };
|
|
354
|
-
}
|
|
355
|
-
try {
|
|
356
|
-
const projectPath = process.env.VITE_GITLAB_PROJECT_PATH;
|
|
357
|
-
await makeGitLabRequest(
|
|
358
|
-
"DELETE",
|
|
359
|
-
`/projects/${encodeProject(projectPath)}/notes/${commentId}`
|
|
360
|
-
);
|
|
361
|
-
return { success: true };
|
|
362
|
-
} catch (error) {
|
|
363
|
-
const message = error?.response?.data?.message || error?.message || "Failed to delete GitLab comment";
|
|
364
|
-
return { success: false, error: message };
|
|
365
|
-
}
|
|
366
|
-
},
|
|
367
|
-
async closeIssue(issueNumber) {
|
|
368
|
-
if (!isGitLabConfigured()) {
|
|
369
|
-
return { success: false, error: "Please sign in with GitLab" };
|
|
370
|
-
}
|
|
371
|
-
try {
|
|
372
|
-
const projectPath = process.env.VITE_GITLAB_PROJECT_PATH;
|
|
373
|
-
const issue = await makeGitLabRequest(
|
|
374
|
-
"PUT",
|
|
375
|
-
`/projects/${encodeProject(projectPath)}/issues/${issueNumber}`,
|
|
376
|
-
{ state_event: "close" }
|
|
377
|
-
);
|
|
378
|
-
return { success: true, data: issue };
|
|
379
|
-
} catch (error) {
|
|
380
|
-
const message = error?.response?.data?.message || error?.message || "Failed to close GitLab issue";
|
|
381
|
-
return { success: false, error: message };
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
async fetchIssues(route) {
|
|
385
|
-
if (!isGitLabConfigured()) {
|
|
386
|
-
return [];
|
|
387
|
-
}
|
|
388
|
-
try {
|
|
389
|
-
const projectPath = process.env.VITE_GITLAB_PROJECT_PATH;
|
|
390
|
-
const issues = await makeGitLabRequest(
|
|
391
|
-
"GET",
|
|
392
|
-
`/projects/${encodeProject(projectPath)}/issues?state=opened&per_page=100`
|
|
393
|
-
);
|
|
394
|
-
return (issues || []).filter((issue) => {
|
|
395
|
-
const labels = issue.labels || [];
|
|
396
|
-
const routeLabel = labels.some((l) => l === `route:${route}`);
|
|
397
|
-
const inBody = (issue.description || "").includes(`Route: \`${route}\``);
|
|
398
|
-
return routeLabel || inBody;
|
|
399
|
-
});
|
|
400
|
-
} catch {
|
|
401
|
-
return [];
|
|
402
|
-
}
|
|
403
|
-
},
|
|
404
|
-
async fetchComments(issueNumber) {
|
|
405
|
-
if (!isGitLabConfigured()) {
|
|
406
|
-
return [];
|
|
407
|
-
}
|
|
408
|
-
try {
|
|
409
|
-
const projectPath = process.env.VITE_GITLAB_PROJECT_PATH;
|
|
410
|
-
const notes = await makeGitLabRequest(
|
|
411
|
-
"GET",
|
|
412
|
-
`/projects/${encodeProject(projectPath)}/issues/${issueNumber}/notes?per_page=100`
|
|
413
|
-
);
|
|
414
|
-
return notes || [];
|
|
415
|
-
} catch {
|
|
416
|
-
return [];
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
// src/contexts/CommentContext.tsx
|
|
422
7
|
import { jsx } from "react/jsx-runtime";
|
|
423
8
|
var CommentContext = React.createContext(void 0);
|
|
424
|
-
var STORAGE_KEY = "
|
|
425
|
-
var SHOW_PINS_KEY = "
|
|
426
|
-
var ENABLE_COMMENTING_KEY = "
|
|
9
|
+
var STORAGE_KEY = "hale-threads";
|
|
10
|
+
var SHOW_PINS_KEY = "hale-show-pins";
|
|
11
|
+
var ENABLE_COMMENTING_KEY = "hale-enable-commenting";
|
|
427
12
|
var migrateOldComments = () => {
|
|
428
13
|
try {
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
14
|
+
const oldThreadsKey = localStorage.getItem("apollo-threads");
|
|
15
|
+
const oldCommentsKey = localStorage.getItem("apollo-comments");
|
|
16
|
+
if (oldThreadsKey) {
|
|
17
|
+
const parsed = JSON.parse(oldThreadsKey);
|
|
18
|
+
const cleanThreads = parsed.map((t) => ({
|
|
19
|
+
id: t.id,
|
|
20
|
+
x: t.x,
|
|
21
|
+
y: t.y,
|
|
22
|
+
route: t.route,
|
|
23
|
+
comments: t.comments.map((c) => ({
|
|
24
|
+
id: c.id,
|
|
25
|
+
text: c.text || "",
|
|
26
|
+
createdAt: c.createdAt,
|
|
27
|
+
author: c.author
|
|
28
|
+
})),
|
|
29
|
+
version: t.version
|
|
30
|
+
}));
|
|
31
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(cleanThreads));
|
|
32
|
+
localStorage.removeItem("apollo-threads");
|
|
33
|
+
return cleanThreads;
|
|
34
|
+
}
|
|
35
|
+
if (oldCommentsKey) {
|
|
36
|
+
const parsed = JSON.parse(oldCommentsKey);
|
|
432
37
|
const threads = parsed.map((oldComment) => ({
|
|
433
38
|
id: oldComment.id,
|
|
434
39
|
x: oldComment.x,
|
|
@@ -467,7 +72,9 @@ var CommentProvider = ({ children }) => {
|
|
|
467
72
|
const [showPins, setShowPins] = React.useState(() => {
|
|
468
73
|
try {
|
|
469
74
|
const stored = localStorage.getItem(SHOW_PINS_KEY);
|
|
470
|
-
return stored === "true";
|
|
75
|
+
if (stored !== null) return stored === "true";
|
|
76
|
+
const oldKey = localStorage.getItem("apollo-show-pins");
|
|
77
|
+
return oldKey === "true";
|
|
471
78
|
} catch (error) {
|
|
472
79
|
return false;
|
|
473
80
|
}
|
|
@@ -475,12 +82,13 @@ var CommentProvider = ({ children }) => {
|
|
|
475
82
|
const [enableCommenting, setEnableCommenting] = React.useState(() => {
|
|
476
83
|
try {
|
|
477
84
|
const stored = localStorage.getItem(ENABLE_COMMENTING_KEY);
|
|
478
|
-
return stored === "true";
|
|
85
|
+
if (stored !== null) return stored === "true";
|
|
86
|
+
const oldKey = localStorage.getItem("apollo-enable-commenting");
|
|
87
|
+
return oldKey === "true";
|
|
479
88
|
} catch (error) {
|
|
480
89
|
return false;
|
|
481
90
|
}
|
|
482
91
|
});
|
|
483
|
-
const [isSyncing, setIsSyncing] = React.useState(false);
|
|
484
92
|
React.useEffect(() => {
|
|
485
93
|
try {
|
|
486
94
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(threads));
|
|
@@ -516,247 +124,36 @@ var CommentProvider = ({ children }) => {
|
|
|
516
124
|
y,
|
|
517
125
|
route,
|
|
518
126
|
comments: [],
|
|
519
|
-
// Start with no comments
|
|
520
|
-
syncStatus: "local",
|
|
521
127
|
version
|
|
522
128
|
};
|
|
523
129
|
setThreads((prev) => [...prev, newThread]);
|
|
524
|
-
if (isGitHubConfigured() || isGitLabConfigured()) {
|
|
525
|
-
(async () => {
|
|
526
|
-
setThreads((prev) => prev.map(
|
|
527
|
-
(t) => t.id === threadId ? { ...t, syncStatus: "syncing" } : t
|
|
528
|
-
));
|
|
529
|
-
const pageName = route === "/" ? "Home page" : route.split("/").filter(Boolean).join(" > ") || "Page";
|
|
530
|
-
const versionStr = version ? ` [v${version}]` : "";
|
|
531
|
-
let createdProvider;
|
|
532
|
-
let createdNumber;
|
|
533
|
-
let createError;
|
|
534
|
-
if (isGitHubConfigured()) {
|
|
535
|
-
const issue = await githubAdapter.createIssue(
|
|
536
|
-
`\u{1F4AC} ${pageName} comment${versionStr}`,
|
|
537
|
-
`Thread created on route: ${route}
|
|
538
|
-
|
|
539
|
-
Coordinates: (${Math.round(x)}, ${Math.round(y)})
|
|
540
|
-
|
|
541
|
-
**Version:** ${version || "N/A"}
|
|
542
|
-
|
|
543
|
-
(Initial comment will be added as a reply)`,
|
|
544
|
-
route,
|
|
545
|
-
x,
|
|
546
|
-
y,
|
|
547
|
-
version
|
|
548
|
-
);
|
|
549
|
-
if (issue.success && issue.data) {
|
|
550
|
-
createdProvider = "github";
|
|
551
|
-
createdNumber = issue.data.number;
|
|
552
|
-
} else {
|
|
553
|
-
createError = issue.error;
|
|
554
|
-
}
|
|
555
|
-
} else if (isGitLabConfigured()) {
|
|
556
|
-
const issue = await gitlabAdapter.createIssue(
|
|
557
|
-
`\u{1F4AC} ${pageName} comment${versionStr}`,
|
|
558
|
-
`Thread created on route: ${route}
|
|
559
|
-
|
|
560
|
-
Coordinates: (${Math.round(x)}, ${Math.round(y)})
|
|
561
|
-
|
|
562
|
-
**Version:** ${version || "N/A"}
|
|
563
|
-
|
|
564
|
-
(Initial comment will be added as a reply)`,
|
|
565
|
-
route,
|
|
566
|
-
x,
|
|
567
|
-
y,
|
|
568
|
-
version
|
|
569
|
-
);
|
|
570
|
-
if (issue.success && issue.data) {
|
|
571
|
-
createdProvider = "gitlab";
|
|
572
|
-
createdNumber = issue.data.number;
|
|
573
|
-
} else {
|
|
574
|
-
createError = issue.error;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
if (createdNumber && createdProvider) {
|
|
578
|
-
setThreads((prev) => prev.map(
|
|
579
|
-
(t) => t.id === threadId ? { ...t, issueNumber: createdNumber, provider: createdProvider, syncStatus: "synced" } : t
|
|
580
|
-
));
|
|
581
|
-
} else if (createError) {
|
|
582
|
-
setThreads((prev) => prev.map(
|
|
583
|
-
(t) => t.id === threadId ? { ...t, syncStatus: "error", syncError: createError } : t
|
|
584
|
-
));
|
|
585
|
-
}
|
|
586
|
-
})();
|
|
587
|
-
}
|
|
588
130
|
return threadId;
|
|
589
131
|
}, []);
|
|
590
|
-
const addReply = React.useCallback(
|
|
132
|
+
const addReply = React.useCallback((threadId, text) => {
|
|
591
133
|
const commentId = `${threadId}-comment-${Date.now()}`;
|
|
592
134
|
const newComment = {
|
|
593
135
|
id: commentId,
|
|
594
136
|
text,
|
|
595
137
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
596
138
|
};
|
|
597
|
-
const thread = threads.find((t) => t.id === threadId);
|
|
598
139
|
setThreads(
|
|
599
140
|
(prev) => prev.map((t) => {
|
|
600
141
|
if (t.id === threadId) {
|
|
601
142
|
return {
|
|
602
143
|
...t,
|
|
603
|
-
comments: [...t.comments, newComment]
|
|
604
|
-
syncStatus: "pending"
|
|
605
|
-
// Mark as pending sync
|
|
144
|
+
comments: [...t.comments, newComment]
|
|
606
145
|
};
|
|
607
146
|
}
|
|
608
147
|
return t;
|
|
609
148
|
})
|
|
610
149
|
);
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
console.log("\u{1F535} Thread has no issue number, creating remote issue first...");
|
|
614
|
-
setThreads((prev) => prev.map(
|
|
615
|
-
(t) => t.id === threadId ? { ...t, syncStatus: "syncing" } : t
|
|
616
|
-
));
|
|
617
|
-
const pageName = thread.route === "/" ? "Home page" : thread.route.split("/").filter(Boolean).join(" > ") || "Page";
|
|
618
|
-
const versionStr = thread.version ? ` [v${thread.version}]` : "";
|
|
619
|
-
let createdProvider;
|
|
620
|
-
let createdNumber;
|
|
621
|
-
const providerPref = thread.provider || (isGitHubConfigured() ? "github" : isGitLabConfigured() ? "gitlab" : void 0);
|
|
622
|
-
if (providerPref === "github" && isGitHubConfigured()) {
|
|
623
|
-
const issue = await githubAdapter.createIssue(
|
|
624
|
-
`\u{1F4AC} ${pageName} comment${versionStr}`,
|
|
625
|
-
`Thread created on route: ${thread.route}
|
|
626
|
-
|
|
627
|
-
Coordinates: (${Math.round(thread.x)}, ${Math.round(thread.y)})
|
|
628
|
-
|
|
629
|
-
**Version:** ${thread.version || "N/A"}`,
|
|
630
|
-
thread.route,
|
|
631
|
-
thread.x,
|
|
632
|
-
thread.y,
|
|
633
|
-
thread.version
|
|
634
|
-
);
|
|
635
|
-
if (issue.success && issue.data) {
|
|
636
|
-
createdProvider = "github";
|
|
637
|
-
createdNumber = issue.data.number;
|
|
638
|
-
} else {
|
|
639
|
-
console.error("\u274C Failed to create GitHub issue:", issue.error);
|
|
640
|
-
}
|
|
641
|
-
} else if (providerPref === "gitlab" && isGitLabConfigured()) {
|
|
642
|
-
const issue = await gitlabAdapter.createIssue(
|
|
643
|
-
`\u{1F4AC} ${pageName} comment${versionStr}`,
|
|
644
|
-
`Thread created on route: ${thread.route}
|
|
645
|
-
|
|
646
|
-
Coordinates: (${Math.round(thread.x)}, ${Math.round(thread.y)})
|
|
647
|
-
|
|
648
|
-
**Version:** ${thread.version || "N/A"}`,
|
|
649
|
-
thread.route,
|
|
650
|
-
thread.x,
|
|
651
|
-
thread.y,
|
|
652
|
-
thread.version
|
|
653
|
-
);
|
|
654
|
-
if (issue.success && issue.data) {
|
|
655
|
-
createdProvider = "gitlab";
|
|
656
|
-
createdNumber = issue.data.number;
|
|
657
|
-
} else {
|
|
658
|
-
console.error("\u274C Failed to create GitLab issue:", issue.error);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
if (createdNumber && createdProvider) {
|
|
662
|
-
console.log("\u2705 Created remote issue #", createdNumber);
|
|
663
|
-
setThreads((prev) => prev.map(
|
|
664
|
-
(t) => t.id === threadId ? { ...t, issueNumber: createdNumber, provider: createdProvider, syncStatus: "pending" } : t
|
|
665
|
-
));
|
|
666
|
-
const updatedThread = threads.find((t) => t.id === threadId);
|
|
667
|
-
if (updatedThread) {
|
|
668
|
-
for (const comment of updatedThread.comments) {
|
|
669
|
-
if (!comment.githubCommentId) {
|
|
670
|
-
const commentResult = createdProvider === "github" ? await githubAdapter.createComment(createdNumber, comment.text) : await gitlabAdapter.createComment(createdNumber, comment.text);
|
|
671
|
-
if (commentResult.success && commentResult.data) {
|
|
672
|
-
setThreads((prev) => prev.map((t) => {
|
|
673
|
-
if (t.id === threadId) {
|
|
674
|
-
return {
|
|
675
|
-
...t,
|
|
676
|
-
comments: t.comments.map(
|
|
677
|
-
(c) => c.id === comment.id ? { ...c, githubCommentId: commentResult.data.id } : c
|
|
678
|
-
)
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
return t;
|
|
682
|
-
}));
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
const result = createdProvider === "github" ? await githubAdapter.createComment(createdNumber, text) : await gitlabAdapter.createComment(createdNumber, text);
|
|
688
|
-
if (result.success && result.data) {
|
|
689
|
-
setThreads(
|
|
690
|
-
(prev) => prev.map((t) => {
|
|
691
|
-
if (t.id === threadId) {
|
|
692
|
-
return {
|
|
693
|
-
...t,
|
|
694
|
-
syncStatus: "synced",
|
|
695
|
-
comments: t.comments.map(
|
|
696
|
-
(c) => c.id === commentId ? { ...c, githubCommentId: result.data.id } : c
|
|
697
|
-
)
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
return t;
|
|
701
|
-
})
|
|
702
|
-
);
|
|
703
|
-
} else {
|
|
704
|
-
setThreads(
|
|
705
|
-
(prev) => prev.map(
|
|
706
|
-
(t) => t.id === threadId ? { ...t, syncStatus: "error", syncError: result.error } : t
|
|
707
|
-
)
|
|
708
|
-
);
|
|
709
|
-
}
|
|
710
|
-
} else {
|
|
711
|
-
setThreads((prev) => prev.map(
|
|
712
|
-
(t) => t.id === threadId ? { ...t, syncStatus: "error", syncError: "Failed to create remote issue" } : t
|
|
713
|
-
));
|
|
714
|
-
}
|
|
715
|
-
} else {
|
|
716
|
-
const isGitHub = thread.provider === "github";
|
|
717
|
-
const isGitLab = thread.provider === "gitlab";
|
|
718
|
-
let result;
|
|
719
|
-
if (isGitHub && isGitHubConfigured()) {
|
|
720
|
-
result = await githubAdapter.createComment(thread.issueNumber, text);
|
|
721
|
-
} else if (isGitLab && isGitLabConfigured()) {
|
|
722
|
-
result = await gitlabAdapter.createComment(thread.issueNumber, text);
|
|
723
|
-
} else {
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
if (result.success && result.data) {
|
|
727
|
-
setThreads(
|
|
728
|
-
(prev) => prev.map((t) => {
|
|
729
|
-
if (t.id === threadId) {
|
|
730
|
-
return {
|
|
731
|
-
...t,
|
|
732
|
-
syncStatus: "synced",
|
|
733
|
-
comments: t.comments.map(
|
|
734
|
-
(c) => c.id === commentId ? { ...c, githubCommentId: result.data.id } : c
|
|
735
|
-
)
|
|
736
|
-
};
|
|
737
|
-
}
|
|
738
|
-
return t;
|
|
739
|
-
})
|
|
740
|
-
);
|
|
741
|
-
} else {
|
|
742
|
-
setThreads(
|
|
743
|
-
(prev) => prev.map(
|
|
744
|
-
(t) => t.id === threadId ? { ...t, syncStatus: "error", syncError: result.error } : t
|
|
745
|
-
)
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
}, [threads]);
|
|
751
|
-
const updateComment = React.useCallback(async (threadId, commentId, text) => {
|
|
752
|
-
const thread = threads.find((t) => t.id === threadId);
|
|
753
|
-
const comment = thread?.comments.find((c) => c.id === commentId);
|
|
150
|
+
}, []);
|
|
151
|
+
const updateComment = React.useCallback((threadId, commentId, text) => {
|
|
754
152
|
setThreads(
|
|
755
153
|
(prev) => prev.map((t) => {
|
|
756
154
|
if (t.id === threadId) {
|
|
757
155
|
return {
|
|
758
156
|
...t,
|
|
759
|
-
syncStatus: "pending",
|
|
760
157
|
comments: t.comments.map(
|
|
761
158
|
(c) => c.id === commentId ? { ...c, text } : c
|
|
762
159
|
)
|
|
@@ -765,82 +162,23 @@ Coordinates: (${Math.round(thread.x)}, ${Math.round(thread.y)})
|
|
|
765
162
|
return t;
|
|
766
163
|
})
|
|
767
164
|
);
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
if (thread?.provider === "github" && isGitHubConfigured()) {
|
|
771
|
-
const result = await githubAdapter.updateComment(comment.githubCommentId, text);
|
|
772
|
-
ok = !!result.success;
|
|
773
|
-
} else if (thread?.provider === "gitlab" && isGitLabConfigured()) {
|
|
774
|
-
const result = await gitlabAdapter.updateComment(comment.githubCommentId, text);
|
|
775
|
-
ok = !!result.success;
|
|
776
|
-
}
|
|
777
|
-
if (ok) {
|
|
778
|
-
setThreads(
|
|
779
|
-
(prev) => prev.map((t) => t.id === threadId ? { ...t, syncStatus: "synced" } : t)
|
|
780
|
-
);
|
|
781
|
-
} else {
|
|
782
|
-
setThreads(
|
|
783
|
-
(prev) => prev.map(
|
|
784
|
-
(t) => t.id === threadId ? { ...t, syncStatus: "error", syncError: "Update failed" } : t
|
|
785
|
-
)
|
|
786
|
-
);
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
}, [threads]);
|
|
790
|
-
const deleteComment = React.useCallback(async (threadId, commentId) => {
|
|
791
|
-
const thread = threads.find((t) => t.id === threadId);
|
|
792
|
-
const comment = thread?.comments.find((c) => c.id === commentId);
|
|
165
|
+
}, []);
|
|
166
|
+
const deleteComment = React.useCallback((threadId, commentId) => {
|
|
793
167
|
setThreads(
|
|
794
168
|
(prev) => prev.map((t) => {
|
|
795
169
|
if (t.id === threadId) {
|
|
796
170
|
return {
|
|
797
171
|
...t,
|
|
798
|
-
syncStatus: "pending",
|
|
799
172
|
comments: t.comments.filter((c) => c.id !== commentId)
|
|
800
173
|
};
|
|
801
174
|
}
|
|
802
175
|
return t;
|
|
803
176
|
})
|
|
804
177
|
);
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
if (thread?.provider === "github" && isGitHubConfigured()) {
|
|
808
|
-
const result = await githubAdapter.deleteComment(comment.githubCommentId);
|
|
809
|
-
ok = !!result.success;
|
|
810
|
-
} else if (thread?.provider === "gitlab" && isGitLabConfigured()) {
|
|
811
|
-
const result = await gitlabAdapter.deleteComment(comment.githubCommentId);
|
|
812
|
-
ok = !!result.success;
|
|
813
|
-
}
|
|
814
|
-
if (ok) {
|
|
815
|
-
setThreads(
|
|
816
|
-
(prev) => prev.map((t) => t.id === threadId ? { ...t, syncStatus: "synced" } : t)
|
|
817
|
-
);
|
|
818
|
-
} else {
|
|
819
|
-
setThreads(
|
|
820
|
-
(prev) => prev.map(
|
|
821
|
-
(t) => t.id === threadId ? { ...t, syncStatus: "error", syncError: "Delete failed" } : t
|
|
822
|
-
)
|
|
823
|
-
);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
}, [threads]);
|
|
827
|
-
const deleteThread = React.useCallback(async (threadId) => {
|
|
828
|
-
const thread = threads.find((t) => t.id === threadId);
|
|
178
|
+
}, []);
|
|
179
|
+
const deleteThread = React.useCallback((threadId) => {
|
|
829
180
|
setThreads((prev) => prev.filter((t) => t.id !== threadId));
|
|
830
|
-
|
|
831
|
-
if (thread.provider === "github" && isGitHubConfigured()) {
|
|
832
|
-
const result = await githubAdapter.closeIssue(thread.issueNumber);
|
|
833
|
-
if (!result.success) {
|
|
834
|
-
console.error("Failed to close GitHub issue:", result.error);
|
|
835
|
-
}
|
|
836
|
-
} else if (thread.provider === "gitlab" && isGitLabConfigured()) {
|
|
837
|
-
const result = await gitlabAdapter.closeIssue(thread.issueNumber);
|
|
838
|
-
if (!result.success) {
|
|
839
|
-
console.error("Failed to close GitLab issue:", result.error);
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}, [threads]);
|
|
181
|
+
}, []);
|
|
844
182
|
const clearAllThreads = React.useCallback(() => {
|
|
845
183
|
setThreads([]);
|
|
846
184
|
}, []);
|
|
@@ -852,211 +190,6 @@ Coordinates: (${Math.round(thread.x)}, ${Math.round(thread.y)})
|
|
|
852
190
|
return routeMatch && versionMatch;
|
|
853
191
|
});
|
|
854
192
|
}, [threads]);
|
|
855
|
-
const syncFromGitHub = React.useCallback(async (route) => {
|
|
856
|
-
setIsSyncing(true);
|
|
857
|
-
console.log(`\u{1F504} Syncing threads from remote providers for route: ${route}`);
|
|
858
|
-
try {
|
|
859
|
-
const newThreads = [];
|
|
860
|
-
if (isGitHubConfigured()) {
|
|
861
|
-
const issues = await githubAdapter.fetchIssues(route);
|
|
862
|
-
for (const issue of issues) {
|
|
863
|
-
let coords = null;
|
|
864
|
-
if (issue.body) {
|
|
865
|
-
const coordMatch = issue.body.match(/Coordinates: `\((\d+),\s*(\d+)\)`/);
|
|
866
|
-
if (coordMatch) {
|
|
867
|
-
coords = [parseInt(coordMatch[1]), parseInt(coordMatch[2])];
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
if (!coords) {
|
|
871
|
-
const coordLabel = issue.labels.find(
|
|
872
|
-
(l) => typeof l === "string" ? l.startsWith("coords:") : l.name?.startsWith("coords:")
|
|
873
|
-
);
|
|
874
|
-
const coordString = typeof coordLabel === "string" ? coordLabel : coordLabel?.name;
|
|
875
|
-
if (coordString) {
|
|
876
|
-
const coordParts = coordString.replace("coords:", "").split(",").map(Number);
|
|
877
|
-
if (coordParts.length === 2) {
|
|
878
|
-
coords = coordParts;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
if (!coords || coords.length !== 2 || isNaN(coords[0]) || isNaN(coords[1])) {
|
|
883
|
-
console.warn(`Skipping issue #${issue.number}: invalid or missing coords`);
|
|
884
|
-
continue;
|
|
885
|
-
}
|
|
886
|
-
const ghComments = await githubAdapter.fetchComments(issue.number);
|
|
887
|
-
const comments = [];
|
|
888
|
-
if (issue.body) {
|
|
889
|
-
comments.push({
|
|
890
|
-
id: `issue-${issue.number}-body`,
|
|
891
|
-
text: issue.body,
|
|
892
|
-
createdAt: issue.created_at,
|
|
893
|
-
author: issue.user?.login,
|
|
894
|
-
githubCommentId: void 0
|
|
895
|
-
// Body is not a comment
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
ghComments.forEach((ghComment) => {
|
|
899
|
-
comments.push({
|
|
900
|
-
id: `comment-${ghComment.id}`,
|
|
901
|
-
text: ghComment.body,
|
|
902
|
-
createdAt: ghComment.created_at,
|
|
903
|
-
author: ghComment.user?.login,
|
|
904
|
-
githubCommentId: ghComment.id
|
|
905
|
-
});
|
|
906
|
-
});
|
|
907
|
-
newThreads.push({
|
|
908
|
-
id: `github-${issue.number}`,
|
|
909
|
-
x: coords[0],
|
|
910
|
-
y: coords[1],
|
|
911
|
-
route,
|
|
912
|
-
comments,
|
|
913
|
-
issueNumber: issue.number,
|
|
914
|
-
provider: "github",
|
|
915
|
-
syncStatus: "synced"
|
|
916
|
-
});
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
if (isGitLabConfigured()) {
|
|
920
|
-
const issues = await gitlabAdapter.fetchIssues(route);
|
|
921
|
-
for (const issue of issues) {
|
|
922
|
-
let coords = null;
|
|
923
|
-
const labels = issue.labels || [];
|
|
924
|
-
const coordLabel = labels.find((l) => l.startsWith("coords:"));
|
|
925
|
-
if (coordLabel) {
|
|
926
|
-
const parts = coordLabel.replace("coords:", "").split(",").map((n) => parseInt(n, 10));
|
|
927
|
-
if (parts.length === 2 && !Number.isNaN(parts[0]) && !Number.isNaN(parts[1])) {
|
|
928
|
-
coords = parts;
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
if (!coords && issue.description) {
|
|
932
|
-
const m = issue.description.match(/Coordinates:\s*`?\((\d+),\s*(\d+)\)`?/);
|
|
933
|
-
if (m) coords = [parseInt(m[1], 10), parseInt(m[2], 10)];
|
|
934
|
-
}
|
|
935
|
-
if (!coords) {
|
|
936
|
-
console.warn(`Skipping GitLab issue #${issue.iid}: invalid or missing coords`);
|
|
937
|
-
continue;
|
|
938
|
-
}
|
|
939
|
-
const glComments = await gitlabAdapter.fetchComments(issue.iid);
|
|
940
|
-
const comments = [];
|
|
941
|
-
if (issue.description) {
|
|
942
|
-
comments.push({
|
|
943
|
-
id: `issue-${issue.iid}-body`,
|
|
944
|
-
text: issue.description,
|
|
945
|
-
createdAt: issue.created_at,
|
|
946
|
-
author: issue.author?.username,
|
|
947
|
-
githubCommentId: void 0
|
|
948
|
-
});
|
|
949
|
-
}
|
|
950
|
-
glComments.forEach((note) => {
|
|
951
|
-
comments.push({
|
|
952
|
-
id: `comment-${note.id}`,
|
|
953
|
-
text: note.body,
|
|
954
|
-
createdAt: note.created_at,
|
|
955
|
-
author: note.author?.username,
|
|
956
|
-
githubCommentId: note.id
|
|
957
|
-
});
|
|
958
|
-
});
|
|
959
|
-
newThreads.push({
|
|
960
|
-
id: `gitlab-${issue.iid}`,
|
|
961
|
-
x: coords[0],
|
|
962
|
-
y: coords[1],
|
|
963
|
-
route,
|
|
964
|
-
comments,
|
|
965
|
-
issueNumber: issue.iid,
|
|
966
|
-
provider: "gitlab",
|
|
967
|
-
syncStatus: "synced"
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
setThreads((prev) => {
|
|
972
|
-
const localOnlyThreads = prev.filter((t) => t.route === route && !t.issueNumber);
|
|
973
|
-
const mergedThreads = [
|
|
974
|
-
...prev.filter((t) => t.route !== route),
|
|
975
|
-
// Keep threads from other routes
|
|
976
|
-
...newThreads,
|
|
977
|
-
// Add synced threads
|
|
978
|
-
...localOnlyThreads
|
|
979
|
-
// Keep local-only threads
|
|
980
|
-
];
|
|
981
|
-
return mergedThreads;
|
|
982
|
-
});
|
|
983
|
-
console.log(`\u2705 Synced ${newThreads.length} threads from providers`);
|
|
984
|
-
} catch (error) {
|
|
985
|
-
console.error("Failed to sync from providers:", error);
|
|
986
|
-
} finally {
|
|
987
|
-
setIsSyncing(false);
|
|
988
|
-
}
|
|
989
|
-
}, []);
|
|
990
|
-
const retrySync = React.useCallback(async () => {
|
|
991
|
-
console.log("\u{1F504} Retrying sync for pending/error threads...");
|
|
992
|
-
const threadsToSync = threads.filter(
|
|
993
|
-
(t) => (t.syncStatus === "pending" || t.syncStatus === "error") && !t.issueNumber
|
|
994
|
-
);
|
|
995
|
-
if (threadsToSync.length === 0) {
|
|
996
|
-
console.log("No threads to sync");
|
|
997
|
-
return;
|
|
998
|
-
}
|
|
999
|
-
if (!isGitHubConfigured()) {
|
|
1000
|
-
console.warn("GitHub not configured. Cannot retry sync.");
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
for (const thread of threadsToSync) {
|
|
1004
|
-
console.log(`\u{1F535} Syncing thread ${thread.id}...`);
|
|
1005
|
-
setThreads((prev) => prev.map(
|
|
1006
|
-
(t) => t.id === thread.id ? { ...t, syncStatus: "syncing" } : t
|
|
1007
|
-
));
|
|
1008
|
-
const pageName = thread.route === "/" ? "Home page" : thread.route.split("/").filter(Boolean).join(" > ") || "Page";
|
|
1009
|
-
const versionStr = thread.version ? ` [v${thread.version}]` : "";
|
|
1010
|
-
const issue = await githubAdapter.createIssue(
|
|
1011
|
-
`\u{1F4AC} ${pageName} comment${versionStr}`,
|
|
1012
|
-
`Thread created on route: ${thread.route}
|
|
1013
|
-
|
|
1014
|
-
Coordinates: (${Math.round(thread.x)}, ${Math.round(thread.y)})
|
|
1015
|
-
|
|
1016
|
-
**Version:** ${thread.version || "N/A"}`,
|
|
1017
|
-
thread.route,
|
|
1018
|
-
thread.x,
|
|
1019
|
-
thread.y,
|
|
1020
|
-
thread.version
|
|
1021
|
-
);
|
|
1022
|
-
if (issue.success && issue.data) {
|
|
1023
|
-
console.log("\u2705 Created GitHub issue #", issue.data.number);
|
|
1024
|
-
setThreads((prev) => prev.map(
|
|
1025
|
-
(t) => t.id === thread.id ? { ...t, issueNumber: issue.data.number } : t
|
|
1026
|
-
));
|
|
1027
|
-
for (const comment of thread.comments) {
|
|
1028
|
-
if (!comment.githubCommentId) {
|
|
1029
|
-
const commentResult = await githubAdapter.createComment(issue.data.number, comment.text);
|
|
1030
|
-
if (commentResult.success && commentResult.data) {
|
|
1031
|
-
setThreads((prev) => prev.map((t) => {
|
|
1032
|
-
if (t.id === thread.id) {
|
|
1033
|
-
return {
|
|
1034
|
-
...t,
|
|
1035
|
-
comments: t.comments.map(
|
|
1036
|
-
(c) => c.id === comment.id ? { ...c, githubCommentId: commentResult.data.id } : c
|
|
1037
|
-
)
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
return t;
|
|
1041
|
-
}));
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
setThreads((prev) => prev.map(
|
|
1046
|
-
(t) => t.id === thread.id ? { ...t, syncStatus: "synced" } : t
|
|
1047
|
-
));
|
|
1048
|
-
} else {
|
|
1049
|
-
console.error("\u274C Failed to create GitHub issue:", issue.error);
|
|
1050
|
-
setThreads((prev) => prev.map(
|
|
1051
|
-
(t) => t.id === thread.id ? { ...t, syncStatus: "error", syncError: issue.error } : t
|
|
1052
|
-
));
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
console.log("\u2705 Retry sync complete");
|
|
1056
|
-
}, [threads]);
|
|
1057
|
-
const hasPendingSync = React.useMemo(() => {
|
|
1058
|
-
return threads.some((t) => t.syncStatus === "pending" || t.syncStatus === "error");
|
|
1059
|
-
}, [threads]);
|
|
1060
193
|
const value = React.useMemo(
|
|
1061
194
|
() => ({
|
|
1062
195
|
threads,
|
|
@@ -1070,13 +203,9 @@ Coordinates: (${Math.round(thread.x)}, ${Math.round(thread.y)})
|
|
|
1070
203
|
deleteComment,
|
|
1071
204
|
deleteThread,
|
|
1072
205
|
clearAllThreads,
|
|
1073
|
-
getThreadsForRoute
|
|
1074
|
-
syncFromGitHub,
|
|
1075
|
-
retrySync,
|
|
1076
|
-
isSyncing,
|
|
1077
|
-
hasPendingSync
|
|
206
|
+
getThreadsForRoute
|
|
1078
207
|
}),
|
|
1079
|
-
[threads, showPins, enableCommenting, toggleShowPins, toggleEnableCommenting, addThread, addReply, updateComment, deleteComment, deleteThread, clearAllThreads, getThreadsForRoute
|
|
208
|
+
[threads, showPins, enableCommenting, toggleShowPins, toggleEnableCommenting, addThread, addReply, updateComment, deleteComment, deleteThread, clearAllThreads, getThreadsForRoute]
|
|
1080
209
|
);
|
|
1081
210
|
return /* @__PURE__ */ jsx(CommentContext.Provider, { value, children });
|
|
1082
211
|
};
|
|
@@ -1092,7 +221,7 @@ var useComments = () => {
|
|
|
1092
221
|
import * as React2 from "react";
|
|
1093
222
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1094
223
|
var VersionContext = React2.createContext(void 0);
|
|
1095
|
-
var VERSION_STORAGE_KEY = "
|
|
224
|
+
var VERSION_STORAGE_KEY = "hale-current-version";
|
|
1096
225
|
var VersionProvider = ({ children }) => {
|
|
1097
226
|
const [currentVersion, setCurrentVersionState] = React2.useState(() => {
|
|
1098
227
|
try {
|
|
@@ -1132,7 +261,7 @@ var useVersion = () => {
|
|
|
1132
261
|
|
|
1133
262
|
// src/components/CommentPin.tsx
|
|
1134
263
|
import { Button } from "@patternfly/react-core";
|
|
1135
|
-
import { CommentIcon
|
|
264
|
+
import { CommentIcon } from "@patternfly/react-icons";
|
|
1136
265
|
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
1137
266
|
var CommentPin = ({
|
|
1138
267
|
thread,
|
|
@@ -1140,11 +269,6 @@ var CommentPin = ({
|
|
|
1140
269
|
isSelected = false
|
|
1141
270
|
}) => {
|
|
1142
271
|
const commentCount = thread.comments.length;
|
|
1143
|
-
const isPending = thread.syncStatus === "pending" || thread.syncStatus === "syncing";
|
|
1144
|
-
const isError = thread.syncStatus === "error";
|
|
1145
|
-
const pulseAnimation = isPending ? {
|
|
1146
|
-
animation: "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite"
|
|
1147
|
-
} : {};
|
|
1148
272
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1149
273
|
/* @__PURE__ */ jsx3("style", { children: `
|
|
1150
274
|
@keyframes pulse {
|
|
@@ -1157,7 +281,7 @@ var CommentPin = ({
|
|
|
1157
281
|
{
|
|
1158
282
|
id: `comment-pin-${thread.id}`,
|
|
1159
283
|
variant: "plain",
|
|
1160
|
-
"aria-label": `Comment thread with ${commentCount} ${commentCount === 1 ? "comment" : "comments"}
|
|
284
|
+
"aria-label": `Comment thread with ${commentCount} ${commentCount === 1 ? "comment" : "comments"}`,
|
|
1161
285
|
onClick: (e) => {
|
|
1162
286
|
e.stopPropagation();
|
|
1163
287
|
onPinClick();
|
|
@@ -1170,7 +294,7 @@ var CommentPin = ({
|
|
|
1170
294
|
width: "32px",
|
|
1171
295
|
height: "32px",
|
|
1172
296
|
borderRadius: "50%",
|
|
1173
|
-
backgroundColor:
|
|
297
|
+
backgroundColor: "#C9190B",
|
|
1174
298
|
color: "white",
|
|
1175
299
|
border: isSelected ? "3px solid #0066CC" : "2px solid white",
|
|
1176
300
|
boxShadow: isSelected ? "0 0 0 2px #0066CC, 0 4px 12px rgba(0, 0, 0, 0.4)" : "0 2px 8px rgba(0, 0, 0, 0.3)",
|
|
@@ -1181,10 +305,9 @@ var CommentPin = ({
|
|
|
1181
305
|
cursor: "pointer",
|
|
1182
306
|
zIndex: isSelected ? 1001 : 1e3,
|
|
1183
307
|
transition: "all 0.2s ease",
|
|
1184
|
-
fontSize: commentCount > 1 ? "0.7rem" : void 0
|
|
1185
|
-
...pulseAnimation
|
|
308
|
+
fontSize: commentCount > 1 ? "0.7rem" : void 0
|
|
1186
309
|
},
|
|
1187
|
-
children:
|
|
310
|
+
children: commentCount === 0 ? /* @__PURE__ */ jsx3("span", { style: { fontWeight: "bold", fontSize: "0.75rem" }, children: "0" }) : commentCount === 1 ? /* @__PURE__ */ jsx3(CommentIcon, {}) : /* @__PURE__ */ jsx3("span", { style: { fontWeight: "bold" }, children: commentCount })
|
|
1188
311
|
}
|
|
1189
312
|
)
|
|
1190
313
|
] });
|
|
@@ -1257,7 +380,7 @@ var CommentOverlay = ({
|
|
|
1257
380
|
};
|
|
1258
381
|
|
|
1259
382
|
// src/components/CommentDrawer.tsx
|
|
1260
|
-
import * as
|
|
383
|
+
import * as React5 from "react";
|
|
1261
384
|
import {
|
|
1262
385
|
Drawer,
|
|
1263
386
|
DrawerContent,
|
|
@@ -1275,91 +398,29 @@ import {
|
|
|
1275
398
|
EmptyState,
|
|
1276
399
|
EmptyStateBody,
|
|
1277
400
|
Divider,
|
|
1278
|
-
Label,
|
|
1279
|
-
Spinner,
|
|
1280
401
|
ExpandableSection,
|
|
1281
402
|
Alert
|
|
1282
403
|
} from "@patternfly/react-core";
|
|
1283
|
-
import { CommentIcon as CommentIcon2, TimesIcon, PlusCircleIcon,
|
|
404
|
+
import { CommentIcon as CommentIcon2, TimesIcon, PlusCircleIcon, MagicIcon } from "@patternfly/react-icons";
|
|
1284
405
|
import { useLocation as useLocation2 } from "react-router-dom";
|
|
1285
406
|
|
|
1286
407
|
// src/contexts/GitLabAuthContext.tsx
|
|
1287
408
|
import * as React4 from "react";
|
|
1288
409
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1289
410
|
var GitLabAuthContext = React4.createContext(void 0);
|
|
1290
|
-
var GITLAB_TOKEN_KEY = "gitlab_access_token";
|
|
1291
|
-
var GITLAB_USER_KEY = "gitlab_user";
|
|
1292
411
|
var GitLabAuthProvider = ({ children }) => {
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
}, []);
|
|
1304
|
-
React4.useEffect(() => {
|
|
1305
|
-
const handleCallback = () => {
|
|
1306
|
-
const hash = window.location.hash;
|
|
1307
|
-
if (hash.includes("#/auth-callback")) {
|
|
1308
|
-
const params = new URLSearchParams(hash.split("?")[1]);
|
|
1309
|
-
const token = params.get("gitlab_token");
|
|
1310
|
-
const username = params.get("gitlab_username") || void 0;
|
|
1311
|
-
const avatar = params.get("gitlab_avatar") || void 0;
|
|
1312
|
-
if (token) {
|
|
1313
|
-
localStorage.setItem(GITLAB_TOKEN_KEY, token);
|
|
1314
|
-
const u = { username, avatar };
|
|
1315
|
-
localStorage.setItem(GITLAB_USER_KEY, JSON.stringify(u));
|
|
1316
|
-
setUser(u);
|
|
1317
|
-
window.location.hash = "/";
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
};
|
|
1321
|
-
handleCallback();
|
|
1322
|
-
}, []);
|
|
1323
|
-
const getToken = React4.useCallback(() => {
|
|
1324
|
-
try {
|
|
1325
|
-
return localStorage.getItem(GITLAB_TOKEN_KEY);
|
|
1326
|
-
} catch {
|
|
1327
|
-
return null;
|
|
1328
|
-
}
|
|
1329
|
-
}, []);
|
|
1330
|
-
const login = () => {
|
|
1331
|
-
const clientId = process.env.VITE_GITLAB_CLIENT_ID;
|
|
1332
|
-
const baseUrl = process.env.VITE_GITLAB_BASE_URL || "https://gitlab.com";
|
|
1333
|
-
if (!clientId) {
|
|
1334
|
-
alert("GitLab login is not configured (missing VITE_GITLAB_CLIENT_ID).");
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
const redirectUri = `${window.location.origin}/api/auth/callback`;
|
|
1338
|
-
const scope = encodeURIComponent("read_user api");
|
|
1339
|
-
const url = `${baseUrl}/oauth/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${scope}`;
|
|
1340
|
-
window.location.href = url;
|
|
1341
|
-
};
|
|
1342
|
-
const logout = () => {
|
|
1343
|
-
try {
|
|
1344
|
-
localStorage.removeItem(GITLAB_TOKEN_KEY);
|
|
1345
|
-
localStorage.removeItem(GITLAB_USER_KEY);
|
|
1346
|
-
} finally {
|
|
1347
|
-
setUser(null);
|
|
1348
|
-
}
|
|
412
|
+
const value = {
|
|
413
|
+
user: null,
|
|
414
|
+
isAuthenticated: false,
|
|
415
|
+
login: () => {
|
|
416
|
+
console.log("GitLab login not available in local mode");
|
|
417
|
+
},
|
|
418
|
+
logout: () => {
|
|
419
|
+
console.log("GitLab logout not available in local mode");
|
|
420
|
+
},
|
|
421
|
+
getToken: () => null
|
|
1349
422
|
};
|
|
1350
|
-
return /* @__PURE__ */ jsx5(
|
|
1351
|
-
GitLabAuthContext.Provider,
|
|
1352
|
-
{
|
|
1353
|
-
value: {
|
|
1354
|
-
user,
|
|
1355
|
-
isAuthenticated: !!getToken(),
|
|
1356
|
-
login,
|
|
1357
|
-
logout,
|
|
1358
|
-
getToken
|
|
1359
|
-
},
|
|
1360
|
-
children
|
|
1361
|
-
}
|
|
1362
|
-
);
|
|
423
|
+
return /* @__PURE__ */ jsx5(GitLabAuthContext.Provider, { value, children });
|
|
1363
424
|
};
|
|
1364
425
|
var useGitLabAuth = () => {
|
|
1365
426
|
const ctx = React4.useContext(GitLabAuthContext);
|
|
@@ -1369,151 +430,8 @@ var useGitLabAuth = () => {
|
|
|
1369
430
|
return ctx;
|
|
1370
431
|
};
|
|
1371
432
|
|
|
1372
|
-
// src/contexts/AIContext.tsx
|
|
1373
|
-
import * as React5 from "react";
|
|
1374
|
-
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1375
|
-
var AIContext = React5.createContext(void 0);
|
|
1376
|
-
var STORAGE_KEY2 = "apollo-ai-chat-history";
|
|
1377
|
-
var VISIBILITY_KEY = "apollo-ai-chatbot-visible";
|
|
1378
|
-
var AIProvider = ({ children }) => {
|
|
1379
|
-
const [isChatbotVisible, setIsChatbotVisible] = React5.useState(() => {
|
|
1380
|
-
try {
|
|
1381
|
-
const stored = localStorage.getItem(VISIBILITY_KEY);
|
|
1382
|
-
return stored === "true";
|
|
1383
|
-
} catch (error) {
|
|
1384
|
-
return false;
|
|
1385
|
-
}
|
|
1386
|
-
});
|
|
1387
|
-
const [messages, setMessages] = React5.useState(() => {
|
|
1388
|
-
try {
|
|
1389
|
-
const stored = localStorage.getItem(STORAGE_KEY2);
|
|
1390
|
-
if (stored) {
|
|
1391
|
-
return JSON.parse(stored);
|
|
1392
|
-
}
|
|
1393
|
-
return [];
|
|
1394
|
-
} catch (error) {
|
|
1395
|
-
console.error("Failed to load AI chat history from localStorage:", error);
|
|
1396
|
-
return [];
|
|
1397
|
-
}
|
|
1398
|
-
});
|
|
1399
|
-
const [isLoading, setIsLoading] = React5.useState(false);
|
|
1400
|
-
React5.useEffect(() => {
|
|
1401
|
-
try {
|
|
1402
|
-
localStorage.setItem(VISIBILITY_KEY, String(isChatbotVisible));
|
|
1403
|
-
} catch (error) {
|
|
1404
|
-
console.error("Failed to save chatbot visibility to localStorage:", error);
|
|
1405
|
-
}
|
|
1406
|
-
}, [isChatbotVisible]);
|
|
1407
|
-
React5.useEffect(() => {
|
|
1408
|
-
try {
|
|
1409
|
-
localStorage.setItem(STORAGE_KEY2, JSON.stringify(messages));
|
|
1410
|
-
} catch (error) {
|
|
1411
|
-
console.error("Failed to save AI chat history to localStorage:", error);
|
|
1412
|
-
}
|
|
1413
|
-
}, [messages]);
|
|
1414
|
-
const toggleChatbot = React5.useCallback(() => {
|
|
1415
|
-
setIsChatbotVisible((prev) => !prev);
|
|
1416
|
-
}, []);
|
|
1417
|
-
const sendMessage = React5.useCallback(async (content, commentContext) => {
|
|
1418
|
-
if (!content.trim()) return;
|
|
1419
|
-
const userMessage = {
|
|
1420
|
-
id: `user-${Date.now()}`,
|
|
1421
|
-
role: "user",
|
|
1422
|
-
content: content.trim(),
|
|
1423
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1424
|
-
};
|
|
1425
|
-
setMessages((prev) => [...prev, userMessage]);
|
|
1426
|
-
setIsLoading(true);
|
|
1427
|
-
try {
|
|
1428
|
-
if (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") {
|
|
1429
|
-
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
1430
|
-
const mockResponse = `**Mock AI Response** (local dev mode)
|
|
1431
|
-
|
|
1432
|
-
I analyzed your query: "${content.trim()}"
|
|
1433
|
-
|
|
1434
|
-
**Summary:**
|
|
1435
|
-
- Found ${commentContext?.threads?.length || 0} comment threads
|
|
1436
|
-
- Version: ${commentContext?.version || "unknown"}
|
|
1437
|
-
- Page: ${commentContext?.route || "unknown"}
|
|
1438
|
-
|
|
1439
|
-
*Note: This is a mock response. Deploy to production to test the real AI.*`;
|
|
1440
|
-
const botMessage2 = {
|
|
1441
|
-
id: `bot-${Date.now()}`,
|
|
1442
|
-
role: "bot",
|
|
1443
|
-
content: mockResponse,
|
|
1444
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1445
|
-
};
|
|
1446
|
-
setMessages((prev) => [...prev, botMessage2]);
|
|
1447
|
-
return;
|
|
1448
|
-
}
|
|
1449
|
-
const response = await fetch("/api/ai-assistant", {
|
|
1450
|
-
method: "POST",
|
|
1451
|
-
headers: {
|
|
1452
|
-
"Content-Type": "application/json"
|
|
1453
|
-
},
|
|
1454
|
-
body: JSON.stringify({
|
|
1455
|
-
query: content.trim(),
|
|
1456
|
-
threads: commentContext?.threads || [],
|
|
1457
|
-
version: commentContext?.version || "unknown",
|
|
1458
|
-
route: commentContext?.route
|
|
1459
|
-
})
|
|
1460
|
-
});
|
|
1461
|
-
if (!response.ok) {
|
|
1462
|
-
const errorData = await response.json();
|
|
1463
|
-
throw new Error(errorData.message || `API error: ${response.status}`);
|
|
1464
|
-
}
|
|
1465
|
-
const data = await response.json();
|
|
1466
|
-
const botMessage = {
|
|
1467
|
-
id: `bot-${Date.now()}`,
|
|
1468
|
-
role: "bot",
|
|
1469
|
-
content: data.message || "No response received.",
|
|
1470
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1471
|
-
};
|
|
1472
|
-
setMessages((prev) => [...prev, botMessage]);
|
|
1473
|
-
} catch (error) {
|
|
1474
|
-
console.error("Failed to send message:", error);
|
|
1475
|
-
const errorMessage = {
|
|
1476
|
-
id: `bot-error-${Date.now()}`,
|
|
1477
|
-
role: "bot",
|
|
1478
|
-
content: error instanceof Error ? `Sorry, I encountered an error: ${error.message}` : "Sorry, something went wrong. Please try again.",
|
|
1479
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1480
|
-
};
|
|
1481
|
-
setMessages((prev) => [...prev, errorMessage]);
|
|
1482
|
-
} finally {
|
|
1483
|
-
setIsLoading(false);
|
|
1484
|
-
}
|
|
1485
|
-
}, []);
|
|
1486
|
-
const clearHistory = React5.useCallback(() => {
|
|
1487
|
-
setMessages([]);
|
|
1488
|
-
try {
|
|
1489
|
-
localStorage.removeItem(STORAGE_KEY2);
|
|
1490
|
-
} catch (error) {
|
|
1491
|
-
console.error("Failed to clear chat history from localStorage:", error);
|
|
1492
|
-
}
|
|
1493
|
-
}, []);
|
|
1494
|
-
const value = React5.useMemo(
|
|
1495
|
-
() => ({
|
|
1496
|
-
isChatbotVisible,
|
|
1497
|
-
messages,
|
|
1498
|
-
isLoading,
|
|
1499
|
-
toggleChatbot,
|
|
1500
|
-
sendMessage,
|
|
1501
|
-
clearHistory
|
|
1502
|
-
}),
|
|
1503
|
-
[isChatbotVisible, messages, isLoading, toggleChatbot, sendMessage, clearHistory]
|
|
1504
|
-
);
|
|
1505
|
-
return /* @__PURE__ */ jsx6(AIContext.Provider, { value, children });
|
|
1506
|
-
};
|
|
1507
|
-
var useAIContext = () => {
|
|
1508
|
-
const context = React5.useContext(AIContext);
|
|
1509
|
-
if (!context) {
|
|
1510
|
-
throw new Error("useAIContext must be used within an AIProvider");
|
|
1511
|
-
}
|
|
1512
|
-
return context;
|
|
1513
|
-
};
|
|
1514
|
-
|
|
1515
433
|
// src/components/CommentDrawer.tsx
|
|
1516
|
-
import { Fragment as Fragment2, jsx as
|
|
434
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1517
435
|
var CommentDrawer = ({
|
|
1518
436
|
children,
|
|
1519
437
|
selectedThreadId,
|
|
@@ -1526,40 +444,27 @@ var CommentDrawer = ({
|
|
|
1526
444
|
updateComment,
|
|
1527
445
|
deleteComment,
|
|
1528
446
|
deleteThread,
|
|
1529
|
-
enableCommenting
|
|
1530
|
-
syncFromGitHub,
|
|
1531
|
-
isSyncing,
|
|
1532
|
-
retrySync,
|
|
1533
|
-
hasPendingSync
|
|
447
|
+
enableCommenting
|
|
1534
448
|
} = useComments();
|
|
1535
449
|
const { currentVersion } = useVersion();
|
|
1536
450
|
const { isAuthenticated: isGitLabAuthenticated } = useGitLabAuth();
|
|
1537
|
-
const
|
|
1538
|
-
const [
|
|
1539
|
-
const [
|
|
1540
|
-
const
|
|
1541
|
-
const
|
|
1542
|
-
const [
|
|
1543
|
-
const [
|
|
1544
|
-
const [summaryExpanded, setSummaryExpanded] = React6.useState(true);
|
|
451
|
+
const [editingCommentId, setEditingCommentId] = React5.useState(null);
|
|
452
|
+
const [editText, setEditText] = React5.useState("");
|
|
453
|
+
const [replyText, setReplyText] = React5.useState("");
|
|
454
|
+
const replyTextAreaRef = React5.useRef(null);
|
|
455
|
+
const [threadSummaries, setThreadSummaries] = React5.useState({});
|
|
456
|
+
const [loadingSummary, setLoadingSummary] = React5.useState(false);
|
|
457
|
+
const [summaryExpanded, setSummaryExpanded] = React5.useState(true);
|
|
1545
458
|
const currentRouteThreads = getThreadsForRoute(location.pathname, currentVersion);
|
|
1546
459
|
const selectedThread = currentRouteThreads.find((t) => t.id === selectedThreadId);
|
|
1547
460
|
const isDrawerOpen = selectedThreadId !== null && selectedThread !== void 0;
|
|
1548
|
-
|
|
1549
|
-
React6.useEffect(() => {
|
|
461
|
+
React5.useEffect(() => {
|
|
1550
462
|
if (!isDrawerOpen || !enableCommenting) return;
|
|
1551
463
|
const timer = setTimeout(() => {
|
|
1552
464
|
replyTextAreaRef.current?.focus();
|
|
1553
465
|
}, 100);
|
|
1554
466
|
return () => clearTimeout(timer);
|
|
1555
467
|
}, [isDrawerOpen, enableCommenting, selectedThreadId]);
|
|
1556
|
-
React6.useEffect(() => {
|
|
1557
|
-
if (isGitHubConfigured() || isGitLabConfigured()) {
|
|
1558
|
-
syncFromGitHub(location.pathname).catch((err) => {
|
|
1559
|
-
console.error("Failed to sync from providers:", err);
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
}, [location.pathname]);
|
|
1563
468
|
const handleEdit = (commentId, text) => {
|
|
1564
469
|
setEditingCommentId(commentId);
|
|
1565
470
|
setEditText(text);
|
|
@@ -1581,86 +486,13 @@ var CommentDrawer = ({
|
|
|
1581
486
|
}
|
|
1582
487
|
};
|
|
1583
488
|
const handleSummarizeThread = async () => {
|
|
1584
|
-
|
|
1585
|
-
setLoadingSummary(true);
|
|
1586
|
-
setSummaryExpanded(true);
|
|
1587
|
-
try {
|
|
1588
|
-
const response = await fetch("/api/ai-assistant", {
|
|
1589
|
-
method: "POST",
|
|
1590
|
-
headers: {
|
|
1591
|
-
"Content-Type": "application/json"
|
|
1592
|
-
},
|
|
1593
|
-
body: JSON.stringify({
|
|
1594
|
-
query: `Summarize the feedback in this thread (${selectedThread.comments.length} comments)`,
|
|
1595
|
-
threads: [selectedThread],
|
|
1596
|
-
version: currentVersion || "unknown",
|
|
1597
|
-
route: location.pathname
|
|
1598
|
-
})
|
|
1599
|
-
});
|
|
1600
|
-
if (!response.ok) {
|
|
1601
|
-
throw new Error(`API error: ${response.status}`);
|
|
1602
|
-
}
|
|
1603
|
-
const data = await response.json();
|
|
1604
|
-
const summary = data.message || "No summary available.";
|
|
1605
|
-
setThreadSummaries((prev) => ({
|
|
1606
|
-
...prev,
|
|
1607
|
-
[selectedThread.id]: summary
|
|
1608
|
-
}));
|
|
1609
|
-
} catch (error) {
|
|
1610
|
-
console.error("Failed to generate summary:", error);
|
|
1611
|
-
setThreadSummaries((prev) => ({
|
|
1612
|
-
...prev,
|
|
1613
|
-
[selectedThread.id]: "Failed to generate summary. Please try again."
|
|
1614
|
-
}));
|
|
1615
|
-
} finally {
|
|
1616
|
-
setLoadingSummary(false);
|
|
1617
|
-
}
|
|
489
|
+
console.log("AI features not available in local-only mode");
|
|
1618
490
|
};
|
|
1619
491
|
const handleDeleteComment = async (threadId, commentId) => {
|
|
1620
492
|
if (window.confirm("Delete this comment?")) {
|
|
1621
493
|
await deleteComment(threadId, commentId);
|
|
1622
494
|
}
|
|
1623
495
|
};
|
|
1624
|
-
const handleSync = async () => {
|
|
1625
|
-
await syncFromGitHub(location.pathname);
|
|
1626
|
-
};
|
|
1627
|
-
const handleRetrySync = async () => {
|
|
1628
|
-
await retrySync();
|
|
1629
|
-
};
|
|
1630
|
-
const getSyncStatusLabel = (status) => {
|
|
1631
|
-
switch (status) {
|
|
1632
|
-
case "synced":
|
|
1633
|
-
return /* @__PURE__ */ jsx7(Label, { color: "green", icon: /* @__PURE__ */ jsx7(GithubIcon, {}), children: "Synced" });
|
|
1634
|
-
case "local":
|
|
1635
|
-
return /* @__PURE__ */ jsx7(Label, { color: "grey", children: "Local Only" });
|
|
1636
|
-
case "pending":
|
|
1637
|
-
return /* @__PURE__ */ jsx7(Label, { color: "blue", icon: /* @__PURE__ */ jsx7(Spinner, { size: "sm" }), children: "Pending..." });
|
|
1638
|
-
case "syncing":
|
|
1639
|
-
return /* @__PURE__ */ jsx7(Label, { color: "blue", icon: /* @__PURE__ */ jsx7(Spinner, { size: "sm" }), children: "Syncing..." });
|
|
1640
|
-
case "error":
|
|
1641
|
-
return /* @__PURE__ */ jsx7(Label, { color: "red", children: "Sync Error" });
|
|
1642
|
-
default:
|
|
1643
|
-
return null;
|
|
1644
|
-
}
|
|
1645
|
-
};
|
|
1646
|
-
const getIssueLink = (provider, issueNumber) => {
|
|
1647
|
-
if (!issueNumber || !provider) return null;
|
|
1648
|
-
try {
|
|
1649
|
-
if (provider === "gitlab") {
|
|
1650
|
-
const baseUrl = process.env.VITE_GITLAB_BASE_URL || "https://gitlab.com";
|
|
1651
|
-
const projectPath = process.env.VITE_GITLAB_PROJECT_PATH;
|
|
1652
|
-
if (!projectPath) return null;
|
|
1653
|
-
return `${baseUrl}/${projectPath}/-/issues/${issueNumber}`;
|
|
1654
|
-
} else {
|
|
1655
|
-
const owner = process.env.VITE_GITHUB_OWNER;
|
|
1656
|
-
const repo = process.env.VITE_GITHUB_REPO;
|
|
1657
|
-
if (!owner || !repo) return null;
|
|
1658
|
-
return `https://github.com/${owner}/${repo}/issues/${issueNumber}`;
|
|
1659
|
-
}
|
|
1660
|
-
} catch (error) {
|
|
1661
|
-
return null;
|
|
1662
|
-
}
|
|
1663
|
-
};
|
|
1664
496
|
const formatDate = (isoDate) => {
|
|
1665
497
|
const date = new Date(isoDate);
|
|
1666
498
|
return date.toLocaleString(void 0, {
|
|
@@ -1672,51 +504,20 @@ var CommentDrawer = ({
|
|
|
1672
504
|
};
|
|
1673
505
|
const panelContent = /* @__PURE__ */ jsxs2(DrawerPanelContent, { isResizable: true, defaultSize: "400px", minSize: "300px", children: [
|
|
1674
506
|
/* @__PURE__ */ jsxs2(DrawerHead, { children: [
|
|
1675
|
-
/* @__PURE__ */
|
|
1676
|
-
/* @__PURE__ */
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
(isGitHubConfigured() || isGitLabConfigured()) && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1681
|
-
/* @__PURE__ */ jsx7(
|
|
1682
|
-
Button2,
|
|
1683
|
-
{
|
|
1684
|
-
id: "sync-github-button",
|
|
1685
|
-
variant: "plain",
|
|
1686
|
-
size: "sm",
|
|
1687
|
-
icon: /* @__PURE__ */ jsx7(SyncAltIcon, {}),
|
|
1688
|
-
onClick: handleSync,
|
|
1689
|
-
isDisabled: isSyncing,
|
|
1690
|
-
"aria-label": "Sync with remote",
|
|
1691
|
-
title: "Sync with remote",
|
|
1692
|
-
children: isSyncing ? /* @__PURE__ */ jsx7(Spinner, { size: "sm" }) : null
|
|
1693
|
-
}
|
|
1694
|
-
),
|
|
1695
|
-
hasPendingSync && /* @__PURE__ */ jsx7(
|
|
1696
|
-
Button2,
|
|
1697
|
-
{
|
|
1698
|
-
id: "retry-sync-button",
|
|
1699
|
-
variant: "plain",
|
|
1700
|
-
size: "sm",
|
|
1701
|
-
icon: /* @__PURE__ */ jsx7(RedoIcon, {}),
|
|
1702
|
-
onClick: handleRetrySync,
|
|
1703
|
-
isDisabled: isSyncing,
|
|
1704
|
-
"aria-label": "Retry sync for pending threads",
|
|
1705
|
-
title: "Retry sync for pending threads"
|
|
1706
|
-
}
|
|
1707
|
-
)
|
|
1708
|
-
] })
|
|
1709
|
-
] }),
|
|
1710
|
-
/* @__PURE__ */ jsx7(DrawerActions, { children: /* @__PURE__ */ jsx7(DrawerCloseButton, { onClick: () => onThreadSelect(null) }) })
|
|
507
|
+
/* @__PURE__ */ jsx6("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", flex: 1 }, children: /* @__PURE__ */ jsxs2(Title, { headingLevel: "h2", size: "xl", children: [
|
|
508
|
+
/* @__PURE__ */ jsx6(CommentIcon2, { style: { marginRight: "0.5rem", color: "#C9190B" } }),
|
|
509
|
+
"Thread"
|
|
510
|
+
] }) }),
|
|
511
|
+
/* @__PURE__ */ jsx6(DrawerActions, { children: /* @__PURE__ */ jsx6(DrawerCloseButton, { onClick: () => onThreadSelect(null) }) })
|
|
1711
512
|
] }),
|
|
1712
|
-
/* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
1715
|
-
/* @__PURE__ */
|
|
513
|
+
/* @__PURE__ */ jsx6(DrawerContentBody, { style: { padding: "1rem" }, children: !selectedThread ? /* @__PURE__ */ jsxs2(EmptyState, { children: [
|
|
514
|
+
/* @__PURE__ */ jsx6(CommentIcon2, { style: { fontSize: "3rem", color: "var(--pf-v6-global--Color--200)", marginBottom: "1rem" } }),
|
|
515
|
+
/* @__PURE__ */ jsx6(Title, { headingLevel: "h3", size: "lg", children: "No thread selected" }),
|
|
516
|
+
/* @__PURE__ */ jsx6(EmptyStateBody, { children: "Click a pin to view its comments." })
|
|
1716
517
|
] }) : /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: "1rem" }, children: [
|
|
1717
|
-
/* @__PURE__ */
|
|
518
|
+
/* @__PURE__ */ jsx6(Card, { isCompact: true, children: /* @__PURE__ */ jsxs2(CardBody, { children: [
|
|
1718
519
|
/* @__PURE__ */ jsxs2("div", { style: { fontSize: "0.875rem", marginBottom: "0.5rem" }, children: [
|
|
1719
|
-
/* @__PURE__ */
|
|
520
|
+
/* @__PURE__ */ jsx6("strong", { children: "Location:" }),
|
|
1720
521
|
" (",
|
|
1721
522
|
Math.round(selectedThread.x),
|
|
1722
523
|
", ",
|
|
@@ -1724,41 +525,22 @@ var CommentDrawer = ({
|
|
|
1724
525
|
")"
|
|
1725
526
|
] }),
|
|
1726
527
|
selectedThread.version && /* @__PURE__ */ jsxs2("div", { style: { fontSize: "0.875rem", marginBottom: "0.5rem" }, children: [
|
|
1727
|
-
/* @__PURE__ */
|
|
528
|
+
/* @__PURE__ */ jsx6("strong", { children: "Version:" }),
|
|
1728
529
|
" ",
|
|
1729
530
|
selectedThread.version
|
|
1730
531
|
] }),
|
|
1731
532
|
/* @__PURE__ */ jsxs2("div", { style: { fontSize: "0.875rem", marginBottom: "0.5rem" }, children: [
|
|
1732
|
-
/* @__PURE__ */
|
|
533
|
+
/* @__PURE__ */ jsx6("strong", { children: "Comments:" }),
|
|
1733
534
|
" ",
|
|
1734
535
|
selectedThread.comments.length
|
|
1735
536
|
] }),
|
|
1736
|
-
/* @__PURE__ */
|
|
1737
|
-
/* @__PURE__ */ jsx7("strong", { style: { fontSize: "0.875rem" }, children: "Status:" }),
|
|
1738
|
-
getSyncStatusLabel(selectedThread.syncStatus)
|
|
1739
|
-
] }),
|
|
1740
|
-
selectedThread.issueNumber && /* @__PURE__ */ jsx7("div", { style: { fontSize: "0.875rem", marginBottom: "0.5rem" }, children: /* @__PURE__ */ jsxs2(
|
|
1741
|
-
"a",
|
|
1742
|
-
{
|
|
1743
|
-
href: getIssueLink(selectedThread.provider, selectedThread.issueNumber) || "#",
|
|
1744
|
-
target: "_blank",
|
|
1745
|
-
rel: "noopener noreferrer",
|
|
1746
|
-
style: { display: "inline-flex", alignItems: "center", gap: "0.25rem" },
|
|
1747
|
-
children: [
|
|
1748
|
-
/* @__PURE__ */ jsx7(GithubIcon, {}),
|
|
1749
|
-
"Issue #",
|
|
1750
|
-
selectedThread.issueNumber,
|
|
1751
|
-
/* @__PURE__ */ jsx7(ExternalLinkAltIcon, { style: { fontSize: "0.75rem" } })
|
|
1752
|
-
]
|
|
1753
|
-
}
|
|
1754
|
-
) }),
|
|
1755
|
-
selectedThread.comments.length > 0 && /* @__PURE__ */ jsx7(
|
|
537
|
+
selectedThread.comments.length > 0 && /* @__PURE__ */ jsx6(
|
|
1756
538
|
Button2,
|
|
1757
539
|
{
|
|
1758
540
|
id: `ai-summarize-thread-${selectedThread.id}`,
|
|
1759
541
|
variant: "secondary",
|
|
1760
542
|
size: "sm",
|
|
1761
|
-
icon: /* @__PURE__ */
|
|
543
|
+
icon: /* @__PURE__ */ jsx6(MagicIcon, {}),
|
|
1762
544
|
onClick: handleSummarizeThread,
|
|
1763
545
|
isLoading: loadingSummary,
|
|
1764
546
|
isDisabled: loadingSummary,
|
|
@@ -1766,26 +548,26 @@ var CommentDrawer = ({
|
|
|
1766
548
|
children: loadingSummary ? "Generating..." : "AI Summarize Thread"
|
|
1767
549
|
}
|
|
1768
550
|
),
|
|
1769
|
-
enableCommenting &&
|
|
551
|
+
enableCommenting && /* @__PURE__ */ jsx6(
|
|
1770
552
|
Button2,
|
|
1771
553
|
{
|
|
1772
554
|
id: `delete-thread-${selectedThread.id}`,
|
|
1773
555
|
variant: "danger",
|
|
1774
556
|
size: "sm",
|
|
1775
|
-
icon: /* @__PURE__ */
|
|
557
|
+
icon: /* @__PURE__ */ jsx6(TimesIcon, {}),
|
|
1776
558
|
onClick: handleDeleteThread,
|
|
1777
559
|
style: { marginTop: "0.5rem", marginLeft: selectedThread.comments.length > 0 ? "0.5rem" : "0" },
|
|
1778
560
|
children: "Delete Thread"
|
|
1779
561
|
}
|
|
1780
562
|
)
|
|
1781
563
|
] }) }),
|
|
1782
|
-
threadSummaries[selectedThread.id] && /* @__PURE__ */
|
|
564
|
+
threadSummaries[selectedThread.id] && /* @__PURE__ */ jsx6(
|
|
1783
565
|
Alert,
|
|
1784
566
|
{
|
|
1785
567
|
variant: "info",
|
|
1786
568
|
isInline: true,
|
|
1787
569
|
title: "AI Summary",
|
|
1788
|
-
actionClose: /* @__PURE__ */
|
|
570
|
+
actionClose: /* @__PURE__ */ jsx6(
|
|
1789
571
|
Button2,
|
|
1790
572
|
{
|
|
1791
573
|
variant: "plain",
|
|
@@ -1795,25 +577,25 @@ var CommentDrawer = ({
|
|
|
1795
577
|
setThreadSummaries(newSummaries);
|
|
1796
578
|
},
|
|
1797
579
|
"aria-label": "Clear summary",
|
|
1798
|
-
children: /* @__PURE__ */
|
|
580
|
+
children: /* @__PURE__ */ jsx6(TimesIcon, {})
|
|
1799
581
|
}
|
|
1800
582
|
),
|
|
1801
|
-
children: /* @__PURE__ */
|
|
583
|
+
children: /* @__PURE__ */ jsx6(
|
|
1802
584
|
ExpandableSection,
|
|
1803
585
|
{
|
|
1804
586
|
toggleText: summaryExpanded ? "Hide summary" : "Show summary",
|
|
1805
587
|
onToggle: (_event, isExpanded) => setSummaryExpanded(isExpanded),
|
|
1806
588
|
isExpanded: summaryExpanded,
|
|
1807
589
|
isIndented: true,
|
|
1808
|
-
children: /* @__PURE__ */
|
|
590
|
+
children: /* @__PURE__ */ jsx6("div", { style: { fontSize: "0.875rem", lineHeight: "1.5" }, children: threadSummaries[selectedThread.id] })
|
|
1809
591
|
}
|
|
1810
592
|
)
|
|
1811
593
|
}
|
|
1812
594
|
),
|
|
1813
|
-
/* @__PURE__ */
|
|
1814
|
-
/* @__PURE__ */
|
|
1815
|
-
/* @__PURE__ */
|
|
1816
|
-
/* @__PURE__ */
|
|
595
|
+
/* @__PURE__ */ jsx6(Divider, {}),
|
|
596
|
+
/* @__PURE__ */ jsx6("div", { style: { display: "flex", flexDirection: "column", gap: "1rem" }, children: selectedThread.comments.length === 0 ? /* @__PURE__ */ jsxs2(EmptyState, { children: [
|
|
597
|
+
/* @__PURE__ */ jsx6(Title, { headingLevel: "h4", size: "md", children: "No comments yet" }),
|
|
598
|
+
/* @__PURE__ */ jsx6(EmptyStateBody, { children: enableCommenting ? "Add a reply below to start the conversation." : "Enable commenting to add replies." })
|
|
1817
599
|
] }) : selectedThread.comments.map((comment, index) => /* @__PURE__ */ jsxs2(Card, { isCompact: true, children: [
|
|
1818
600
|
/* @__PURE__ */ jsxs2(CardTitle, { children: [
|
|
1819
601
|
"Comment #",
|
|
@@ -1826,8 +608,8 @@ var CommentDrawer = ({
|
|
|
1826
608
|
formatDate(comment.createdAt)
|
|
1827
609
|
] })
|
|
1828
610
|
] }),
|
|
1829
|
-
/* @__PURE__ */
|
|
1830
|
-
/* @__PURE__ */
|
|
611
|
+
/* @__PURE__ */ jsx6(CardBody, { children: editingCommentId === comment.id ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
612
|
+
/* @__PURE__ */ jsx6(
|
|
1831
613
|
TextArea,
|
|
1832
614
|
{
|
|
1833
615
|
id: `edit-comment-${comment.id}`,
|
|
@@ -1847,7 +629,7 @@ var CommentDrawer = ({
|
|
|
1847
629
|
}
|
|
1848
630
|
),
|
|
1849
631
|
/* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: "0.5rem" }, children: [
|
|
1850
|
-
/* @__PURE__ */
|
|
632
|
+
/* @__PURE__ */ jsx6(
|
|
1851
633
|
Button2,
|
|
1852
634
|
{
|
|
1853
635
|
id: `save-comment-${comment.id}`,
|
|
@@ -1857,7 +639,7 @@ var CommentDrawer = ({
|
|
|
1857
639
|
children: "Save"
|
|
1858
640
|
}
|
|
1859
641
|
),
|
|
1860
|
-
/* @__PURE__ */
|
|
642
|
+
/* @__PURE__ */ jsx6(
|
|
1861
643
|
Button2,
|
|
1862
644
|
{
|
|
1863
645
|
id: `cancel-edit-${comment.id}`,
|
|
@@ -1869,9 +651,9 @@ var CommentDrawer = ({
|
|
|
1869
651
|
)
|
|
1870
652
|
] })
|
|
1871
653
|
] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1872
|
-
/* @__PURE__ */
|
|
1873
|
-
enableCommenting &&
|
|
1874
|
-
/* @__PURE__ */
|
|
654
|
+
/* @__PURE__ */ jsx6("div", { style: { marginBottom: "0.75rem", whiteSpace: "pre-wrap" }, children: comment.text || /* @__PURE__ */ jsx6("em", { style: { color: "var(--pf-v6-global--Color--200)" }, children: "No text" }) }),
|
|
655
|
+
enableCommenting && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: "0.5rem" }, children: [
|
|
656
|
+
/* @__PURE__ */ jsx6(
|
|
1875
657
|
Button2,
|
|
1876
658
|
{
|
|
1877
659
|
id: `edit-comment-btn-${comment.id}`,
|
|
@@ -1881,13 +663,13 @@ var CommentDrawer = ({
|
|
|
1881
663
|
children: "Edit"
|
|
1882
664
|
}
|
|
1883
665
|
),
|
|
1884
|
-
/* @__PURE__ */
|
|
666
|
+
/* @__PURE__ */ jsx6(
|
|
1885
667
|
Button2,
|
|
1886
668
|
{
|
|
1887
669
|
id: `delete-comment-btn-${comment.id}`,
|
|
1888
670
|
variant: "danger",
|
|
1889
671
|
size: "sm",
|
|
1890
|
-
icon: /* @__PURE__ */
|
|
672
|
+
icon: /* @__PURE__ */ jsx6(TimesIcon, {}),
|
|
1891
673
|
onClick: () => handleDeleteComment(selectedThread.id, comment.id),
|
|
1892
674
|
children: "Delete"
|
|
1893
675
|
}
|
|
@@ -1895,15 +677,15 @@ var CommentDrawer = ({
|
|
|
1895
677
|
] })
|
|
1896
678
|
] }) })
|
|
1897
679
|
] }, comment.id)) }),
|
|
1898
|
-
enableCommenting &&
|
|
1899
|
-
/* @__PURE__ */
|
|
680
|
+
enableCommenting && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
681
|
+
/* @__PURE__ */ jsx6(Divider, {}),
|
|
1900
682
|
/* @__PURE__ */ jsxs2(Card, { isCompact: true, children: [
|
|
1901
683
|
/* @__PURE__ */ jsxs2(CardTitle, { children: [
|
|
1902
|
-
/* @__PURE__ */
|
|
684
|
+
/* @__PURE__ */ jsx6(PlusCircleIcon, { style: { marginRight: "0.5rem" } }),
|
|
1903
685
|
"Add Reply"
|
|
1904
686
|
] }),
|
|
1905
687
|
/* @__PURE__ */ jsxs2(CardBody, { children: [
|
|
1906
|
-
/* @__PURE__ */
|
|
688
|
+
/* @__PURE__ */ jsx6(
|
|
1907
689
|
TextArea,
|
|
1908
690
|
{
|
|
1909
691
|
ref: replyTextAreaRef,
|
|
@@ -1921,7 +703,7 @@ var CommentDrawer = ({
|
|
|
1921
703
|
}
|
|
1922
704
|
}
|
|
1923
705
|
),
|
|
1924
|
-
/* @__PURE__ */
|
|
706
|
+
/* @__PURE__ */ jsx6(
|
|
1925
707
|
Button2,
|
|
1926
708
|
{
|
|
1927
709
|
id: `add-reply-${selectedThread.id}`,
|
|
@@ -1937,323 +719,34 @@ var CommentDrawer = ({
|
|
|
1937
719
|
] })
|
|
1938
720
|
] }) })
|
|
1939
721
|
] });
|
|
1940
|
-
return /* @__PURE__ */
|
|
1941
|
-
};
|
|
1942
|
-
|
|
1943
|
-
// src/components/AIAssistant.tsx
|
|
1944
|
-
import { ChatbotToggle } from "@patternfly/chatbot";
|
|
1945
|
-
|
|
1946
|
-
// src/components/AIChatPanel.tsx
|
|
1947
|
-
import * as React7 from "react";
|
|
1948
|
-
import {
|
|
1949
|
-
Chatbot,
|
|
1950
|
-
ChatbotDisplayMode,
|
|
1951
|
-
ChatbotHeader,
|
|
1952
|
-
ChatbotHeaderActions,
|
|
1953
|
-
ChatbotHeaderMain,
|
|
1954
|
-
ChatbotHeaderTitle,
|
|
1955
|
-
ChatbotContent,
|
|
1956
|
-
ChatbotFooter,
|
|
1957
|
-
ChatbotWelcomePrompt,
|
|
1958
|
-
MessageBox,
|
|
1959
|
-
Message
|
|
1960
|
-
} from "@patternfly/chatbot";
|
|
1961
|
-
import {
|
|
1962
|
-
Button as Button3,
|
|
1963
|
-
Label as Label2,
|
|
1964
|
-
TextArea as TextArea2,
|
|
1965
|
-
ActionList,
|
|
1966
|
-
ActionListItem
|
|
1967
|
-
} from "@patternfly/react-core";
|
|
1968
|
-
import { TimesIcon as TimesIcon2, PaperPlaneIcon, TrashIcon } from "@patternfly/react-icons";
|
|
1969
|
-
import { useLocation as useLocation3 } from "react-router-dom";
|
|
1970
|
-
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1971
|
-
var AIChatPanel = () => {
|
|
1972
|
-
const { messages, isLoading, sendMessage, toggleChatbot, clearHistory } = useAIContext();
|
|
1973
|
-
const { currentVersion } = useVersion();
|
|
1974
|
-
const { threads } = useComments();
|
|
1975
|
-
const location = useLocation3();
|
|
1976
|
-
const [inputValue, setInputValue] = React7.useState("");
|
|
1977
|
-
const commentCount = React7.useMemo(() => {
|
|
1978
|
-
return threads.filter((t) => t.version === currentVersion).reduce((acc, t) => acc + t.comments.length, 0);
|
|
1979
|
-
}, [threads, currentVersion]);
|
|
1980
|
-
const handleSendMessage = async () => {
|
|
1981
|
-
if (inputValue.trim() && !isLoading) {
|
|
1982
|
-
await sendMessage(inputValue, {
|
|
1983
|
-
threads,
|
|
1984
|
-
version: currentVersion || "unknown",
|
|
1985
|
-
route: location.pathname
|
|
1986
|
-
});
|
|
1987
|
-
setInputValue("");
|
|
1988
|
-
}
|
|
1989
|
-
};
|
|
1990
|
-
const handleKeyDown = (event) => {
|
|
1991
|
-
if (event.key === "Enter" && !event.shiftKey) {
|
|
1992
|
-
event.preventDefault();
|
|
1993
|
-
handleSendMessage();
|
|
1994
|
-
}
|
|
1995
|
-
};
|
|
1996
|
-
const handleQuickAction = (action) => {
|
|
1997
|
-
setInputValue(action);
|
|
1998
|
-
};
|
|
1999
|
-
return /* @__PURE__ */ jsx8(
|
|
2000
|
-
"div",
|
|
2001
|
-
{
|
|
2002
|
-
style: {
|
|
2003
|
-
position: "fixed",
|
|
2004
|
-
bottom: "80px",
|
|
2005
|
-
right: "20px",
|
|
2006
|
-
width: "400px",
|
|
2007
|
-
maxHeight: "600px",
|
|
2008
|
-
zIndex: 2e3,
|
|
2009
|
-
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)"
|
|
2010
|
-
},
|
|
2011
|
-
children: /* @__PURE__ */ jsxs3(Chatbot, { displayMode: ChatbotDisplayMode.default, children: [
|
|
2012
|
-
/* @__PURE__ */ jsxs3(ChatbotHeader, { children: [
|
|
2013
|
-
/* @__PURE__ */ jsxs3(ChatbotHeaderMain, { children: [
|
|
2014
|
-
/* @__PURE__ */ jsx8(ChatbotHeaderTitle, { children: "\u{1F916} AI Feedback Assistant" }),
|
|
2015
|
-
currentVersion && commentCount > 0 && /* @__PURE__ */ jsxs3("span", { style: { marginLeft: "0.5rem" }, children: [
|
|
2016
|
-
/* @__PURE__ */ jsxs3(Label2, { color: "blue", isCompact: true, children: [
|
|
2017
|
-
"Version ",
|
|
2018
|
-
currentVersion
|
|
2019
|
-
] }),
|
|
2020
|
-
/* @__PURE__ */ jsxs3(Label2, { color: "grey", isCompact: true, style: { marginLeft: "0.25rem" }, children: [
|
|
2021
|
-
commentCount,
|
|
2022
|
-
" comments"
|
|
2023
|
-
] })
|
|
2024
|
-
] })
|
|
2025
|
-
] }),
|
|
2026
|
-
/* @__PURE__ */ jsxs3(ChatbotHeaderActions, { children: [
|
|
2027
|
-
messages.length > 0 && /* @__PURE__ */ jsx8(
|
|
2028
|
-
Button3,
|
|
2029
|
-
{
|
|
2030
|
-
variant: "plain",
|
|
2031
|
-
onClick: clearHistory,
|
|
2032
|
-
"aria-label": "Clear chat history",
|
|
2033
|
-
icon: /* @__PURE__ */ jsx8(TrashIcon, {})
|
|
2034
|
-
}
|
|
2035
|
-
),
|
|
2036
|
-
/* @__PURE__ */ jsx8(
|
|
2037
|
-
Button3,
|
|
2038
|
-
{
|
|
2039
|
-
variant: "plain",
|
|
2040
|
-
onClick: toggleChatbot,
|
|
2041
|
-
"aria-label": "Close chatbot",
|
|
2042
|
-
icon: /* @__PURE__ */ jsx8(TimesIcon2, {})
|
|
2043
|
-
}
|
|
2044
|
-
)
|
|
2045
|
-
] })
|
|
2046
|
-
] }),
|
|
2047
|
-
/* @__PURE__ */ jsx8(ChatbotContent, { children: /* @__PURE__ */ jsx8(MessageBox, { children: messages.length === 0 ? /* @__PURE__ */ jsx8(
|
|
2048
|
-
ChatbotWelcomePrompt,
|
|
2049
|
-
{
|
|
2050
|
-
title: "Welcome to AI Feedback Assistant",
|
|
2051
|
-
description: "Ask me about comments across your prototype. I can help you:",
|
|
2052
|
-
children: /* @__PURE__ */ jsxs3(ActionList, { children: [
|
|
2053
|
-
/* @__PURE__ */ jsx8(ActionListItem, { children: /* @__PURE__ */ jsx8(
|
|
2054
|
-
Button3,
|
|
2055
|
-
{
|
|
2056
|
-
variant: "link",
|
|
2057
|
-
isInline: true,
|
|
2058
|
-
onClick: () => handleQuickAction("What feedback was left in the last week?"),
|
|
2059
|
-
children: "Summarize recent feedback"
|
|
2060
|
-
}
|
|
2061
|
-
) }),
|
|
2062
|
-
/* @__PURE__ */ jsx8(ActionListItem, { children: /* @__PURE__ */ jsx8(
|
|
2063
|
-
Button3,
|
|
2064
|
-
{
|
|
2065
|
-
variant: "link",
|
|
2066
|
-
isInline: true,
|
|
2067
|
-
onClick: () => handleQuickAction("Show me all accessibility issues"),
|
|
2068
|
-
children: "Find accessibility issues"
|
|
2069
|
-
}
|
|
2070
|
-
) }),
|
|
2071
|
-
/* @__PURE__ */ jsx8(ActionListItem, { children: /* @__PURE__ */ jsx8(
|
|
2072
|
-
Button3,
|
|
2073
|
-
{
|
|
2074
|
-
variant: "link",
|
|
2075
|
-
isInline: true,
|
|
2076
|
-
onClick: () => handleQuickAction("What are the main navigation problems?"),
|
|
2077
|
-
children: "Identify navigation problems"
|
|
2078
|
-
}
|
|
2079
|
-
) }),
|
|
2080
|
-
/* @__PURE__ */ jsx8(ActionListItem, { children: /* @__PURE__ */ jsx8(
|
|
2081
|
-
Button3,
|
|
2082
|
-
{
|
|
2083
|
-
variant: "link",
|
|
2084
|
-
isInline: true,
|
|
2085
|
-
onClick: () => handleQuickAction("Which page has the most comments?"),
|
|
2086
|
-
children: "Analyze comment distribution"
|
|
2087
|
-
}
|
|
2088
|
-
) })
|
|
2089
|
-
] })
|
|
2090
|
-
}
|
|
2091
|
-
) : /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
2092
|
-
messages.map((msg) => /* @__PURE__ */ jsx8(
|
|
2093
|
-
Message,
|
|
2094
|
-
{
|
|
2095
|
-
name: msg.role === "user" ? "You" : "AI Assistant",
|
|
2096
|
-
role: msg.role === "user" ? "user" : "bot",
|
|
2097
|
-
avatar: msg.role === "user" ? "/images/user_advatar.jpg" : "/images/halefavicon.png",
|
|
2098
|
-
content: msg.content,
|
|
2099
|
-
timestamp: new Date(msg.timestamp).toLocaleString(void 0, {
|
|
2100
|
-
month: "short",
|
|
2101
|
-
day: "numeric",
|
|
2102
|
-
hour: "2-digit",
|
|
2103
|
-
minute: "2-digit"
|
|
2104
|
-
})
|
|
2105
|
-
},
|
|
2106
|
-
msg.id
|
|
2107
|
-
)),
|
|
2108
|
-
isLoading && /* @__PURE__ */ jsx8(
|
|
2109
|
-
Message,
|
|
2110
|
-
{
|
|
2111
|
-
name: "AI Assistant",
|
|
2112
|
-
role: "bot",
|
|
2113
|
-
avatar: "/images/halefavicon.png",
|
|
2114
|
-
content: "\u23F3 Analyzing comments..."
|
|
2115
|
-
}
|
|
2116
|
-
)
|
|
2117
|
-
] }) }) }),
|
|
2118
|
-
/* @__PURE__ */ jsx8(ChatbotFooter, { children: /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "0.5rem", alignItems: "flex-end", width: "100%" }, children: [
|
|
2119
|
-
/* @__PURE__ */ jsx8(
|
|
2120
|
-
TextArea2,
|
|
2121
|
-
{
|
|
2122
|
-
id: "ai-chat-input",
|
|
2123
|
-
value: inputValue,
|
|
2124
|
-
onChange: (_event, value) => setInputValue(value),
|
|
2125
|
-
onKeyDown: handleKeyDown,
|
|
2126
|
-
placeholder: "Ask about feedback...",
|
|
2127
|
-
"aria-label": "Message input",
|
|
2128
|
-
rows: 2,
|
|
2129
|
-
style: { flex: 1 },
|
|
2130
|
-
isDisabled: isLoading
|
|
2131
|
-
}
|
|
2132
|
-
),
|
|
2133
|
-
/* @__PURE__ */ jsx8(
|
|
2134
|
-
Button3,
|
|
2135
|
-
{
|
|
2136
|
-
variant: "primary",
|
|
2137
|
-
onClick: handleSendMessage,
|
|
2138
|
-
isDisabled: !inputValue.trim() || isLoading,
|
|
2139
|
-
icon: /* @__PURE__ */ jsx8(PaperPlaneIcon, {}),
|
|
2140
|
-
"aria-label": "Send message"
|
|
2141
|
-
}
|
|
2142
|
-
)
|
|
2143
|
-
] }) })
|
|
2144
|
-
] })
|
|
2145
|
-
}
|
|
2146
|
-
);
|
|
2147
|
-
};
|
|
2148
|
-
|
|
2149
|
-
// src/components/AIAssistant.tsx
|
|
2150
|
-
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2151
|
-
var AIAssistant = () => {
|
|
2152
|
-
const { isChatbotVisible, toggleChatbot } = useAIContext();
|
|
2153
|
-
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
2154
|
-
/* @__PURE__ */ jsx9(
|
|
2155
|
-
"div",
|
|
2156
|
-
{
|
|
2157
|
-
style: {
|
|
2158
|
-
position: "fixed",
|
|
2159
|
-
bottom: "20px",
|
|
2160
|
-
right: "20px",
|
|
2161
|
-
zIndex: 2e3
|
|
2162
|
-
},
|
|
2163
|
-
children: /* @__PURE__ */ jsx9(
|
|
2164
|
-
ChatbotToggle,
|
|
2165
|
-
{
|
|
2166
|
-
id: "ai-chatbot-toggle",
|
|
2167
|
-
isChatbotVisible,
|
|
2168
|
-
onToggleChatbot: toggleChatbot,
|
|
2169
|
-
toggleButtonLabel: "AI Assistant",
|
|
2170
|
-
tooltipLabel: "Get AI help analyzing feedback",
|
|
2171
|
-
style: {
|
|
2172
|
-
backgroundColor: "#C9190B",
|
|
2173
|
-
borderRadius: "50%",
|
|
2174
|
-
width: "56px",
|
|
2175
|
-
height: "56px",
|
|
2176
|
-
border: "2px solid white",
|
|
2177
|
-
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.3)",
|
|
2178
|
-
color: "white"
|
|
2179
|
-
}
|
|
2180
|
-
}
|
|
2181
|
-
)
|
|
2182
|
-
}
|
|
2183
|
-
),
|
|
2184
|
-
isChatbotVisible && /* @__PURE__ */ jsx9(AIChatPanel, {})
|
|
2185
|
-
] });
|
|
722
|
+
return /* @__PURE__ */ jsx6(Drawer, { isExpanded: isDrawerOpen, isInline: true, position: "right", children: /* @__PURE__ */ jsx6(DrawerContent, { panelContent, children: /* @__PURE__ */ jsx6(DrawerContentBody, { children }) }) });
|
|
2186
723
|
};
|
|
2187
724
|
|
|
2188
725
|
// src/contexts/GitHubAuthContext.tsx
|
|
2189
|
-
import * as
|
|
2190
|
-
import { jsx as
|
|
2191
|
-
var GitHubAuthContext =
|
|
726
|
+
import * as React6 from "react";
|
|
727
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
728
|
+
var GitHubAuthContext = React6.createContext(void 0);
|
|
2192
729
|
var GitHubAuthProvider = ({ children }) => {
|
|
2193
|
-
const
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
const handleOAuthCallback = () => {
|
|
2202
|
-
const hash = window.location.hash;
|
|
2203
|
-
if (hash.includes("#/auth-callback")) {
|
|
2204
|
-
const params = new URLSearchParams(hash.split("?")[1]);
|
|
2205
|
-
const token = params.get("token");
|
|
2206
|
-
const login2 = params.get("login");
|
|
2207
|
-
const avatar = params.get("avatar");
|
|
2208
|
-
if (token && login2 && avatar) {
|
|
2209
|
-
storeGitHubAuth(token, login2, decodeURIComponent(avatar));
|
|
2210
|
-
setUser({ login: login2, avatar: decodeURIComponent(avatar) });
|
|
2211
|
-
window.location.hash = "/";
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
};
|
|
2215
|
-
handleOAuthCallback();
|
|
2216
|
-
}, []);
|
|
2217
|
-
const login = () => {
|
|
2218
|
-
const clientId = process.env.VITE_GITHUB_CLIENT_ID;
|
|
2219
|
-
if (!clientId) {
|
|
2220
|
-
alert("GitHub login is not configured (missing VITE_GITHUB_CLIENT_ID).");
|
|
2221
|
-
return;
|
|
730
|
+
const value = {
|
|
731
|
+
user: null,
|
|
732
|
+
isAuthenticated: false,
|
|
733
|
+
login: () => {
|
|
734
|
+
console.log("GitHub login not available in local mode");
|
|
735
|
+
},
|
|
736
|
+
logout: () => {
|
|
737
|
+
console.log("GitHub logout not available in local mode");
|
|
2222
738
|
}
|
|
2223
|
-
const redirectUri = `${window.location.origin}/api/github-oauth-callback`;
|
|
2224
|
-
const scope = "public_repo";
|
|
2225
|
-
const githubAuthUrl = `https://github.com/login/oauth/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${encodeURIComponent(scope)}`;
|
|
2226
|
-
console.log("\u{1F50D} GitHub OAuth redirect:", { clientId: "present", redirectUri });
|
|
2227
|
-
window.location.href = githubAuthUrl;
|
|
2228
739
|
};
|
|
2229
|
-
|
|
2230
|
-
clearGitHubAuth();
|
|
2231
|
-
setUser(null);
|
|
2232
|
-
};
|
|
2233
|
-
return /* @__PURE__ */ jsx10(
|
|
2234
|
-
GitHubAuthContext.Provider,
|
|
2235
|
-
{
|
|
2236
|
-
value: {
|
|
2237
|
-
user,
|
|
2238
|
-
isAuthenticated: !!user,
|
|
2239
|
-
login,
|
|
2240
|
-
logout
|
|
2241
|
-
},
|
|
2242
|
-
children
|
|
2243
|
-
}
|
|
2244
|
-
);
|
|
740
|
+
return /* @__PURE__ */ jsx7(GitHubAuthContext.Provider, { value, children });
|
|
2245
741
|
};
|
|
2246
742
|
var useGitHubAuth = () => {
|
|
2247
|
-
const context =
|
|
743
|
+
const context = React6.useContext(GitHubAuthContext);
|
|
2248
744
|
if (context === void 0) {
|
|
2249
745
|
throw new Error("useGitHubAuth must be used within a GitHubAuthProvider");
|
|
2250
746
|
}
|
|
2251
747
|
return context;
|
|
2252
748
|
};
|
|
2253
749
|
export {
|
|
2254
|
-
AIAssistant,
|
|
2255
|
-
AIChatPanel,
|
|
2256
|
-
AIProvider,
|
|
2257
750
|
CommentDrawer,
|
|
2258
751
|
CommentOverlay,
|
|
2259
752
|
CommentPin,
|
|
@@ -2261,11 +754,6 @@ export {
|
|
|
2261
754
|
GitHubAuthProvider,
|
|
2262
755
|
GitLabAuthProvider,
|
|
2263
756
|
VersionProvider,
|
|
2264
|
-
githubAdapter,
|
|
2265
|
-
gitlabAdapter,
|
|
2266
|
-
isGitHubConfigured,
|
|
2267
|
-
isGitLabConfigured,
|
|
2268
|
-
useAIContext,
|
|
2269
757
|
useComments,
|
|
2270
758
|
useGitHubAuth,
|
|
2271
759
|
useGitLabAuth,
|