diff --git a/src/constants.ts b/src/constants.ts index 0f345ea0..52db8271 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,11 +6,13 @@ export const OPEN_SAUCED_INSIGHTS_DOMAIN = "insights.opensauced.pizza"; export const AI_PR_DESCRIPTION_CONFIG_KEY = "ai-pr-description-config"; // API endpoints -export const OPEN_SAUCED_USERS_ENDPOINT = "https://api.opensauced.pizza/v1/users"; -export const OPEN_SAUCED_REPOS_ENDPOINT = "https://api.opensauced.pizza/v1/repos"; -export const OPEN_SAUCED_SESSION_ENDPOINT = "https://api.opensauced.pizza/v1/auth/session"; -export const OPEN_SAUCED_USER_INSIGHTS_ENDPOINT = "https://api.opensauced.pizza/v1/user/insights"; -export const OPEN_SAUCED_AI_PR_DESCRIPTION_ENDPOINT = "https://api.opensauced.pizza/v1/prs/description/generate"; +export const OPEN_SAUCED_API_ENDPOINT = "https://api.opensauced.pizza/v1"; +export const OPEN_SAUCED_USERS_ENDPOINT = `${OPEN_SAUCED_API_ENDPOINT}/users`; +export const OPEN_SAUCED_REPOS_ENDPOINT = `${OPEN_SAUCED_API_ENDPOINT}/repos`; +export const OPEN_SAUCED_SESSION_ENDPOINT = `${OPEN_SAUCED_API_ENDPOINT}/auth/session`; +export const OPEN_SAUCED_USER_INSIGHTS_ENDPOINT = `${OPEN_SAUCED_API_ENDPOINT}/user/insights`; +export const OPEN_SAUCED_AI_PR_DESCRIPTION_ENDPOINT = `${OPEN_SAUCED_API_ENDPOINT}/prs/description/generate`; +export const OPEN_SAUCED_USER_HIGHLIGHTS_ENDPOINT = `${OPEN_SAUCED_API_ENDPOINT}/user/highlights`; export const OPEN_SAUCED_AI_CODE_REFACTOR_ENDPOINT = "https://api.opensauced.pizza/v1/prs/suggestion/generate"; // GitHub constants/selectors diff --git a/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts b/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts index b3f60926..7c468b3b 100644 --- a/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts +++ b/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts @@ -23,56 +23,69 @@ export const DescriptionGeneratorButton = () => { }; const handleSubmit = async () => { + const logo = document.getElementById("ai-description-button-logo") ?? null; + try { if (!(await isLoggedIn())) { return window.open(SUPABASE_LOGIN_URL, "_blank"); } - const logo = document.getElementById("ai-description-button-logo"); if (!logo) { return; } - const url = getPullRequestAPIURL(window.location.href); - const descriptionConfig = await getAIDescriptionConfig(); - - if (!descriptionConfig) { - return; - } - if (!descriptionConfig.enabled) { - return alert("AI PR description is disabled!"); - } logo.classList.toggle("animate-spin"); - const [diff, commitMessages] = await getDescriptionContext(url, descriptionConfig.config.source); - - if (!diff && !commitMessages) { - logo.classList.toggle("animate-spin"); - return alert(`No input context was generated.`); - } - if (isOutOfContextBounds([diff, commitMessages], descriptionConfig.config.maxInputLength)) { - logo.classList.toggle("animate-spin"); - return alert(`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, - ); + const descriptionStream = await getAiDescription(); logo.classList.toggle("animate-spin"); - if (!descriptionStream) { - return console.error("No description was generated!"); - } + const textArea = document.getElementsByName(GITHUB_PR_COMMENT_TEXT_AREA_SELECTOR)[0] as HTMLTextAreaElement; insertTextAtCursor(textArea, descriptionStream); } catch (error: unknown) { + logo?.classList.toggle("animate-spin"); + if (error instanceof Error) { + alert(error.message); console.error("Description generation error:", error.message); } } }; + +export const getAiDescription = async () => { + const url = getPullRequestAPIURL(window.location.href); + 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; +}; + diff --git a/src/content-scripts/github.ts b/src/content-scripts/github.ts index a1e4f9e3..c154de7c 100644 --- a/src/content-scripts/github.ts +++ b/src/content-scripts/github.ts @@ -16,6 +16,7 @@ import domUpdateWatch from "../utils/dom-utils/domUpdateWatcher"; import injectDescriptionGeneratorButton from "../utils/dom-utils/addDescriptionGenerator"; import injectChangeSuggestorButton from "../utils/dom-utils/changeSuggestorButton"; import prEditWatch, { prReviewWatch } from "../utils/dom-utils/prWatcher"; +import { getAiDescription } from "./components/GenerateAIDescription/DescriptionGeneratorButton"; const processGithubPage = async () => { if (prefersDarkMode(document.cookie)) { @@ -55,3 +56,27 @@ const processGithubPage = async () => { }; void processGithubPage(); + +chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { + switch (msg.type) { + case "get_highlight": { + const title = (document.querySelector(".js-issue-title.markdown-title") as HTMLHeadingElement)?.innerText; + + sendResponse(title); + break; + } + case "get_ai_description": { + const asyncRequest = async () => { + const aiText = await getAiDescription(); + + sendResponse(aiText); + }; + + asyncRequest().catch((e: Error | undefined) => { + sendResponse(e?.toString()); + console.error(e); + }); + return true; + } + } +}); diff --git a/src/pages/home.tsx b/src/pages/home.tsx index 8fc140d1..e5a5bc06 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -10,6 +10,9 @@ import { useOpensaucedUserCheck } from "../hooks/useOpensaucedUserCheck"; import { Profile } from "./profile"; import { goTo } from "react-chrome-extension-router"; import AIPRDescription from "./aiprdescription"; +import PostOnHighlight from "./posthighlight"; + + import Help from "./help"; const Home = () => { @@ -78,6 +81,16 @@ const Home = () => { AI Configuration + + {currentTabIsOpensaucedUser && ( + + OpenSauced logo + + + +
+ + setHighlightTitle(e.target.value)} + /> + +