diff --git a/.env.development b/.env.development index ceb179c3..7ef52858 100644 --- a/.env.development +++ b/.env.development @@ -1,3 +1,3 @@ VITE_OPEN_SAUCED_INSIGHTS_DOMAIN = "beta.app.opensauced.pizza" -VITE_OPEN_SAUCED_API_ENDPOINT = "https://beta.api.opensauced.pizza/v1" -VITE_OPEN_SAUCED_SUPABASE_ID = "fcqqkxwlntnrtjfbcioz" \ No newline at end of file +VITE_OPEN_SAUCED_API_ENDPOINT = "https://beta.api.opensauced.pizza/v2" +VITE_OPEN_SAUCED_SUPABASE_ID = "fcqqkxwlntnrtjfbcioz" diff --git a/.env.production b/.env.production index 055ccdde..df6b0f36 100644 --- a/.env.production +++ b/.env.production @@ -1,3 +1,3 @@ VITE_OPEN_SAUCED_INSIGHTS_DOMAIN = "app.opensauced.pizza" -VITE_OPEN_SAUCED_API_ENDPOINT = "https://api.opensauced.pizza/v1" -VITE_OPEN_SAUCED_SUPABASE_ID = "ibcwmlhcimymasokhgvn" \ No newline at end of file +VITE_OPEN_SAUCED_API_ENDPOINT = "https://api.opensauced.pizza/v2" +VITE_OPEN_SAUCED_SUPABASE_ID = "ibcwmlhcimymasokhgvn" diff --git a/.env.test b/.env.test index ceb179c3..7ef52858 100644 --- a/.env.test +++ b/.env.test @@ -1,3 +1,3 @@ VITE_OPEN_SAUCED_INSIGHTS_DOMAIN = "beta.app.opensauced.pizza" -VITE_OPEN_SAUCED_API_ENDPOINT = "https://beta.api.opensauced.pizza/v1" -VITE_OPEN_SAUCED_SUPABASE_ID = "fcqqkxwlntnrtjfbcioz" \ No newline at end of file +VITE_OPEN_SAUCED_API_ENDPOINT = "https://beta.api.opensauced.pizza/v2" +VITE_OPEN_SAUCED_SUPABASE_ID = "fcqqkxwlntnrtjfbcioz" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..b5733b4b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# Explicitly declare text files you want to always be normalized and converted to native line endings on checkout. +*.ts text eol=lf +*.tsx text eol=lf +*.js text eol=lf +*.json text eol=lf +*.md text eol=lf diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c6d6304..2b9c4849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,43 @@ > All notable changes to this project will be documented in this file +## [1.15.0-beta.2](https://github.com/open-sauced/ai/compare/v1.15.0-beta.1...v1.15.0-beta.2) (2024-01-25) + + +### 🍕 Features + +* Upgrade to v2 API ([693d459](https://github.com/open-sauced/ai/commit/693d45998dba2cacc18be10b397fdc624668fb03)) + +## [1.15.0-beta.1](https://github.com/open-sauced/ai/compare/v1.14.3-beta.3...v1.15.0-beta.1) (2024-01-12) + + +### 🍕 Features + +* enable AI description generator on comments ([#285](https://github.com/open-sauced/ai/issues/285)) ([a30380b](https://github.com/open-sauced/ai/commit/a30380b1def14b97cf28f3945a1608cca6e3ba54)) + +## [1.14.3-beta.3](https://github.com/open-sauced/ai/compare/v1.14.3-beta.2...v1.14.3-beta.3) (2024-01-12) + + +### 🐛 Bug Fixes + +* Link to Usage Guide and Update README ([#298](https://github.com/open-sauced/ai/issues/298)) ([ba5cef6](https://github.com/open-sauced/ai/commit/ba5cef65dad7c2bc8340e274195b8a5b37e57b4f)) + +## [1.14.3-beta.2](https://github.com/open-sauced/ai/compare/v1.14.3-beta.1...v1.14.3-beta.2) (2024-01-12) + + +### 🐛 Bug Fixes + +* add `.gitattributes` file and normalize all the line endings ([#301](https://github.com/open-sauced/ai/issues/301)) ([a6279ea](https://github.com/open-sauced/ai/commit/a6279eaea274922b16b8854ab7b4fb39b66de69c)) +* chunk processing in Chat component ([#289](https://github.com/open-sauced/ai/issues/289)) ([f1c29ff](https://github.com/open-sauced/ai/commit/f1c29ff9e219c46a768bc46b22553bb8a436e6c1)) + +## [1.14.3-beta.1](https://github.com/open-sauced/ai/compare/v1.14.2...v1.14.3-beta.1) (2023-11-09) + + +### 🐛 Bug Fixes + +* change the classname and remove extra code ([241c385](https://github.com/open-sauced/ai/commit/241c385e82f3757b95b045423fbb01e512772bfd)) +* elemenet to the dom if it's not added already ([c654b82](https://github.com/open-sauced/ai/commit/c654b826d2443adc6cffbeaa61970b6294596ca6)) + ## [1.14.2](https://github.com/open-sauced/ai/compare/v1.14.1...v1.14.2) (2023-11-06) diff --git a/README.md b/README.md index 9b0080d5..2ce5b251 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,12 @@ Leverage AI to generate pull request descriptions based on the diff & commit mes ## Documentation -The developer documentation for the project can be found [here](./docs/). For a usage guide, click [here](https://docs.opensauced.pizza/chrome-extension/introduction-to-the-chrome-extension/). +You can find the documentation for the Chrome extension below: -## Running the project locally +- [Developer documentation](./docs/) for the project +- [Usage guide](https://docs.opensauced.pizza/tools/chrome-extension/introduction-to-the-chrome-extension/) + +## Running the Project Locally To run the project, you'll need the following software binaries installed on your development machines: @@ -38,32 +41,33 @@ To install the project dependencies: npm ci ``` +To run a local instance of the project on: -To run a local instance of the project: - -### On development environment +### Development Environment ```shell npm run dev ``` -### On Production environment +### Production Environment ```shell npm run build npm run preview ``` +## Installing the Local Build on a Chromium-Based Browser: -## Installing the local build on a Chromium based browser: After running the above commands, -1. Navigate to `chrome://extensions`. + +1. Navigate to `chrome://extensions`. 2. Enable the `Developer Mode`. 3. Select `Load unpacked` and choose the generated `dist` directory from the project folder to install it. You should now have the extension installed and running. ## 🙌🏼 Core Team +
@@ -79,7 +83,9 @@ You should now have the extension installed and running. ## 🤝 Contributing -We encourage you to contribute to OpenSauced! Please check out the [Contributing guide](https://docs.opensauced.pizza/contributing/introduction-to-contributing/) for guidelines about how to proceed. +_We encourage contributors to open issues or take a look at the [Bugs](https://github.com/orgs/open-sauced/projects/6). If you would like to find **good first issues**, please check out the `#good-first-issues` channel in our [Discord](https://discord.gg/opensauced)._ + +We encourage you to contribute to OpenSauced! Please check out the [Contributing Guide](https://docs.opensauced.pizza/contributing/introduction-to-contributing/) for guidelines about how to self-assign an issue and how to get started. We have a commit utility called [@open-sauced/conventional-commit](https://github.com/open-sauced/conventional-commit) that helps you write your commits in a way that is easy to understand and process by others. @@ -91,12 +97,12 @@ The `beta` branch is the default branch. We squash & merge PRs to the `beta` bra A merge to `beta` will trigger a beta release. A merge to `main` will trigger a full release. -Make sure to checkout the beta branch for the latest changes, and follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification when writing commit messages. +Make sure to checkout the `beta` branch for the latest changes, and follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification when writing commit messages. ## 🍕 Community Got Questions? Join the conversation in our [Discord](https://discord.gg/U2peSNf23P). -Find Open Sauced videos and release overviews on our [YouTube Channel](https://www.youtube.com/channel/UCklWxKrTti61ZCROE1e5-MQ). +Find Open Sauced videos and release overviews on our [YouTube Channel](https://www.youtube.com/channel/UCklWxKrTti61ZCROE1e5-MQ), and check out the resources on our [Dev.to org](https://dev.to/opensauced). ## ⚖️ LICENSE diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index db15227e..ae64c6bb 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,12 +1,12 @@ { "name": "opensauced-browser-extension", - "version": "1.14.2", + "version": "1.15.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "opensauced-browser-extension", - "version": "1.14.2", + "version": "1.15.0-beta.2", "dependencies": { "date-fns": "^2.30.0", "gpt-tokenizer": "^1.0.5", diff --git a/package.json b/package.json index 2464b7d3..3e6701ba 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "opensauced-browser-extension", "private": true, - "version": "1.14.2", + "version": "1.15.0-beta.2", "files": [ "dist" ], diff --git a/src/constants.ts b/src/constants.ts index a2183cfe..68fe569e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -30,20 +30,23 @@ export const OPEN_SAUCED_EMOJIS_ENDPOINT = `${OPEN_SAUCED_API_ENDPOINT}/emojis`; // Content-scripts selectors export const GITHUB_PROFILE_MENU_SELECTOR = ".p-nickname.vcard-username.d-block"; -export const GITHUB_PROFILE_EDIT_MENU_SELECTOR = "button.js-profile-editable-edit-button"; -export const GITHUB_PR_COMMENT_HEADER_SELECTOR = "timeline-comment-header clearfix d-flex"; -export const GITHUB_NEW_PR_COMMENT_EDITOR_SELECTOR = "flex-nowrap d-none d-md-inline-block mr-md-0 mr-3"; -export const GITHUB_PR_COMMENT_EDITOR_SELECTOR = "flex-nowrap d-inline-block mr-3"; -export const GITHUB_REVIEW_SUGGESTION_SELECTOR = "js-suggestion-button-placeholder"; +export const GITHUB_PROFILE_EDIT_MENU_CLASS = "button.js-profile-editable-edit-button"; +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_SELECTOR = "pull_request[body]"; -export const GITHUB_PR_SUGGESTION_TEXT_AREA_SELECTOR = "[name='comment[body]']"; -export const GITHUB_PR_BASE_BRANCH_SELECTOR = "css-truncate css-truncate-target"; +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"; // External Links export const EXTERNAL_RESOURCES = [ - { link: "https://docs.opensauced.pizza/chrome-extension/introduction-to-the-chrome-extension/", key: "Docs" }, + { + link: "https://docs.opensauced.pizza/tools/chrome-extension/introduction-to-the-chrome-extension/", + key: "Docs", + }, { link: "https://github.com/open-sauced/ai/issues", key: "Issues" }, - { link: "https://github.com/orgs/open-sauced/discussions", key: "Discussions" }, + { + link: "https://github.com/orgs/open-sauced/discussions", + key: "Discussions", + }, ]; diff --git a/src/content-scripts/components/AICodeReview/AICodeReviewMenu.ts b/src/content-scripts/components/AICodeReview/AICodeReviewMenu.ts index 9c52ba7a..dc5c05a2 100644 --- a/src/content-scripts/components/AICodeReview/AICodeReviewMenu.ts +++ b/src/content-scripts/components/AICodeReview/AICodeReviewMenu.ts @@ -1,4 +1,4 @@ -import { GITHUB_PR_SUGGESTION_TEXT_AREA_SELECTOR } from "../../../constants"; +import { GITHUB_PR_SUGGESTION_TEXT_AREA_Attribute } from "../../../constants"; import { insertTextAtCursor } from "../../../utils/ai-utils/cursorPositionInsert"; import { DescriptionConfig, @@ -130,7 +130,7 @@ const handleSubmit = async ( return console.error("No description was generated!"); } const textArea = commentNode.querySelector( - GITHUB_PR_SUGGESTION_TEXT_AREA_SELECTOR, + GITHUB_PR_SUGGESTION_TEXT_AREA_Attribute, )!; insertTextAtCursor(textArea as HTMLTextAreaElement, suggestionStream); diff --git a/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts b/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts index dd6a1a61..12803465 100644 --- a/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts +++ b/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts @@ -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_SELECTOR } 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: ` @@ -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_SELECTOR)[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); diff --git a/src/content-scripts/github.ts b/src/content-scripts/github.ts index 1c8ba0aa..26fcc681 100644 --- a/src/content-scripts/github.ts +++ b/src/content-scripts/github.ts @@ -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); diff --git a/src/repo-query/pages/chat.tsx b/src/repo-query/pages/chat.tsx index 2b40cd5c..7717bc45 100644 --- a/src/repo-query/pages/chat.tsx +++ b/src/repo-query/pages/chat.tsx @@ -18,7 +18,8 @@ export const Chat = ({ ownerName, repoName }: { ownerName: string, repoName: str const processChunk = (chunk: string) => { const chunkLines = chunk.split("\n"); - const [eventLine, dataLine] = chunkLines; + const indexOfEvent = chunkLines.findIndex(e => e.includes("event: ")); + const [eventLine, dataLine] = [chunkLines[indexOfEvent], chunkLines[indexOfEvent + 1]]; const event = eventLine.split(": ")[1]; let data:any; @@ -26,7 +27,7 @@ export const Chat = ({ ownerName, repoName }: { ownerName: string, repoName: str try { data = JSON.parse(dataLine.split(": ")[1]); } catch (e) { - console.error(dataLine); + console.error("The recieved data line", dataLine); // remove quotes from string data = dataLine.split("data: ")[1].substring(1, dataLine.split("data: ")[1].length - 2); diff --git a/src/utils/ai-utils/cursorPositionInsert.ts b/src/utils/ai-utils/cursorPositionInsert.ts index e9fbcb8d..688db207 100644 --- a/src/utils/ai-utils/cursorPositionInsert.ts +++ b/src/utils/ai-utils/cursorPositionInsert.ts @@ -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); }; diff --git a/src/utils/dom-utils/addDescriptionGenerator.ts b/src/utils/dom-utils/addDescriptionGenerator.ts index 1c75f2b0..22534a46 100644 --- a/src/utils/dom-utils/addDescriptionGenerator.ts +++ b/src/utils/dom-utils/addDescriptionGenerator.ts @@ -1,6 +1,4 @@ import { DescriptionGeneratorButton } from "../../content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton"; -import { GITHUB_NEW_PR_COMMENT_EDITOR_SELECTOR, GITHUB_PR_COMMENT_EDITOR_SELECTOR } from "../../constants"; -import { isGithubPullRequestPage } from "../urlMatchers"; import { isPublicRepository } from "../fetchGithubAPIData"; import { SettingsConfig } from "../../popup/pages/settings"; @@ -23,11 +21,15 @@ const injectDescriptionGeneratorButton = async () => { } } - const selector = isGithubPullRequestPage(window.location.href) ? GITHUB_PR_COMMENT_EDITOR_SELECTOR : GITHUB_NEW_PR_COMMENT_EDITOR_SELECTOR; - const commentFormatRow = document.getElementsByClassName(selector)[0]; - const addGeneratorButton = DescriptionGeneratorButton(); + const firstPrDescription = document.querySelectorAll(".ActionBar-item-container"); - commentFormatRow.insertBefore(addGeneratorButton, commentFormatRow.firstChild); + firstPrDescription.forEach((item, index) => { + if (!item.querySelector(`#ai-description-button-${index}`)) { + const addGeneratorButton = DescriptionGeneratorButton(index); + + item.insertBefore(addGeneratorButton, item.firstChild); + } + }); }; export default injectDescriptionGeneratorButton; diff --git a/src/utils/dom-utils/addPRToHighlights.ts b/src/utils/dom-utils/addPRToHighlights.ts index 4d2f5792..26709de7 100644 --- a/src/utils/dom-utils/addPRToHighlights.ts +++ b/src/utils/dom-utils/addPRToHighlights.ts @@ -1,5 +1,5 @@ import { AddPRToHighlightsButton } from "../../content-scripts/components/AddPRToHighlights/AddPRToHighlightsButton"; -import { GITHUB_PR_COMMENT_HEADER_SELECTOR } from "../../constants"; +import { GITHUB_PR_COMMENT_HEADER_CLASS } from "../../constants"; import { isLoggedIn } from "../checkAuthentication"; import { isPublicRepository } from "../fetchGithubAPIData"; @@ -9,7 +9,7 @@ const injectAddPRToHighlightsButton = async () => { } const commentFormatRow = document.getElementsByClassName( - GITHUB_PR_COMMENT_HEADER_SELECTOR, + GITHUB_PR_COMMENT_HEADER_CLASS, )[0]; const addPRToHighlightsButton = AddPRToHighlightsButton(); diff --git a/src/utils/dom-utils/changeSuggestorButton.ts b/src/utils/dom-utils/changeSuggestorButton.ts index d91ccdda..5d70e334 100644 --- a/src/utils/dom-utils/changeSuggestorButton.ts +++ b/src/utils/dom-utils/changeSuggestorButton.ts @@ -1,5 +1,5 @@ import { AICodeReviewButton } from "../../content-scripts/components/AICodeReview/AICodeReviewButton"; -import { GITHUB_REVIEW_SUGGESTION_SELECTOR } from "../../constants"; +import { GITHUB_REVIEW_SUGGESTION_CLASS } from "../../constants"; import { isPublicRepository } from "../fetchGithubAPIData"; import { SettingsConfig } from "../../popup/pages/settings"; @@ -22,7 +22,7 @@ const injectChangeSuggestorButton = async (commentNode: HTMLElement) => { } } - const suggestChangesIcon = commentNode.getElementsByClassName(GITHUB_REVIEW_SUGGESTION_SELECTOR)[0]; + const suggestChangesIcon = commentNode.getElementsByClassName(GITHUB_REVIEW_SUGGESTION_CLASS)[0]; const changeSuggestorButton = AICodeReviewButton(commentNode); if (suggestChangesIcon.querySelector("#os-ai-change-gen")) {