Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable AI description generator on comments #285

Merged
merged 11 commits into from
Jan 12, 2024
1 change: 0 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export const GITHUB_PROFILE_EDIT_MENU_CLASS = "button.js-profile-editable-edit-b
export const GITHUB_PR_COMMENT_HEADER_CLASS = "timeline-comment-header clearfix d-flex";
export const GITHUB_REVIEW_SUGGESTION_CLASS = "js-suggestion-button-placeholder";
export const GITHUB_REPO_ACTIONS_SELECTOR = ".pagehead-actions";
export const GITHUB_PR_COMMENT_TEXT_AREA_CLASS = "pull_request[body]";
export const GITHUB_PR_SUGGESTION_TEXT_AREA_Attribute = "[name='comment[body]']";
export const GITHUB_PR_BASE_BRANCH_CLASS = "css-truncate css-truncate-target";
export const LINKEDIN_PROJECT_FORM_SELECTOR = ".artdeco-text-input--input";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ import openSaucedLogoIcon from "../../../assets/opensauced-icon.svg";
import { getPullRequestAPIURL } from "../../../utils/urlMatchers";
import { getDescriptionContext, isOutOfContextBounds } from "../../../utils/fetchGithubAPIData";
import { generateDescription } from "../../../utils/ai-utils/openai";
import { GITHUB_PR_COMMENT_TEXT_AREA_CLASS } from "../../../constants";
import { insertTextAtCursor } from "../../../utils/ai-utils/cursorPositionInsert";
import { getAIDescriptionConfig } from "../../../utils/ai-utils/descriptionconfig";
import { getAuthToken, isLoggedIn, optLogIn } from "../../../utils/checkAuthentication";

export const DescriptionGeneratorButton = () => {
export const DescriptionGeneratorButton = (number: number) => {
const descriptionGeneratorButton = createHtmlElement("a", {
id: "ai-description-button",
id: `ai-description-button-${number}`,
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>
Expand All @@ -21,41 +20,39 @@ export const DescriptionGeneratorButton = () => {
return descriptionGeneratorButton;
};

const handleSubmit = async () => {
const logo = document.getElementById("ai-description-button-logo");
const button = document.getElementById("ai-description-button");
const handleSubmit = async (event: Event) => {
const button = event.currentTarget as HTMLElement;
const logo = button.querySelector("#ai-description-button-logo");


try {
if (!(await isLoggedIn())) {
return void optLogIn();
}

if (!logo || !button) {
return;
}

const descriptionConfig = await getAIDescriptionConfig();

if (!descriptionConfig) {
return;
}

logo.classList.toggle("animate-spin");
logo?.classList.toggle("animate-spin");
button.classList.toggle("pointer-events-none");


const { protocol, hostname, pathname } = window.location;
const descriptionStream = await getAiDescription(`${protocol}//${hostname}${pathname}`);

logo.classList.toggle("animate-spin");
logo?.classList.toggle("animate-spin");
button.classList.toggle("pointer-events-none");
const textArea = button.closest(".Box.CommentBox")?.querySelector("textarea");

const textArea = document.getElementsByName(GITHUB_PR_COMMENT_TEXT_AREA_CLASS)[0] as HTMLTextAreaElement;

insertTextAtCursor(textArea, descriptionStream);
if (textArea) {
insertTextAtCursor(textArea, descriptionStream);
}
} catch (error: unknown) {
logo?.classList.toggle("animate-spin");
button?.classList.toggle("pointer-events-none");
button.classList.toggle("pointer-events-none");

if (error instanceof Error) {
alert(error.message);
Expand Down
1 change: 1 addition & 0 deletions src/content-scripts/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const processGithubPage = async () => {
prReviewWatch(injectChangeSuggestorButton, 500);
} else if (isGithubPullRequestPage(window.location.href)) {
prEditWatch(injectDescriptionGeneratorButton, 500);
void injectDescriptionGeneratorButton();
void injectAddPRToHighlightsButton();
} else if (isGithubProfilePage(window.location.href)) {
const username = getGithubUsername(window.location.href);
Expand Down
5 changes: 4 additions & 1 deletion src/utils/ai-utils/cursorPositionInsert.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// This function is used to insert text at the cursor position in the text area
export const insertTextAtCursor = (textArea: HTMLTextAreaElement, text: string) => {
let length = 0;

textArea.focus();
const typewriter = setInterval(() => {
textArea.setRangeText(text[length++], textArea.selectionStart, textArea.selectionEnd, "end");
if (length >= text.length) {
clearInterval(typewriter);
textArea.setRangeText("\n\n_Generated using [OpenSauced](https://opensauced.ai/)._", textArea.selectionStart, textArea.selectionEnd, "end");
textArea.ownerDocument.execCommand("insertText", false, "\n\n_Generated using [OpenSauced](https://opensauced.ai/)_");
textArea.blur();
}
}, 10);
};
12 changes: 7 additions & 5 deletions src/utils/dom-utils/addDescriptionGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ const injectDescriptionGeneratorButton = async () => {
}
}

const firstPrDescription = document.querySelector(".ActionBar-item-container");
const firstPrDescription = document.querySelectorAll(".ActionBar-item-container");

if (firstPrDescription && !firstPrDescription.querySelector("#ai-description-button")) {
const addGeneratorButton = DescriptionGeneratorButton();
firstPrDescription.forEach((item, index) => {
if (!item.querySelector(`#ai-description-button-${index}`)) {
const addGeneratorButton = DescriptionGeneratorButton(index);

firstPrDescription.insertBefore(addGeneratorButton, firstPrDescription.firstChild);
}
item.insertBefore(addGeneratorButton, item.firstChild);
}
});
};

export default injectDescriptionGeneratorButton;
Loading