feedback-vos 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -22
- package/README.md +103 -309
- package/dist/index.d.mts +5 -9
- package/dist/index.d.ts +5 -9
- package/dist/index.js +342 -83
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +342 -83
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +103 -98
- package/package.json +59 -59
package/dist/index.js
CHANGED
|
@@ -60,7 +60,7 @@ function ScreenshotButton({
|
|
|
60
60
|
"button",
|
|
61
61
|
{
|
|
62
62
|
type: "button",
|
|
63
|
-
className: "p-1 w-10 h-10 rounded-md border-transparent flex \
|
|
63
|
+
className: "p-1 w-10 h-10 rounded-md border-transparent flex \n justify-end items-end text-zinc-400 hover:text-zinc-100 transition-colors",
|
|
64
64
|
onClick: () => onScreenshotTook(null),
|
|
65
65
|
style: {
|
|
66
66
|
backgroundImage: `url(${screenshot})`,
|
|
@@ -75,119 +75,384 @@ function ScreenshotButton({
|
|
|
75
75
|
"button",
|
|
76
76
|
{
|
|
77
77
|
type: "button",
|
|
78
|
-
className: "p-2 bg-zinc-800 rounded-md border-transparent hover:bg-zinc-700\
|
|
78
|
+
className: "p-2 bg-zinc-800 rounded-md border-transparent hover:bg-zinc-700\n transitions-colors focus:outline-none focus:ring-2\n focus:ring-offset-2 focus:ring-offset-zinc-900 focus:ring-brand-500",
|
|
79
79
|
onClick: handleTakeScreenshot,
|
|
80
80
|
children: isTakenScreenshot ? /* @__PURE__ */ jsxRuntime.jsx(Loading, {}) : /* @__PURE__ */ jsxRuntime.jsx(phosphorReact.Camera, { weight: "bold", className: "w-6 h-6" })
|
|
81
81
|
}
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// src/lib/integrations/
|
|
86
|
-
async function
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
name: typeMap[type]
|
|
85
|
+
// src/lib/integrations/github.ts
|
|
86
|
+
async function uploadScreenshotToRepo(token, owner, repo, screenshot, screenshotPath) {
|
|
87
|
+
const compressedScreenshot = await compressScreenshot(screenshot, 1920, 0.7);
|
|
88
|
+
const base64Data = compressedScreenshot.split(",")[1];
|
|
89
|
+
const binaryData = Uint8Array.from(atob(base64Data), (c) => c.charCodeAt(0));
|
|
90
|
+
const repoResponse = await fetch(
|
|
91
|
+
`https://api.github.com/repos/${owner}/${repo}`,
|
|
92
|
+
{
|
|
93
|
+
headers: {
|
|
94
|
+
"Authorization": `Bearer ${token}`,
|
|
95
|
+
"Accept": "application/vnd.github.v3+json",
|
|
96
|
+
"User-Agent": "feedback-vos"
|
|
98
97
|
}
|
|
99
|
-
},
|
|
100
|
-
Comment: {
|
|
101
|
-
rich_text: [
|
|
102
|
-
{
|
|
103
|
-
text: {
|
|
104
|
-
content: comment
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
98
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
99
|
+
);
|
|
100
|
+
let defaultBranch = "main";
|
|
101
|
+
if (repoResponse.ok) {
|
|
102
|
+
const repoData = await repoResponse.json();
|
|
103
|
+
defaultBranch = repoData.default_branch || "main";
|
|
104
|
+
}
|
|
105
|
+
const folderPath = screenshotPath || ".feedback-vos";
|
|
106
|
+
const timestamp = Date.now();
|
|
107
|
+
const randomId = Math.random().toString(36).substring(2, 9);
|
|
108
|
+
const filename = `feedback-${timestamp}-${randomId}.jpg`;
|
|
109
|
+
const path = `${folderPath}/${filename}`;
|
|
110
|
+
const base64Content = btoa(String.fromCharCode(...binaryData));
|
|
111
|
+
const folderCheckUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${folderPath}`;
|
|
112
|
+
try {
|
|
113
|
+
const folderCheck = await fetch(folderCheckUrl, {
|
|
114
|
+
headers: {
|
|
115
|
+
"Authorization": `Bearer ${token}`,
|
|
116
|
+
"Accept": "application/vnd.github.v3+json",
|
|
117
|
+
"User-Agent": "feedback-vos"
|
|
123
118
|
}
|
|
124
119
|
});
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
120
|
+
if (folderCheck.status === 404) {
|
|
121
|
+
const readmeContent = btoa("# Feedback Screenshots\n\nThis folder contains screenshots from user feedback.\n");
|
|
122
|
+
try {
|
|
123
|
+
const folderCreateResponse = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${folderPath}/README.md`, {
|
|
124
|
+
method: "PUT",
|
|
125
|
+
headers: {
|
|
126
|
+
"Authorization": `Bearer ${token}`,
|
|
127
|
+
"Accept": "application/vnd.github.v3+json",
|
|
128
|
+
"Content-Type": "application/json",
|
|
129
|
+
"User-Agent": "feedback-vos"
|
|
130
|
+
},
|
|
131
|
+
body: JSON.stringify({
|
|
132
|
+
message: "Create feedback screenshots folder",
|
|
133
|
+
content: readmeContent,
|
|
134
|
+
branch: defaultBranch
|
|
135
|
+
})
|
|
136
|
+
});
|
|
137
|
+
if (!folderCreateResponse.ok) {
|
|
138
|
+
const folderError = await folderCreateResponse.json().catch(() => ({}));
|
|
139
|
+
console.warn("Could not create folder, proceeding with upload anyway:", folderError);
|
|
140
|
+
} else {
|
|
141
|
+
console.log(`Folder ${folderPath} created successfully`);
|
|
132
142
|
}
|
|
133
|
-
|
|
134
|
-
|
|
143
|
+
} catch (folderCreateError) {
|
|
144
|
+
console.warn("Could not create folder, proceeding with upload:", folderCreateError);
|
|
145
|
+
}
|
|
146
|
+
} else if (folderCheck.status === 200) {
|
|
147
|
+
const folderData = await folderCheck.json().catch(() => null);
|
|
148
|
+
if (folderData && !Array.isArray(folderData)) {
|
|
149
|
+
throw new Error(`Path "${folderPath}" exists as a file, not a folder. Please use a different path or remove the file.`);
|
|
150
|
+
}
|
|
151
|
+
console.log(`Folder ${folderPath} already exists`);
|
|
152
|
+
}
|
|
153
|
+
} catch (folderError) {
|
|
154
|
+
if (folderError instanceof Error && folderError.message.includes("exists as a file")) {
|
|
155
|
+
throw folderError;
|
|
156
|
+
}
|
|
157
|
+
console.warn("Could not verify/create screenshots folder:", folderError);
|
|
135
158
|
}
|
|
136
|
-
const
|
|
137
|
-
|
|
159
|
+
const uploadUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${path}`;
|
|
160
|
+
const response = await fetch(uploadUrl, {
|
|
161
|
+
method: "PUT",
|
|
138
162
|
headers: {
|
|
139
|
-
"Authorization": `Bearer ${
|
|
163
|
+
"Authorization": `Bearer ${token}`,
|
|
164
|
+
"Accept": "application/vnd.github.v3+json",
|
|
140
165
|
"Content-Type": "application/json",
|
|
141
|
-
"
|
|
166
|
+
"User-Agent": "feedback-vos"
|
|
142
167
|
},
|
|
143
168
|
body: JSON.stringify({
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
properties,
|
|
148
|
-
children
|
|
169
|
+
message: `Add feedback screenshot: ${filename}`,
|
|
170
|
+
content: base64Content,
|
|
171
|
+
branch: defaultBranch
|
|
149
172
|
})
|
|
150
173
|
});
|
|
151
174
|
if (!response.ok) {
|
|
152
|
-
const
|
|
153
|
-
|
|
175
|
+
const errorData = await response.json().catch(() => ({}));
|
|
176
|
+
const errorMessage = errorData.message || "Unknown error";
|
|
177
|
+
if (response.status === 409) {
|
|
178
|
+
throw new Error(`File already exists. This is unexpected - please try again.`);
|
|
179
|
+
} else if (response.status === 403) {
|
|
180
|
+
throw new Error(`Permission denied. Make sure your token has write access to the repository.`);
|
|
181
|
+
} else if (response.status === 422) {
|
|
182
|
+
throw new Error(`Validation failed: ${errorMessage}. The file might be too large.`);
|
|
183
|
+
}
|
|
184
|
+
throw new Error(`Failed to upload screenshot (${response.status}): ${errorMessage}`);
|
|
185
|
+
}
|
|
186
|
+
await response.json();
|
|
187
|
+
const rawUrl = `https://github.com/${owner}/${repo}/blob/${defaultBranch}/${path}?raw=true`;
|
|
188
|
+
console.log(`Screenshot uploaded successfully to: ${rawUrl}`);
|
|
189
|
+
return rawUrl;
|
|
190
|
+
}
|
|
191
|
+
function compressScreenshot(dataUrl, maxWidth = 1920, quality = 0.7) {
|
|
192
|
+
return new Promise((resolve, reject) => {
|
|
193
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
194
|
+
reject(new Error("Screenshot compression only works in browser environments"));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const img = new Image();
|
|
198
|
+
img.onload = () => {
|
|
199
|
+
const canvas = document.createElement("canvas");
|
|
200
|
+
let width = img.width;
|
|
201
|
+
let height = img.height;
|
|
202
|
+
if (width > maxWidth) {
|
|
203
|
+
height = height * maxWidth / width;
|
|
204
|
+
width = maxWidth;
|
|
205
|
+
}
|
|
206
|
+
canvas.width = width;
|
|
207
|
+
canvas.height = height;
|
|
208
|
+
const ctx = canvas.getContext("2d");
|
|
209
|
+
if (!ctx) {
|
|
210
|
+
reject(new Error("Could not get canvas context"));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
214
|
+
const compressedDataUrl = canvas.toDataURL("image/jpeg", quality);
|
|
215
|
+
resolve(compressedDataUrl);
|
|
216
|
+
};
|
|
217
|
+
img.onerror = () => reject(new Error("Failed to load image"));
|
|
218
|
+
img.src = dataUrl;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
async function verifyRepositoryAccess(token, owner, repo) {
|
|
222
|
+
try {
|
|
223
|
+
const response = await fetch(
|
|
224
|
+
`https://api.github.com/repos/${owner}/${repo}`,
|
|
225
|
+
{
|
|
226
|
+
headers: {
|
|
227
|
+
"Authorization": `Bearer ${token}`,
|
|
228
|
+
"Accept": "application/vnd.github.v3+json",
|
|
229
|
+
"User-Agent": "feedback-vos"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
if (response.status === 404) {
|
|
234
|
+
return { exists: false, hasIssues: false, error: "Repository not found" };
|
|
235
|
+
}
|
|
236
|
+
if (!response.ok) {
|
|
237
|
+
const errorData = await response.json().catch(() => ({}));
|
|
238
|
+
return {
|
|
239
|
+
exists: false,
|
|
240
|
+
hasIssues: false,
|
|
241
|
+
error: errorData.message || `HTTP ${response.status}`
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const repoData = await response.json();
|
|
245
|
+
return {
|
|
246
|
+
exists: true,
|
|
247
|
+
hasIssues: repoData.has_issues !== false
|
|
248
|
+
// Default is true if not specified
|
|
249
|
+
};
|
|
250
|
+
} catch (error) {
|
|
251
|
+
return {
|
|
252
|
+
exists: false,
|
|
253
|
+
hasIssues: false,
|
|
254
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
255
|
+
};
|
|
154
256
|
}
|
|
155
257
|
}
|
|
156
|
-
|
|
157
|
-
// src/lib/integrations/github.ts
|
|
158
258
|
async function sendToGitHub(config, data) {
|
|
159
|
-
const { token, owner, repo } = config;
|
|
259
|
+
const { token, owner, repo, screenshotPath } = config;
|
|
160
260
|
const { type, comment, screenshot } = data;
|
|
261
|
+
if (!token || !owner || !repo) {
|
|
262
|
+
throw new Error("GitHub configuration is incomplete. Please provide token, owner, and repo.");
|
|
263
|
+
}
|
|
264
|
+
const verification = await verifyRepositoryAccess(token, owner, repo);
|
|
265
|
+
if (!verification.exists) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
`Repository "${owner}/${repo}" not found or not accessible.
|
|
268
|
+
Error: ${verification.error || "Unknown error"}
|
|
269
|
+
|
|
270
|
+
Please verify:
|
|
271
|
+
- Repository exists at https://github.com/${owner}/${repo}
|
|
272
|
+
- Your token has access to this repository
|
|
273
|
+
- Token has "repo" scope (for private) or "public_repo" scope (for public)`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
if (!verification.hasIssues) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`Issues are disabled for repository "${owner}/${repo}".
|
|
279
|
+
|
|
280
|
+
Please enable Issues in repository Settings \u2192 General \u2192 Features \u2192 Issues`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
161
283
|
const title = `[${type}] Feedback`;
|
|
284
|
+
const ABSOLUTE_MAX_LENGTH = 65536;
|
|
285
|
+
const BASE_BODY_LENGTH = 50;
|
|
286
|
+
const SCREENSHOT_URL_LENGTH = 150;
|
|
287
|
+
const SAFETY_MARGIN = 1e3;
|
|
288
|
+
const MAX_COMMENT_LENGTH = ABSOLUTE_MAX_LENGTH - BASE_BODY_LENGTH - SCREENSHOT_URL_LENGTH - SAFETY_MARGIN;
|
|
289
|
+
const limitedComment = comment.length > MAX_COMMENT_LENGTH ? comment.substring(0, MAX_COMMENT_LENGTH) + "\n\n... (comment truncated)" : comment;
|
|
162
290
|
let body = `**Type:** ${type}
|
|
163
291
|
|
|
164
292
|
**Comment:**
|
|
165
|
-
${
|
|
293
|
+
${limitedComment}`;
|
|
166
294
|
if (screenshot) {
|
|
167
|
-
|
|
295
|
+
try {
|
|
296
|
+
console.log("Uploading screenshot to repository...");
|
|
297
|
+
console.log("Screenshot path:", screenshotPath || ".feedback-vos");
|
|
298
|
+
const screenshotUrl = await uploadScreenshotToRepo(token, owner, repo, screenshot, screenshotPath);
|
|
299
|
+
console.log("Screenshot uploaded successfully, URL:", screenshotUrl);
|
|
300
|
+
body += `
|
|
168
301
|
|
|
169
302
|
**Screenshot:**
|
|
170
|
-
`;
|
|
304
|
+
console.log("Body length after adding screenshot URL:", body.length);
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error("Failed to upload screenshot to repository:", error);
|
|
307
|
+
body += `
|
|
308
|
+
|
|
309
|
+
**Screenshot:** Upload failed - screenshot not included.`;
|
|
310
|
+
console.log("Body length after upload failure:", body.length);
|
|
311
|
+
}
|
|
171
312
|
}
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
313
|
+
const SAFE_MAX_LENGTH = 65e3;
|
|
314
|
+
console.log(`Issue body length before final check: ${body.length} characters`);
|
|
315
|
+
if (body.length > SAFE_MAX_LENGTH) {
|
|
316
|
+
console.warn(`Body is too long (${body.length}), truncating...`);
|
|
317
|
+
const excess = body.length - SAFE_MAX_LENGTH;
|
|
318
|
+
const commentStartMarker = "\n\n**Comment:**\n";
|
|
319
|
+
const commentStart = body.indexOf(commentStartMarker);
|
|
320
|
+
const screenshotStart = body.indexOf("\n\n**Screenshot:**");
|
|
321
|
+
if (commentStart > 0) {
|
|
322
|
+
const commentStartPos = commentStart + commentStartMarker.length;
|
|
323
|
+
const commentEndPos = screenshotStart > 0 ? screenshotStart : body.length;
|
|
324
|
+
const currentComment = body.substring(commentStartPos, commentEndPos);
|
|
325
|
+
const newCommentLength = Math.max(100, currentComment.length - excess - 500);
|
|
326
|
+
const truncatedComment = currentComment.substring(0, newCommentLength) + "\n\n... (truncated due to size limit)";
|
|
327
|
+
const beforeComment = body.substring(0, commentStartPos);
|
|
328
|
+
const afterComment = screenshotStart > 0 ? body.substring(screenshotStart) : "";
|
|
329
|
+
body = beforeComment + truncatedComment + afterComment;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (body.length >= ABSOLUTE_MAX_LENGTH) {
|
|
333
|
+
console.error(`Body STILL too long after truncation: ${body.length} characters. Force truncating.`);
|
|
334
|
+
const baseText = `**Type:** ${type}
|
|
335
|
+
|
|
336
|
+
**Comment:**
|
|
337
|
+
`;
|
|
338
|
+
const maxCommentLength = ABSOLUTE_MAX_LENGTH - baseText.length - 1e3;
|
|
339
|
+
const safeComment = comment.substring(0, Math.max(100, maxCommentLength)) + "\n\n... (truncated due to size limit)";
|
|
340
|
+
body = baseText + safeComment;
|
|
341
|
+
const screenshotIndex = body.indexOf("\n\n**Screenshot:**");
|
|
342
|
+
if (screenshotIndex > 0) {
|
|
343
|
+
body = body.substring(0, screenshotIndex) + "\n\n**Screenshot:** Not included due to size limit.";
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (body.length >= ABSOLUTE_MAX_LENGTH) {
|
|
347
|
+
const minimalBase = `**Type:** ${type}
|
|
348
|
+
|
|
349
|
+
**Comment:**
|
|
350
|
+
`;
|
|
351
|
+
const maxSafeComment = ABSOLUTE_MAX_LENGTH - minimalBase.length - 100;
|
|
352
|
+
const minimalComment = comment.substring(0, Math.max(50, maxSafeComment)) + "\n\n... (truncated)";
|
|
353
|
+
body = minimalBase + minimalComment;
|
|
354
|
+
}
|
|
355
|
+
console.log(`Final issue body length: ${body.length} characters (limit: ${ABSOLUTE_MAX_LENGTH})`);
|
|
356
|
+
if (body.length >= ABSOLUTE_MAX_LENGTH) {
|
|
357
|
+
throw new Error(`CRITICAL: Cannot create issue - body is ${body.length} characters, exceeds GitHub limit of ${ABSOLUTE_MAX_LENGTH}.`);
|
|
358
|
+
}
|
|
359
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/issues`;
|
|
360
|
+
try {
|
|
361
|
+
const response = await fetch(url, {
|
|
175
362
|
method: "POST",
|
|
176
363
|
headers: {
|
|
177
|
-
"Authorization": `
|
|
364
|
+
"Authorization": `Bearer ${token}`,
|
|
178
365
|
"Accept": "application/vnd.github.v3+json",
|
|
179
|
-
"Content-Type": "application/json"
|
|
366
|
+
"Content-Type": "application/json",
|
|
367
|
+
"User-Agent": "feedback-vos"
|
|
180
368
|
},
|
|
181
369
|
body: JSON.stringify({
|
|
182
370
|
title,
|
|
183
371
|
body,
|
|
372
|
+
// Note: Labels must exist in the repository. If they don't exist, GitHub will ignore them.
|
|
373
|
+
// You can create these labels in your repository settings if needed.
|
|
184
374
|
labels: ["feedback", type.toLowerCase()]
|
|
185
375
|
})
|
|
376
|
+
});
|
|
377
|
+
if (!response.ok) {
|
|
378
|
+
let errorMessage = `GitHub API error (${response.status}): `;
|
|
379
|
+
try {
|
|
380
|
+
const errorData = await response.json();
|
|
381
|
+
errorMessage += errorData.message || JSON.stringify(errorData);
|
|
382
|
+
if (response.status === 404) {
|
|
383
|
+
errorMessage += `
|
|
384
|
+
|
|
385
|
+
Trying to access: ${url}`;
|
|
386
|
+
errorMessage += `
|
|
387
|
+
|
|
388
|
+
Possible causes:
|
|
389
|
+
`;
|
|
390
|
+
errorMessage += `- Repository "${owner}/${repo}" does not exist or is not accessible
|
|
391
|
+
`;
|
|
392
|
+
errorMessage += `- Token does not have access to this repository
|
|
393
|
+
`;
|
|
394
|
+
errorMessage += `- Check that owner and repo names are correct (case-sensitive)
|
|
395
|
+
`;
|
|
396
|
+
errorMessage += `- Verify the repository URL: https://github.com/${owner}/${repo}
|
|
397
|
+
`;
|
|
398
|
+
errorMessage += `- Make sure Issues are enabled in repository settings
|
|
399
|
+
`;
|
|
400
|
+
errorMessage += `
|
|
401
|
+
To debug:
|
|
402
|
+
`;
|
|
403
|
+
errorMessage += `1. Visit https://github.com/${owner}/${repo} to verify it exists
|
|
404
|
+
`;
|
|
405
|
+
errorMessage += `2. Check repository Settings \u2192 General \u2192 Features \u2192 Issues (must be enabled)
|
|
406
|
+
`;
|
|
407
|
+
errorMessage += `3. Verify your token has "repo" scope (for private) or "public_repo" scope (for public)
|
|
408
|
+
`;
|
|
409
|
+
errorMessage += `4. Test token access: curl -H "Authorization: Bearer YOUR_TOKEN" https://api.github.com/repos/${owner}/${repo}`;
|
|
410
|
+
} else if (response.status === 401) {
|
|
411
|
+
errorMessage += `
|
|
412
|
+
|
|
413
|
+
Possible causes:
|
|
414
|
+
`;
|
|
415
|
+
errorMessage += `- Invalid or expired GitHub token
|
|
416
|
+
`;
|
|
417
|
+
errorMessage += `- Token does not have the required "repo" or "public_repo" scope
|
|
418
|
+
`;
|
|
419
|
+
errorMessage += `- Generate a new token at https://github.com/settings/tokens`;
|
|
420
|
+
} else if (response.status === 422) {
|
|
421
|
+
errorMessage += `
|
|
422
|
+
|
|
423
|
+
Possible causes:
|
|
424
|
+
`;
|
|
425
|
+
errorMessage += `- Issue body is too long (maximum is 65536 characters)
|
|
426
|
+
`;
|
|
427
|
+
errorMessage += `- Screenshot is too large - it will be automatically compressed
|
|
428
|
+
`;
|
|
429
|
+
errorMessage += `- Try reducing the comment length or removing the screenshot
|
|
430
|
+
`;
|
|
431
|
+
errorMessage += `- Invalid label names (labels must exist in the repository)`;
|
|
432
|
+
} else if (response.status === 403) {
|
|
433
|
+
errorMessage += `
|
|
434
|
+
|
|
435
|
+
Possible causes:
|
|
436
|
+
`;
|
|
437
|
+
errorMessage += `- Token does not have permission to create issues
|
|
438
|
+
`;
|
|
439
|
+
errorMessage += `- Repository has issues disabled
|
|
440
|
+
`;
|
|
441
|
+
errorMessage += `- Rate limit exceeded (check GitHub API rate limits)
|
|
442
|
+
`;
|
|
443
|
+
errorMessage += `- Check repository Settings \u2192 General \u2192 Features \u2192 Issues`;
|
|
444
|
+
}
|
|
445
|
+
} catch (parseError) {
|
|
446
|
+
const errorText = await response.text();
|
|
447
|
+
errorMessage += errorText || "Unknown error";
|
|
448
|
+
}
|
|
449
|
+
throw new Error(errorMessage);
|
|
186
450
|
}
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
451
|
+
} catch (error) {
|
|
452
|
+
if (error instanceof Error) {
|
|
453
|
+
throw error;
|
|
454
|
+
}
|
|
455
|
+
throw new Error(`Failed to create GitHub issue: ${String(error)}`);
|
|
191
456
|
}
|
|
192
457
|
}
|
|
193
458
|
function FeedbackContentStep({
|
|
@@ -195,7 +460,6 @@ function FeedbackContentStep({
|
|
|
195
460
|
onFeedbackRestartRequest,
|
|
196
461
|
onFeedbackSent,
|
|
197
462
|
integration,
|
|
198
|
-
notionConfig,
|
|
199
463
|
githubConfig
|
|
200
464
|
}) {
|
|
201
465
|
const [screenshot, setScreenshot] = react$1.useState(null);
|
|
@@ -211,9 +475,7 @@ function FeedbackContentStep({
|
|
|
211
475
|
comment,
|
|
212
476
|
screenshot
|
|
213
477
|
};
|
|
214
|
-
if (integration === "
|
|
215
|
-
await sendToNotion(notionConfig, feedbackData);
|
|
216
|
-
} else if (integration === "github" && githubConfig) {
|
|
478
|
+
if (integration === "github") {
|
|
217
479
|
await sendToGitHub(githubConfig, feedbackData);
|
|
218
480
|
} else {
|
|
219
481
|
throw new Error("Invalid integration configuration");
|
|
@@ -254,7 +516,7 @@ function FeedbackContentStep({
|
|
|
254
516
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
255
517
|
"textarea",
|
|
256
518
|
{
|
|
257
|
-
className: "min-w-[384px] w-full min-h-[112px] text-sm \
|
|
519
|
+
className: "min-w-[384px] w-full min-h-[112px] text-sm \n placeholder-zinc-400 text-zinc-100 border-zinc-600 bg-transparent rounded-md \n focus:border-brand-500 focus:ring-brand-500 focus:ring-1 resize-none focus:outline-none\n scrollbar-thumb-zinc-700 scrollbar-track-transparent scrollbar-thin",
|
|
258
520
|
placeholder: "Tell in detail what is happening",
|
|
259
521
|
onChange: (e) => setComment(e.target.value)
|
|
260
522
|
}
|
|
@@ -272,7 +534,7 @@ function FeedbackContentStep({
|
|
|
272
534
|
{
|
|
273
535
|
type: "submit",
|
|
274
536
|
disabled: comment.length === 0 || isSendingFeedback,
|
|
275
|
-
className: "p-2 bg-brand-500 rounded-md border-transparent flex-1 justify-center\
|
|
537
|
+
className: "p-2 bg-brand-500 rounded-md border-transparent flex-1 justify-center\n items-center text-sm hover:bg-brand-300 focus:outline-none focus:ring-2\n focus:ring-offset-2 focus:ring-offset-zinc-900 focus:ring-brand-500\n transition-colors disabled:opacity-50 disabled:cursor-not-allowed\n disabled:hover:bg-brand-500",
|
|
276
538
|
children: isSendingFeedback ? /* @__PURE__ */ jsxRuntime.jsx(Loading, {}) : "Send feedback"
|
|
277
539
|
}
|
|
278
540
|
)
|
|
@@ -324,7 +586,7 @@ var feedbackTypes = {
|
|
|
324
586
|
}
|
|
325
587
|
}
|
|
326
588
|
};
|
|
327
|
-
function WidgetForm({ integration,
|
|
589
|
+
function WidgetForm({ integration, githubConfig }) {
|
|
328
590
|
const [feedbackType, setFeedbackType] = react$1.useState(null);
|
|
329
591
|
const [feedbackSent, setFeedbackSent] = react$1.useState(false);
|
|
330
592
|
function handleRestartFeedback() {
|
|
@@ -339,7 +601,6 @@ function WidgetForm({ integration, notionConfig, githubConfig }) {
|
|
|
339
601
|
onFeedbackRestartRequest: handleRestartFeedback,
|
|
340
602
|
onFeedbackSent: () => setFeedbackSent(true),
|
|
341
603
|
integration,
|
|
342
|
-
notionConfig,
|
|
343
604
|
githubConfig
|
|
344
605
|
}
|
|
345
606
|
) }),
|
|
@@ -359,7 +620,6 @@ function WidgetForm({ integration, notionConfig, githubConfig }) {
|
|
|
359
620
|
}
|
|
360
621
|
function Widget({
|
|
361
622
|
integration,
|
|
362
|
-
notionConfig,
|
|
363
623
|
githubConfig,
|
|
364
624
|
position = "bottom-right"
|
|
365
625
|
}) {
|
|
@@ -374,7 +634,6 @@ function Widget({
|
|
|
374
634
|
WidgetForm,
|
|
375
635
|
{
|
|
376
636
|
integration,
|
|
377
|
-
notionConfig,
|
|
378
637
|
githubConfig
|
|
379
638
|
}
|
|
380
639
|
) }),
|