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: OpenSauced AI PR description #79

Merged
merged 25 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8a488fa
WIP just starting
bdougie May 9, 2023
c883f70
not sure why it is not appending new element
bdougie May 9, 2023
d55c5e2
feat: AI highlight button
Anush008 May 10, 2023
0ea64df
Merge branch 'beta' into ai-powered-highlights
Anush008 May 13, 2023
dc3fc0f
feat: cursor position text insert, pr info
Anush008 May 14, 2023
cdb7139
feat: AI description config window
Anush008 May 14, 2023
ce45bb7
feat: pr actions dropdown
Anush008 May 14, 2023
a22d974
chore: Create PR description button
Anush008 May 15, 2023
2153e19
feat: OpenAI Completion
Anush008 May 15, 2023
8e9fe72
feat: type response from stream
Anush008 May 16, 2023
e1bda0d
feat: Config window new values, save button
Anush008 May 16, 2023
441562b
chore: Default description config setters
Anush008 May 16, 2023
6b22c7b
chore: form config submit
Anush008 May 16, 2023
75d4827
refactor: formatting, linting fixes
Anush008 May 16, 2023
a95e801
chore: description config type update, dropdown toggle
Anush008 May 16, 2023
8ae81ca
chore: disable state handle
Anush008 May 16, 2023
9ddcd50
chore: updated title
Anush008 May 17, 2023
1938c63
fix: missing base branch, spinner
Anush008 May 17, 2023
9147a42
chore: request errors to console
Anush008 May 17, 2023
08e709c
feat: conditional diff, commitmsg fetching
Anush008 May 17, 2023
3de3e44
feat: Input context length check, redirect when logged out
Anush008 May 17, 2023
22b243b
feat: highlights dropdown collapse
Anush008 May 17, 2023
be52e9f
feat: Edit PR button injection, tokenizer length check
Anush008 May 18, 2023
02494a5
feat: Private repository check
Anush008 May 18, 2023
2e35735
feat: "Description generated using OpenSauced" append
Anush008 May 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 89 additions & 1 deletion npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
"@types/chrome": "^0.0.231",
"@types/node-emoji": "^1.8.2",
"node-emoji": "^1.11.0",
"openai-streams": "^5.3.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-hot-toast": "^2.4.1",
"react-icons": "^4.8.0"
},
"devDependencies": {
"@crxjs/vite-plugin": "^1.0.14",
"@types/node": "^20.1.4",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
Expand Down
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Home from "./pages/home";
import Loading from "./pages/loading";
import { Profile } from "./pages/profile";
import { useAuth } from "./hooks/useAuth";
import AIPRDescription from "./pages/aiprdescription";

export const RouteContext = createContext<{ page: { name: string, props?: any }, setCurrentPage:(page: RouteKeys, props?: any) => void }>({ page: { name: "loading" }, setCurrentPage: () => {} });

Expand All @@ -13,6 +14,7 @@ const routes = {
home: <Home />,
loading: <Loading />,
profile: <Profile />,
aiprdescription: <AIPRDescription />,
};

type RouteKeys = keyof typeof routes;
Expand Down
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const SUPABASE_LOGIN_URL = "https://ibcwmlhcimymasokhgvn.supabase.co/auth
export const SUPABASE_AUTH_COOKIE_NAME = "supabase-auth-token";
export const OPEN_SAUCED_AUTH_TOKEN_KEY = "os-access-token";
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";
Expand All @@ -15,4 +16,6 @@ export const GITHUB_PROFILE_EDIT_MENU_SELECTOR = "button.js-profile-editable-edi
export const GITHUB_PR_AUTHOR_USERNAME_SELECTOR = "author Link--primary text-bold css-overflow-wrap-anywhere";
export const GITHUB_LOGGED_IN_USER_USERNAME_SELECTOR = "meta[name=\"user-login\"]";
export const GITHUB_PR_COMMENT_HEADER_SELECTOR = "timeline-comment-header clearfix d-flex";
export const GITHUB_PR_COMMENT_EDITOR_SELECTOR = "flex-nowrap d-none d-md-inline-block mr-md-0 mr-3";
export const GITHUB_REPO_ACTIONS_SELECTOR = ".pagehead-actions";
export const GITHUB_PR_COMMENT_TEXT_AREA_SELECTOR = "pull_request[body]";
Anush008 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@ import openSaucedLogoIcon from "../../../assets/opensauced-icon.svg";

export const AddPRToHighlightsButton = () => {
const addPRToHighlightsButton = createHtmlElement("a", {
href: `https://insights.opensauced.pizza/feed?url=${encodeURIComponent(window.location.href)}`,
target: "_blank",
rel: "noopener noreferrer",
innerHTML: `<span aria-label="Add PR to OpenSauced highlights." data-view-component="true" class="tooltipped tooltipped-n">
<img data-view-component="true" class="mr-1 mt-1" height="16px" width="16px" src=${chrome.runtime.getURL(openSaucedLogoIcon)}>
</span>`,
className: "relative",
innerHTML: `<span aria-label="Add PR to OpenSauced highlights" class="tooltipped tooltipped-n">
<img class="mr-1 mt-1" height="16px" width="16px" src=${chrome.runtime.getURL(
openSaucedLogoIcon,
)}>
</span>
<details-menu id="details-menu-os" class="dropdown-menu hidden dropdown-menu-sw color-fg-default w-48 mt-2">
<a href="https://insights.opensauced.pizza/feed?prurl=${encodeURIComponent(window.location.href)}" class="dropdown-item" target="_blank">
Add PR to Highlights
</a>
</details-menu>`,
onclick: () => {
const menu = document.getElementById("details-menu-os");
if (!menu) return;
menu.classList.toggle("hidden");
}
});

return addPRToHighlightsButton;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { createHtmlElement } from "../../../utils/createHtmlElement";
import openSaucedLogoIcon from "../../../assets/opensauced-icon.svg";
import { getPullRequestAPIURL } from "../../../utils/urlMatchers";
import { getPRDiff, getPRCommitMessages } from "../../../utils/aiprdescription/fetchGithubAPIData";
import { generateDescription } from "../../../utils/aiprdescription/openai";
import { GITHUB_PR_COMMENT_TEXT_AREA_SELECTOR } from "../../../constants";
import { insertAtCursorFromStream } from "../../../utils/aiprdescription/cursorPositionInsert";
import { getAIDescriptionConfig } from "../../../utils/aiprdescription/descriptionconfig";

export const DescriptionGeneratorButton = () => {
const descriptionGeneratorButton = createHtmlElement("a", {
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: async () => {
const logo = document.getElementById("ai-description-button-logo");
if (!logo) {
return alert("Logo not found!");
}
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-pulse");
//TODO: Conditionally fetch diff and commit messages based on config
const [diff, commitMessages] = await Promise.all([getPRDiff(url), getPRCommitMessages(url)]);
const descriptionStream = await generateDescription(descriptionConfig.config.openai_api_key!,
"gpt-3.5-turbo",
descriptionConfig.config.language,
descriptionConfig.config.length,
descriptionConfig.config.temperature / 10,
descriptionConfig.config.tone, diff, commitMessages
);

logo.classList.toggle("animate-pulse");
if (!descriptionStream) {
return alert("No description was generated!");
Anush008 marked this conversation as resolved.
Show resolved Hide resolved
}
const textArea = document.getElementsByName(GITHUB_PR_COMMENT_TEXT_AREA_SELECTOR)[0] as HTMLTextAreaElement;

void insertAtCursorFromStream(textArea, descriptionStream);
},

});

return descriptionGeneratorButton;
};
11 changes: 7 additions & 4 deletions src/content-scripts/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
isGithubProfilePage,
isGithubPullRequestPage,
isGithubRepoPage,
isPullRequestCreatePage,
} from "../utils/urlMatchers";
import { isOpenSaucedUser } from "../utils/fetchOpenSaucedApiData";
import injectViewOnOpenSauced from "../utils/dom-utils/viewOnOpenSauced";
Expand All @@ -11,14 +12,16 @@ import { prefersDarkMode } from "../utils/colorPreference";
import injectAddPRToHighlightsButton from "../utils/dom-utils/addPRToHighlights";
import injectRepoVotingButtons from "../utils/dom-utils/repoVotingButtons";
import domUpdateWatch from "../utils/dom-utils/domUpdateWatcher";
import injectDescriptionGeneratorButton from "../utils/dom-utils/addDescriptionGenerator";

const processGithubPage = async () => {
if (prefersDarkMode(document.cookie)) {
document.documentElement.classList.add("dark");
}

if (isGithubPullRequestPage(window.location.href)) {
await injectAddPRToHighlightsButton();
if (isPullRequestCreatePage(window.location.href)) {
injectDescriptionGeneratorButton();
} else if (isGithubPullRequestPage(window.location.href)) {
void injectAddPRToHighlightsButton();
} else if (isGithubProfilePage(window.location.href)) {
const username = getGithubUsername(window.location.href);

Expand All @@ -37,7 +40,7 @@ const processGithubPage = async () => {
await injectRepoVotingButtons(ownerName, repoName);
}

domUpdateWatch(processGithubPage, 25);
domUpdateWatch(processGithubPage, 50);
};

void processGithubPage();
11 changes: 11 additions & 0 deletions src/hooks/useRefs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useRef } from "react";

export const useRefs = () => {
const refs = useRef<Record<string, HTMLElement | null>>({});

const setRefFromKey = (key: string) => (element: HTMLElement | null) => {
refs.current[key] = element;
};

return { refs: refs.current, setRefFromKey };
};
Loading