/
DescriptionGeneratorButton.ts
98 lines (78 loc) · 3.64 KB
/
DescriptionGeneratorButton.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { createHtmlElement } from "../../../utils/createHtmlElement";
import openSaucedLogoIcon from "../../../assets/opensauced-icon.svg";
import { getPullRequestAPIURL } from "../../../utils/urlMatchers";
import { getDescriptionContext, isOutOfContextBounds } from "../../../utils/fetchGithubAPIData";
import { generateDescription } from "../../../utils/aiprdescription/openai";
import { GITHUB_PR_COMMENT_TEXT_AREA_SELECTOR, SUPABASE_LOGIN_URL } from "../../../constants";
import { insertTextAtCursor } from "../../../utils/aiprdescription/cursorPositionInsert";
import { getAIDescriptionConfig } from "../../../utils/aiprdescription/descriptionconfig";
import { getAuthToken, isLoggedIn } from "../../../utils/checkAuthentication";
export const DescriptionGeneratorButton = () => {
const descriptionGeneratorButton = createHtmlElement("a", {
id: "ai-description-button",
innerHTML: `<span id="ai-description-gen" class="toolbar-item btn-octicon">
<img class="octicon octicon-heading" height="16px" width="16px" id="ai-description-button-logo" src=${chrome.runtime.getURL(openSaucedLogoIcon)}>
</span>
<tool-tip for="ai-description-gen">Generate PR description</tool-tip>`,
onclick: handleSubmit,
});
return descriptionGeneratorButton;
};
const handleSubmit = async () => {
const logo = document.getElementById("ai-description-button-logo");
const button = document.getElementById("ai-description-button");
try {
if (!(await isLoggedIn())) {
return window.open(SUPABASE_LOGIN_URL, "_blank");
}
if (!logo || !button) {
return;
}
logo.classList.toggle("animate-spin");
button.classList.toggle("pointer-events-none");
const descriptionStream = await getAiDescription();
logo.classList.toggle("animate-spin");
button.classList.toggle("pointer-events-none");
const textArea = document.getElementsByName(GITHUB_PR_COMMENT_TEXT_AREA_SELECTOR)[0] as HTMLTextAreaElement;
insertTextAtCursor(textArea, descriptionStream);
} catch (error: unknown) {
logo?.classList.toggle("animate-spin");
button?.classList.toggle("pointer-events-none");
if (error instanceof Error) {
alert(error.message);
console.error("Description generation error:", error.message);
}
}
};
export const getAiDescription = async () => {
const { protocol, hostname, pathname } = window.location;
const url = getPullRequestAPIURL(`${protocol}//${hostname}${pathname}`);
const descriptionConfig = await getAIDescriptionConfig();
if (!descriptionConfig) {
throw new Error("Configuration file is empty!");
}
if (!descriptionConfig.enabled) {
throw new Error("AI PR description is disabled!");
}
const [diff, commitMessages] = await getDescriptionContext(url, descriptionConfig.config.source);
if (!diff && !commitMessages) {
throw new Error(`No input context was generated.`);
}
if (isOutOfContextBounds([diff, commitMessages], descriptionConfig.config.maxInputLength)) {
throw new Error(`Max input length exceeded. Try setting the description source to commit-messages.`);
}
const token = await getAuthToken();
const descriptionStream = await generateDescription(
token,
descriptionConfig.config.language,
descriptionConfig.config.length,
descriptionConfig.config.temperature / 10,
descriptionConfig.config.tone,
diff,
commitMessages,
);
if (!descriptionStream) {
throw new Error("No description was generated!");
}
return descriptionStream;
};