Compare commits

..

19 Commits

Author SHA1 Message Date
Danny Avila
47d0184990 npm all prod(deps): bump mdast-util-from-markdown from 1.3.0 to 1.3.1 (#447) (#453)
Bumps [mdast-util-from-markdown](https://github.com/syntax-tree/mdast-util-from-markdown) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/syntax-tree/mdast-util-from-markdown/releases)
- [Commits](https://github.com/syntax-tree/mdast-util-from-markdown/compare/1.3.0...1.3.1)

---
updated-dependencies:
- dependency-name: mdast-util-from-markdown
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 12:15:53 -04:00
Danny Avila
ee59fa40f5 npm all prod(deps): bump @babel/plugin-transform-react-jsx (#446) (#452)
Bumps [@babel/plugin-transform-react-jsx](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-react-jsx) from 7.21.5 to 7.22.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.22.3/packages/babel-plugin-transform-react-jsx)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-react-jsx"
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 12:10:41 -04:00
Danny Avila
68b3731d06 npm all prod(deps): bump micromark-extension-gfm-task-list-item (#445) (#451)
Bumps [micromark-extension-gfm-task-list-item](https://github.com/micromark/micromark-extension-gfm-task-list-item) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/micromark/micromark-extension-gfm-task-list-item/releases)
- [Commits](https://github.com/micromark/micromark-extension-gfm-task-list-item/compare/1.0.4...1.0.5)

---
updated-dependencies:
- dependency-name: micromark-extension-gfm-task-list-item
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 12:05:58 -04:00
Danny Avila
e4889ff8bb npm all prod(deps): bump postcss-double-position-gradients (#444) (#450)
Bumps [postcss-double-position-gradients](https://github.com/csstools/postcss-plugins/tree/HEAD/plugins/postcss-double-position-gradients) from 4.0.3 to 4.0.4.
- [Changelog](https://github.com/csstools/postcss-plugins/blob/main/plugins/postcss-double-position-gradients/CHANGELOG.md)
- [Commits](https://github.com/csstools/postcss-plugins/commits/HEAD/plugins/postcss-double-position-gradients)

---
updated-dependencies:
- dependency-name: postcss-double-position-gradients
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 12:01:23 -04:00
Danny Avila
2c026d11a5 npm all prod(deps): bump @babel/helper-member-expression-to-functions (#443) (#449)
Bumps [@babel/helper-member-expression-to-functions](https://github.com/babel/babel/tree/HEAD/packages/babel-helper-member-expression-to-functions) from 7.21.5 to 7.22.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.22.3/packages/babel-helper-member-expression-to-functions)

---
updated-dependencies:
- dependency-name: "@babel/helper-member-expression-to-functions"
  dependency-type: indirect
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 11:55:15 -04:00
Danny Avila
1a252170f5 chore(package): bump meilisearch (#448)
* npm api prod(deps): bump meilisearch from 0.32.5 to 0.33.0 in /api (#436)

Bumps [meilisearch](https://github.com/meilisearch/meilisearch-js) from 0.32.5 to 0.33.0.
- [Release notes](https://github.com/meilisearch/meilisearch-js/releases)
- [Commits](https://github.com/meilisearch/meilisearch-js/compare/v0.32.5...v0.33.0)

---
updated-dependencies:
- dependency-name: meilisearch
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(package): update meilisearch package from 0.32.3 to 0.33.0
chore(package): update cross-fetch package from 3.1.5 to 3.1.6 in meilisearch package dependencies

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-05 11:48:05 -04:00
Danny Avila
d40aaa703d chore: change dependabot settings for all packages (#442) 2023-06-05 11:22:14 -04:00
Danny Avila
44005258fc chore: create develop branch and change dependabot settings (#435) 2023-06-05 10:35:04 -04:00
Danny Avila
19495a461d chore(.gitignore): add .env.test to gitignore (#424)
feat(api): update @waylaidwanderer/chatgpt-api to version 1.37.0
2023-06-03 08:23:11 -04:00
Danny Avila
fcf068dddf style(NewConversationMenu): change dropdown menu background color to dark gray (#419)
style(EndpointItem): change active background color to light gray
2023-06-02 00:35:43 -04:00
Anirudh
7468b3011f Added Settings Modal (#342)
* Improve UI with style changes and add Settings button

- Improved the UI of the `Input` and `Message` components.
- Added a `Settings` button to the `NavLinks` component.
- Introduced a `Settings` component to handle user settings.
- Refactored the `Dialog` component for consistency.

* Revert not needed changes

* Updated style.css to only work for select

* feat: Remove Dark Mode component and add theme selection feature

This commit removes the Dark Mode component from the navigation bar and replaces it with a theme selection dropdown menu in the Settings dialog. The implementation of the theme selection feature includes a function that allows the user to set the theme based on the system, light, or dark mode.

* Add auto theme setting to Settings component.

This commit adds a new state variable to keep track of whether the auto theme is enabled or not. It also registers an event listener to update the theme based on system preference changes. The event listener is removed when the component is unmounted.

* Improve user experience by allowing customized themes
- Create `selectedOption` state to track user-selected theme
- Remove unused `isAutoTheme` state variable

* feat(Nav): Add SVG icon to settings gear

This commit adds an SVG icon to the settings gear in the Navigation component's Settings file. The new SVG icon replaces the previous GearIcon component.

* refactor(ui): Update overlay background color

This commit updates the background color of the overlay in the AlertDialog and Dialog components by changing the classes applied to the elements. The new color is a transition from `bg-black/50 backdrop-blur-sm` to `bg-gray-500/90 dark:bg-gray-800/90`. This change improves the readability of the dialog boxes.

* Refactor ThemeContext to include system theme and fix bug in Settings

The ThemeContext now includes a "system" theme and ClearConvos no longer relies on the "selected option" state to update the theme. The bug is now fixed if the system theme changes.

* Refactor DialogTemplate styles and color scheme

Adjusted the color scheme of the DialogTemplate component to dark mode, updated the background color to gray-900 and removed unnecessary classes.

* Refactor: Change button logic to require confirmation before clearing convos

This commit refactors the code by adding a confirmation dialog to prompt for a user's confirmation before clearing all conversations in the Settings.jsx file. The change ensures the user is aware of the irreversible action before initiating the clearConvos function. Additionally, the commit updates the clear chat button's class name and changes the button's onClick logic to call the confirmClearConvos function instead of directly invoking the clearConvos method.

* Refactor component name to reflect functionality change.

- Changed component name from ClearConvos to Settings to support potential future use cases.

* Refactor conversation clearing functionality in `Settings.jsx`

This commit optimizes the conversation clearing functionality in the `Settings.jsx` component by removing the `confirmClearConvos` function and directly calling the `clearConvos` function on confirmation. This change will simplify the code and improve the user experience.

* Refactor Input component UI styles

Simplify Input component styles by simplifying the gradient background, removing border color styles, and updating button styles.

* feat: Add e2e test for Settings modal

This commit adds an e2e test to verify whether the Settings modal is displayed on the landing page. It uses a headless browser to navigate to the page and interacts with it to verify if the dialog and its components are visible.

* test: Add Navigation and Settings tests

Add Navigation and Settings tests to verify that the navigation bar and Settings button are visible and that the Settings modal displays the expected content. The settings modal verification includes checking whether the modal is visible, if the modal title, tab list, clear conversation button and theme are present, and if the theme option can be selected to change the mode.

* Quick fix

* feat(navbar): Add confirmation before clearing conversations

Adding confirmation modal to prevent accidentally clearing conversations. Before, once you clicked on the "Clear" button it immediately clears all conversations. With this change, if you click on "Clear" the first time, it will change the text to "Confirm Clear" and if you click it again, it will clear all conversations.

* Add click functionality to the navigation bar and improve UI design

The code introduced click functionality to the nav bar and improved the user interface. It also used the new theme select feature to change the theme to dark.

* test: Add test for dark mode theme change

Refactor the test for Navigation suite to check for the 'dark' class in the HTML element when the 'dark' theme is selected in the modal. This ensures that the dark mode theme change works correctly, and improves test coverage.

* Improve navigation test clarity

This commit improves code clarity and adds more detailed test assertions to the navigation suite. New assert statements are added to check whether the modal theme selection changes the theme and that the HTML element receives the 'dark' class. A new function `changeMode` was introduced to avoid code repetition. A short description was added to the commit message to adhere to best practices.

* Improve navigation test clarity

This commit improves code clarity and adds more detailed test assertions to the navigation suite. New assert statements are added to check whether the modal theme selection changes the theme and that the HTML element receives the 'dark' class. A new function `changeMode` was introduced to avoid code repetition. A short description was added to the commit message to adhere to best practices.

* Hotfix

* Removed repetation

* Refactor: Change text-gray-400 to text-white/50 to make tailwind more cleaner

* style: Update CSS classes to improve the conversation UI

- Update Conversation component to improve UX
- Changed styling for group hover effect using shades of gray
- Improved color contrast of the Message component for easy readability
- Replaced class names in buildTree.js with a new class name
- Added a new color theme (gray-1000) in tailwind.config to replace an old background color.

* Refactor EndpointItem, EndpointItems, and NewConversationMenu for better user experience

- The `EndpointItem` component now accepts an `isSelected` prop instead of `onSelect` to better reflect its usage in `EndpointItems` and `NewConversationMenu`.
- `EndpointItems` component now has a `selectedEndpoint` prop to highlight the selected item in the list.
- `NewConversationMenu` now has a gap between the endpoint options to improve user experience.

* Added error messages

* refactor: Improve endpoint menu highlighting and error handling

In the UI, when the user selects an endpoint, the active class is now properly set. In the error handling function, `isJson` is now a private function called by `getError`, which provides better parsing of error messages, and returns more succinct messages upon encountering specific errors. Finally, a new end-to-end test has been added to check if the active class is properly set on selecting an endpoint in the new conversation menu.

* test: Add Conversation and Change Path of Auth JSON

In the Landing spec, test the functionality to create conversations and check that the number of items has increased. In the Popup spec, change the path of the Auth JSON used by the context.

* Fixed logo issues

* Make everything not rounded

* Added time

---------

Co-authored-by: Danny Avila <110412045+danny-avila@users.noreply.github.com>
2023-06-02 00:32:35 -04:00
Anirudh
dade7b450f feat: Add clear button to search bar (#328)
* feat: Add clear button to search bar

This commit introduces a clear button to the SearchBar component using the X icon from Lucide-React. When the user enters a query in the input field, the clear button appears allowing them to easily remove the search term. The clear button is hidden when there is no search term entered.

* Refactor SearchBar component to improve user experience

Changed SearchBar's input field to add padding on the left side and an absolute positioned search icon. Also, added absolute positioned X icon on the right side when there is an input value, ensuring a better user experience.

* Refactor SearchBar component to show Clear Search icon dynamically

This commit makes changes to the SearchBar React component to render the Clear Search X icon only when the input field has a value. A showClearIcon state using useState hook is added and updated every time the input value changes. The useEffect hook is used to handle the case when the user clears the input value. This allows better UX by providing clear intent to the user that the icon is clickable and will clear the search query.

* Improve UX: Add styling to clear button & export button

This commit modifies the NavLinks component to improve user experience by removing a rounded styling to the "Clear conversations" and "Export conversations" buttons. Prior to this change, the buttons had a rounded styling.

* Refactor submit button styling for improved accessibility and readability.

Changed submit button styling for better accessibility and readability, including adjustments to padding and hover effects. The new styles ensure that the button is easily clickable for all users, while also improving its visual appearance.

* hotfix

* Improve UI styling in Conversation component

Changed the background color and hover effect of the conversation link in Conversation component to make it more visually appealing. The previous background color was '#2A2B32' and now it's 'gray-800'. The 'px-4' class has also been changed to 'hover:pr-4' for better readability.

---------

Co-authored-by: Danny Avila <110412045+danny-avila@users.noreply.github.com>
2023-06-02 00:11:34 -04:00
Danny Avila
7fbf27c5aa chore(.gitignore): add client/public/images/ to ignore list (#417)
refactor(chatgpt-client.js): free encoder memory after use
feat(chatgpt-client.tokens.js): add script to test memory usage of ChatGPTClient
2023-06-02 00:08:19 -04:00
Fuegovic
4705975e59 feat:add hyperlink to bing.com in SetTokenDialog (#414) 2023-05-31 00:41:01 -04:00
Danny Avila
2f59c82bec chore(api): update chatgpt-api package version to 1.36.3 (#404)
docs(api): update BINGAI_TOKEN instructions in .env.example
docs(client): update BINGAI_TOKEN instructions in SetTokenDialog component
2023-05-29 11:00:51 -04:00
Fuegovic
6a34978e98 Fix: typo and phrasing (#393)
* Update FEATURE-REQUEST.yml

Fix typo and phrasing

* Update pull_request_template.md

add one option to type of change
2023-05-28 17:55:57 -04:00
Fuegovic
d437e4b8cd update: "documents" folder to "docs" (#391)
* Rename .github/PULL_REQUEST_TEMPLATE/PULL-REQUEST.md to .github/pull_request_template.md

fix: Pull Request Template Location

* documents -> docs

* Update windows_install.md

Fix: Docker hyperlink

* Update linux_install.md

Fix: Layout (step 6)

* Rename docs/contributions/code_of_conduct.md to CODE_OF_CONDUCT.md

fix: Code of Conduct location according to GitHub's Guide

* Update CODE_OF_CONDUCT.md

Update: Contact info

* Update README.md

Update: Code of Conduct hyperlink in TOC

* Update CODE_OF_CONDUCT.md

Update: Link to ReadMe

* Update CONTRIBUTORS.md

update: add new name to the list

* Update and rename docs/contributions/contributor_guidelines.md to CONTRIBUTING.md

fix: change location according to GitHub's standards

* Delete CONTRIBUTORS.md

delete: contributor.md from root (already present in readme)

* Update SECURITY.md

* Update CONTRIBUTING.md

Update discord link to point to rules

* Update README.md

Update discord link to point to rules

* Update README.md

fix: ToC
2023-05-27 07:03:28 -04:00
Fuegovic
f40a2f8ee8 update: documentation (#389)
* Update docker_install.md

update Bing Token instructions

* Update linux_install.md

Update Bing Token Instructions
Add # markers to sections

* Update mac_install.md

Update Bing Token Instructions
Fix Formating
Recommend Docker

* Update windows_install.md

Update Bing Token Instructions

* Update linux_install.md

Recommend Docker

* Create QUESTION.yml

Questions Template

* Update QUESTION.yml

fix syntax

* Update QUESTION.yml

* Update QUESTION.yml

* Create FEATURE-REQUEST

* Rename FEATURE-REQUEST to FEATURE-REQUEST.yml

add file extension
2023-05-26 22:22:11 -04:00
Danny Avila
2d31c9f8b6 chore: bump package versions to 0.4.7 (#388) 2023-05-26 17:56:23 -04:00
60 changed files with 833 additions and 266 deletions

View File

@@ -0,0 +1,57 @@
name: Feature Request
description: File a feature request
title: "Enhancement: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill this out!
- type: input
id: contact
attributes:
label: Contact Details
description: How can we contact you if we need more information?
placeholder: ex. email@example.com
validations:
required: false
- type: textarea
id: what
attributes:
label: What features would you like to see added?
description: Please provide as many details as possible.
placeholder: Please provide as many details as possible.
validations:
required: true
- type: textarea
id: details
attributes:
label: More details
description: Please provide additional details if needed.
placeholder: Please provide additional details if needed.
validations:
required: true
- type: dropdown
id: subject
attributes:
label: Which components are impacted by your request?
multiple: true
options:
- General
- UI
- Endpoints
- Plugins
- Other
- type: textarea
id: screenshots
attributes:
label: Pictures
description: If relevant, please include images to help clarify your request. You can drag and drop images directly here, paste them, or provide a link to them.
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/danny-avila/chatgpt-clone/blob/main/documents/contributions/code_of_conduct.md)
options:
- label: I agree to follow this project's Code of Conduct
required: true

58
.github/ISSUE_TEMPLATE/QUESTION.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Question
description: Ask your question
title: "[Question]: "
labels: ["question"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill this!
- type: input
id: contact
attributes:
label: Contact Details
description: How can we get in touch with you if we need more info?
placeholder: ex. email@example.com
validations:
required: false
- type: textarea
id: what-is-your-question
attributes:
label: What is your question?
description: Please give as many details as possible
placeholder: Please give as many details as possible
validations:
required: true
- type: textarea
id: more-details
attributes:
label: More Details
description: Please provide more details if needed.
placeholder: Please provide more details if needed.
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: What is the main subject of your question?
multiple: true
options:
- Documentation
- Installation
- UI
- Endpoints
- User System/OAuth
- Other
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your problem. You can drag and drop, paste images directly here or link to them.
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/danny-avila/chatgpt-clone/blob/main/documents/contributions/code_of_conduct.md)
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

@@ -7,6 +7,8 @@ version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/api" # Location of package manifests
target-branch: "develop"
versioning-strategy: increase-if-necessary
schedule:
interval: "weekly"
allow:
@@ -18,6 +20,8 @@ updates:
include: "scope"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/client" # Location of package manifests
target-branch: "develop"
versioning-strategy: increase-if-necessary
schedule:
interval: "weekly"
allow:
@@ -29,6 +33,8 @@ updates:
include: "scope"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
target-branch: "develop"
versioning-strategy: increase-if-necessary
schedule:
interval: "weekly"
allow:

View File

@@ -10,7 +10,7 @@ Please delete options that are not relevant.
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Documentation update
## How Has This Been Tested?

2
.gitignore vendored
View File

@@ -26,6 +26,7 @@ dist/
public/main.js
public/main.js.map
public/main.js.LICENSE.txt
client/public/images/
client/public/main.js
client/public/main.js.map
client/public/main.js.LICENSE.txt
@@ -48,6 +49,7 @@ bower_components/
# Environment
.npmrc
.env
.env.test
cache.json
api/data/
owner.yml

View File

@@ -59,8 +59,8 @@ representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
https://t.me/proffapt.
reported to the community leaders responsible for enforcement here on GitHub or
on the official [Discord Server](https://discord.gg/uDyZ5Tzhct).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
@@ -129,4 +129,4 @@ https://www.contributor-covenant.org/translations.
##
## [Go Back to ReadMe](../../README.md)
## [Go Back to ReadMe](README.md)

View File

@@ -12,11 +12,11 @@ If the feature you would like to contribute has not already received prior appro
*Please note that a pull request involving a feature that has not been reviewed and approved by the project maintainers may be rejected.*
If you would like to discuss the changes you wish to make, join our [Discord community](https://discord.gg/NGaa9RPCft).
If you would like to discuss the changes you wish to make, join our [Discord community](https://discord.gg/uDyZ5Tzhct).
## Our Standards
Please read our [Coding Standards and Conventions](coding_conventions.md) before beginning on a contribution.
Please read our [Coding Standards and Conventions](docs/contributions/coding_conventions.md) before beginning on a contribution.
Examples of behavior that contributes to creating a positive environment
include:
@@ -180,6 +180,6 @@ Apply the following naming conventions to branches, labels, and other Git-relate
##
## [Go Back to ReadMe](../../README.md)
## [Go Back to ReadMe](README.md)

View File

@@ -1,26 +0,0 @@
# Contributors List
We appreciate all the contributors who helped make this project possible:
- danny-avila (Admin)
- wtlyu (Contributor)
- danorlando (Contributor)
- alfredo-f (Contributor)
- HyunggyuJang (Contributor)
- fuegovic (Contributor)
- DavidDev1334
- toordog (Contributor)
- heathriel (External Contributor)
- hackreactor-bot (Contributor)
- git-bruh (Contributor)
- zhangsean (Contributor)
- llk89 (Contributor)
- adamrb (Contributor)
If you have contributed to this project and would like to be added to the list of contributors, please submit a pull request updating this file with your name and GitHub username.
##
## [Go Back to ReadMe](README.md)

View File

@@ -74,46 +74,45 @@
<details open>
<summary><strong>Getting Started</strong></summary>
* [Docker Install](/documents/install/docker_install.md)
* [Linux Install](documents/install/linux_install.md)
* [Mac Install](documents/install/mac_install.md)
* [Windows Install](documents/install/windows_install.md)
* [Docker Install](/docs/install/docker_install.md)
* [Linux Install](docs/install/linux_install.md)
* [Mac Install](docs/install/mac_install.md)
* [Windows Install](docs/install/windows_install.md)
</details>
<details>
<summary><strong>General Information</strong></summary>
* [Project Origin](documents/general_info/project_origin.md)
* [Multilingual Information](documents/general_info/multilingual_information.md)
* [Roadmap](documents/general_info/roadmap.md)
* [Tech Stack](documents/general_info/tech_stack.md)
* [Code of Conduct](CODE_OF_CONDUCT.md)
* [Project Origin](docs/general_info/project_origin.md)
* [Multilingual Information](docs/general_info/multilingual_information.md)
* [Roadmap](docs/general_info/roadmap.md)
* [Tech Stack](docs/general_info/tech_stack.md)
* [Changelog](CHANGELOG.md)
* [Bing Jailbreak Info](documents/general_info/bing_jailbreak_info.md)
* [Bing Jailbreak Info](docs/general_info/bing_jailbreak_info.md)
</details>
<details>
<summary><strong>Features</strong></summary>
* [User Auth System](documents/features/user_auth_system.md)
* [Proxy](documents/features/proxy.md)
* [User Auth System](docs/features/user_auth_system.md)
* [Proxy](docs/features/proxy.md)
</details>
<details>
<summary><strong>Cloud Deployment</strong></summary>
* [Heroku](documents/deployment/heroku.md)
* [Heroku](docs/deployment/heroku.md)
</details>
<details>
<summary><strong>Contributions</strong></summary>
* [Code of Conduct](documents/contributions/code_of_conduct.md)
* [Contributor Guidelines](documents/contributions/contributor_guidelines.md)
* [Documentation Guidelines](documents/contributions/documentation_guidelines.md)
* [Code Standards and Conventions](documents/contributions/coding_conventions.md)
* [Testing](documents/contributions/testing.md)
* [Contributor Guidelines](CONTRIBUTING.md)
* [Documentation Guidelines](docs/contributions/documentation_guidelines.md)
* [Code Standards and Conventions](docs/contributions/coding_conventions.md)
* [Testing](docs/contributions/testing.md)
* [Security](SECURITY.md)
* [Contributors](CONTRIBUTORS.md)
* [Trello Board](https://trello.com/b/17z094kq/chatgpt-clone)
</details>
@@ -130,7 +129,7 @@ Please read the documentation before you do!
For new features, components, or extensions, please open an issue and discuss before sending a PR.
- Join the [Discord community](https://discord.gg/NGaa9RPCft)
- Join the [Discord community](https://discord.gg/uDyZ5Tzhct)
This project exists in its current state thanks to all the people who contribute
---

View File

@@ -46,10 +46,10 @@ We would like to express our gratitude to the security researchers and community
We do not currently have a bug bounty program in place. However, we welcome and appreciate any security-related contributions through pull requests (PRs) that address vulnerabilities in our codebase.
We believe in the power of collaboration to improve the security of our project and invite you to join us in making it more robust.
##
## [Go Back to ReadMe](README.md)
**Reference**
- https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html
##
## [Go Back to ReadMe](README.md)

View File

@@ -59,9 +59,9 @@ OPENAI_MODELS=gpt-3.5-turbo,gpt-3.5-turbo-0301,text-davinci-003,gpt-4
##########################
# Also used for Sydney and jailbreak
# As of 5/23/23, to use Bing, you will need your full cookie string from bing.com. Use dev tools or an extension while
# logged into the site to view it in your network request Cookie header value. For full instructions, see my comment here:
# https://github.com/waylaidwanderer/node-chatgpt-api/issues/378#issuecomment-1559868368
# To get your Access token for Bing, login to https://www.bing.com
# Use dev tools or an extension while logged into the site to copy the content of the _U cookie.
#If this fails, follow these instructions https://github.com/danny-avila/chatgpt-clone/issues/370#issuecomment-1560382302 to provide the full cookie strings.
# Set to "user_provided" to allow the user to provide its token from the UI.
# Leave it blank to disable this endpoint.
BINGAI_TOKEN="user_provided"

View File

@@ -81,6 +81,7 @@ const askClient = async ({
try {
usage.completion_tokens = (enc.encode(res.response)).length;
enc.free();
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
res.usage = usage;
} catch (e) {

View File

@@ -0,0 +1,89 @@
require('dotenv').config();
const run = async () => {
const { ChatGPTClient } = await import('@waylaidwanderer/chatgpt-api');
const text = `
The standard Lorem Ipsum passage, used since the 1500s
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
Section 1.10.32 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"
1914 translation by H. Rackham
"But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?"
Section 1.10.33 of "de Finibus Bonorum et Malorum", written by Cicero in 45 BC
"At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat."
1914 translation by H. Rackham
"On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains."
`;
const model = 'gpt-3.5-turbo';
const maxContextTokens = model === 'gpt-4' ? 8191 : model === 'gpt-4-32k' ? 32767 : 4095; // 1 less than maximum
const clientOptions = {
reverseProxyUrl: process.env.OPENAI_REVERSE_PROXY || null,
maxContextTokens,
modelOptions: {
model,
},
proxy: process.env.PROXY || null,
debug: true
};
let apiKey = process.env.OPENAI_KEY;
const maxMemory = 0.05 * 1024 * 1024 * 1024;
// Calculate initial percentage of memory used
const initialMemoryUsage = process.memoryUsage().heapUsed;
function printProgressBar(percentageUsed) {
const filledBlocks = Math.round(percentageUsed / 2); // Each block represents 2%
const emptyBlocks = 50 - filledBlocks; // Total blocks is 50 (each represents 2%), so the rest are empty
const progressBar = '[' + '█'.repeat(filledBlocks) + ' '.repeat(emptyBlocks) + '] ' + percentageUsed.toFixed(2) + '%';
console.log(progressBar);
}
const iterations = 16000;
console.time('loopTime');
// Trying to catch the error doesn't help; all future calls will immediately crash
for (let i = 0; i < iterations; i++) {
try {
console.log(`Iteration ${i}`);
const client = new ChatGPTClient(apiKey, clientOptions);
client.getTokenCount(text);
// const encoder = client.constructor.getTokenizer('cl100k_base');
// console.log(`Iteration ${i}: call encode()...`);
// encoder.encode(text, 'all');
// encoder.free();
const memoryUsageDuringLoop = process.memoryUsage().heapUsed;
const percentageUsed = memoryUsageDuringLoop / maxMemory * 100;
printProgressBar(percentageUsed);
if (i === (iterations - 1)) {
console.log(' done');
// encoder.free();
}
} catch (e) {
console.log(`caught error! in Iteration ${i}`);
console.log(e);
}
}
console.timeEnd('loopTime');
// Calculate final percentage of memory used
const finalMemoryUsage = process.memoryUsage().heapUsed;
// const finalPercentageUsed = finalMemoryUsage / maxMemory * 100;
console.log(`Initial memory usage: ${initialMemoryUsage / 1024 / 1024} megabytes`);
console.log(`Final memory usage: ${finalMemoryUsage / 1024 / 1024} megabytes`);
setTimeout(() => {
const memoryUsageAfterTimeout = process.memoryUsage().heapUsed;
console.log(`Post timeout: ${memoryUsageAfterTimeout / 1024 / 1024} megabytes`);
} , 10000);
}
run();

View File

@@ -1,6 +1,6 @@
{
"name": "chat-backend",
"version": "0.4.6",
"version": "0.4.7",
"description": "",
"main": "server/index.js",
"scripts": {
@@ -21,7 +21,7 @@
"dependencies": {
"@dqbd/tiktoken": "^1.0.2",
"@keyv/mongo": "^2.1.8",
"@waylaidwanderer/chatgpt-api": "^1.36.0",
"@waylaidwanderer/chatgpt-api": "^1.37.0",
"axios": "^1.3.4",
"bcryptjs": "^2.4.3",
"cookie": "^0.5.0",
@@ -38,7 +38,7 @@
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
"lodash": "^4.17.21",
"meilisearch": "^0.32.3",
"meilisearch": "^0.33.0",
"mongoose": "^7.1.1",
"nodemailer": "^6.9.1",
"openai": "^3.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "chat-frontend",
"version": "0.4.6",
"version": "0.4.7",
"description": "",
"scripts": {
"build": "vite build",

View File

@@ -86,7 +86,7 @@ export default function Conversation({ conversation, retainView }) {
if (currentConversation?.conversationId !== conversationId) {
aProps.className =
'group relative flex cursor-pointer items-center gap-3 break-all rounded-md py-3 px-3 hover:bg-[#2A2B32] hover:pr-4';
'group relative flex cursor-pointer items-center gap-3 break-all rounded-md py-3 px-3 hover:bg-gray-800 hover:pr-4';
}
return (
@@ -123,7 +123,7 @@ export default function Conversation({ conversation, retainView }) {
/>
</div>
) : (
<div className="absolute inset-y-0 right-0 z-10 w-8 rounded-r-md bg-gradient-to-l from-gray-900 group-hover:from-[#2A2B32]" />
<div className="absolute inset-y-0 right-0 z-10 w-8 rounded-r-md bg-gradient-to-l from-gray-900 group-hover:from-gray-700/70" />
)}
</a>
);

View File

@@ -6,6 +6,7 @@ import { useRecoilValue } from 'recoil';
import SetTokenDialog from '../SetTokenDialog';
import store from '../../../store';
import { cn } from '~/utils/index.jsx';
const alternateName = {
openAI: 'OpenAI',
@@ -15,7 +16,7 @@ const alternateName = {
google: 'PaLM'
};
export default function ModelItem({ endpoint, value }) {
export default function ModelItem({ endpoint, value, isSelected }) {
const [setTokenDialogOpen, setSetTokenDialogOpen] = useState(false);
const endpointsConfig = useRecoilValue(store.endpointsConfig);
@@ -33,8 +34,11 @@ export default function ModelItem({ endpoint, value }) {
<>
<DropdownMenuRadioItem
value={value}
className={cn(
'group dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800',
isSelected && 'dark:bg-gray-800 bg-gray-50 active'
)}
id={endpoint}
className="group dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800"
>
{icon}
{alternateName[endpoint] || endpoint}
@@ -42,7 +46,10 @@ export default function ModelItem({ endpoint, value }) {
<div className="flex w-4 flex-1" />
{isUserProvided ? (
<button
className="invisible m-0 mr-1 flex-initial rounded-md p-0 text-xs font-medium text-gray-400 hover:text-gray-700 group-hover:visible dark:font-normal dark:text-gray-400 dark:hover:text-gray-200"
className={cn(
'invisible m-0 mr-1 flex-initial rounded-md p-0 text-xs font-medium text-gray-400 hover:text-gray-700 group-hover:visible dark:font-normal dark:text-gray-400 dark:hover:text-gray-200',
isSelected && 'visible text-gray-700 dark:text-gray-200'
)}
onClick={(e) => {
e.preventDefault();
setSetTokenDialogOpen(true);

View File

@@ -1,11 +1,11 @@
import React from 'react';
import EndpointItem from './EndpointItem.jsx';
export default function EndpointItems({ endpoints, onSelect }) {
export default function EndpointItems({ endpoints, onSelect, selectedEndpoint }) {
return (
<>
{endpoints.map((endpoint) => (
<EndpointItem key={endpoint} value={endpoint} onSelect={onSelect} endpoint={endpoint} />
<EndpointItem isSelected={selectedEndpoint === endpoint} key={endpoint} value={endpoint} onSelect={onSelect} endpoint={endpoint} />
))}
</>
);

View File

@@ -137,7 +137,7 @@ export default function NewConversationMenu() {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="min-w-[300px] dark:bg-gray-700 z-[100]"
className="min-w-[300px] dark:bg-gray-900 z-[100]"
onCloseAutoFocus={(event) => event.preventDefault()}
>
<DropdownMenuLabel
@@ -150,11 +150,11 @@ export default function NewConversationMenu() {
<DropdownMenuRadioGroup
value={endpoint}
onValueChange={onSelectEndpoint}
className="overflow-y-auto"
className="overflow-y-auto gap-1 flex flex-col"
>
{showEndpoints &&
(availableEndpoints.length ? (
<EndpointItems endpoints={availableEndpoints} onSelect={onSelectEndpoint} />
<EndpointItems selectedEndpoint={endpoint} endpoints={availableEndpoints} onSelect={onSelectEndpoint} />
) : (
<DropdownMenuLabel className="dark:text-gray-300">
No endpoint available.

View File

@@ -43,16 +43,26 @@ const SetTokenDialog = ({ open, onOpenChange, endpoint }) => {
const helpText = {
bingAI: (
<small className="break-all text-gray-600">
{`As of 5/23/23, to use Bing, you will need your full cookie string from bing.com. Use dev tools or an extension while
logged into the site to view it in your network request Cookie header value. For full instructions, see my `}
{`To get your Access token for Bing, login to `}
<a
target="_blank"
href="https://www.bing.com"
rel="noreferrer"
className="text-blue-600 underline"
>
https://www.bing.com
</a>
{`. Use dev tools or an extension while logged into the site to copy the content of the _U cookie.
If this fails, follow these `}
<a
target="_blank"
href="https://github.com/waylaidwanderer/node-chatgpt-api/issues/378#issuecomment-1559868368"
rel="noreferrer"
className="text-blue-600 underline"
>
comment here
instructions
</a>
{` to provide the full cookie strings.`}
</small>
),
chatGPTBrowser: (

View File

@@ -88,7 +88,7 @@ export default function SubmitButton({
disabled={disabled}
className="group absolute bottom-0 right-0 flex h-[100%] w-[50px] items-center justify-center bg-transparent p-1 text-gray-500"
>
<div className="m-1 mr-0 rounded-md p-2 pb-[10px] pt-[10px] group-hover:bg-gray-100 group-disabled:hover:bg-transparent dark:group-hover:bg-gray-900 dark:group-hover:text-gray-400 dark:group-disabled:hover:bg-transparent">
<div className="m-1 mr-0 rounded-md pt-[11px] pb-[9px] pl-[9.5px] pr-[7px] group-hover:bg-gray-100 group-disabled:hover:bg-transparent dark:group-hover:bg-gray-900 dark:group-hover:text-gray-400 dark:group-disabled:hover:bg-transparent">
<svg
stroke="currentColor"
fill="none"

View File

@@ -11,15 +11,7 @@ import { useMessageHandler } from '~/utils/handleSubmit';
import { useGetConversationByIdQuery } from '~/data-provider';
import { cn } from '~/utils/';
import store from '~/store';
function isJson(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
import getError from '~/utils/getError';
export default function Message({
conversation,
@@ -73,24 +65,6 @@ export default function Message({
}
};
const getError = (text) => {
const errorMessage = text.length > 512 ? text.slice(0, 512) + '...' : text;
const match = text.match(/\{[^{}]*\}/);
var json = match ? match[0] : '';
if (isJson(json)) {
json = JSON.parse(json);
if (json.code === 'invalid_api_key') {
return 'Invalid API key. Please check your API key and try again. You can access your API key by clicking on the model logo in the top-left corner of the textbox.';
} else if (json.type === 'insufficient_quota') {
return "We're sorry, but the default API key has reached its limit. To continue using this service, please set up your own API key. You can do this by clicking on the model logo in the top-left corner of the textbox.";
} else {
return `Oops! Something went wrong. Please try again in a few moments. Here's the specific error message we encountered: ${errorMessage}`;
}
} else {
return `Oops! Something went wrong. Please try again in a few moments. Here's the specific error message we encountered: ${errorMessage}`;
}
};
const props = {
className:
'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 bg-white dark:text-gray-100 group dark:bg-gray-800'
@@ -103,7 +77,7 @@ export default function Message({
if (!isCreatedByUser)
props.className =
'w-full border-b border-black/10 bg-gray-50 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-[#444654]';
'w-full border-b border-black/10 bg-gray-50 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-gray-1000';
if (message.bg && searchResult) {
props.className = message.bg.split('hover')[0];

View File

@@ -1,23 +0,0 @@
import { forwardRef, useContext } from 'react';
import DarkModeIcon from '../svg/DarkModeIcon';
import LightModeIcon from '../svg/LightModeIcon';
import { ThemeContext } from '~/hooks/ThemeContext';
const DarkMode = forwardRef(() => {
const { theme, setTheme } = useContext(ThemeContext);
const clickHandler = () => setTheme(theme === 'dark' ? 'light' : 'dark');
const mode = theme === 'dark' ? 'Light mode' : 'Dark mode';
return (
<button
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700"
onClick={clickHandler}
>
{theme === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
{mode}
</button>
);
});
export default DarkMode;

View File

@@ -3,11 +3,12 @@ import { Fragment, useState } from 'react';
import { useRecoilValue } from 'recoil';
import SearchBar from './SearchBar';
import TrashIcon from '../svg/TrashIcon';
import GearIcon from '../svg/GearIcon';
import Settings from './Settings';
import { Download } from 'lucide-react';
import NavLink from './NavLink';
import ExportModel from './ExportConversation/ExportModel';
import ClearConvos from './ClearConvos';
import DarkMode from './DarkMode';
import Logout from './Logout';
import { useAuthContext } from '~/hooks/AuthContext';
import { cn } from '~/utils/';
@@ -18,6 +19,7 @@ import store from '~/store';
export default function NavLinks({ clearSearch, isSearchEnabled }) {
const [showExports, setShowExports] = useState(false);
const [showClearConvos, setShowClearConvos] = useState(false);
const [showSettings, setShowSettings] = useState(false);
const { user } = useAuthContext();
const conversation = useRecoilValue(store.conversation) || {};
@@ -47,7 +49,7 @@ export default function NavLinks({ clearSearch, isSearchEnabled }) {
<img
className="rounded-sm"
src={
user?.avatar || `https://avatars.dicebear.com/api/initials/${user?.name}.svg`
user?.avatar || `https://api.dicebear.com/6.x/initials/svg?seed=${user?.name || 'User'}&fontFamily=Verdana&fontSize=36`
}
alt=""
/>
@@ -77,8 +79,8 @@ export default function NavLinks({ clearSearch, isSearchEnabled }) {
<Menu.Item as="div">
<NavLink
className={cn(
'flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700',
exportable ? 'cursor-pointer text-white' : 'cursor-not-allowed text-gray-400'
'flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700 rounded-none',
exportable ? 'cursor-pointer text-white' : 'cursor-not-allowed text-white/50'
)}
svg={() => <Download size={16} />}
text="Export conversation"
@@ -86,17 +88,22 @@ export default function NavLinks({ clearSearch, isSearchEnabled }) {
/>
</Menu.Item>
<div className="my-1.5 h-px bg-white/20" role="none" />
<Menu.Item as="div">
<DarkMode />
</Menu.Item>
<Menu.Item as="div">
<NavLink
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700"
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700 rounded-none"
svg={() => <TrashIcon />}
text="Clear conversations"
clickHandler={() => setShowClearConvos(true)}
/>
</Menu.Item>
<Menu.Item as="div">
<NavLink
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700 rounded-none"
svg={() => <GearIcon />}
text="Settings"
clickHandler={() => setShowSettings(true)}
/>
</Menu.Item>
<div className="my-1.5 h-px bg-white/20" role="none" />
<Menu.Item as="div">
<Logout />
@@ -108,6 +115,7 @@ export default function NavLinks({ clearSearch, isSearchEnabled }) {
</Menu>
{showExports && <ExportModel open={showExports} onOpenChange={setShowExports} />}
{showClearConvos && <ClearConvos open={showClearConvos} onOpenChange={setShowClearConvos} />}
{showSettings && <Settings open={showSettings} onOpenChange={setShowSettings} />}
</>
);
}

View File

@@ -1,11 +1,12 @@
import { forwardRef } from 'react';
import { Search } from 'lucide-react';
import { forwardRef, useState, useEffect } from 'react';
import { Search, X } from 'lucide-react';
import { useRecoilState } from 'recoil';
import store from '~/store';
const SearchBar = forwardRef((props, ref) => {
const { clearSearch } = props;
const [searchQuery, setSearchQuery] = useRecoilState(store.searchQuery);
const [showClearIcon, setShowClearIcon] = useState(false);
const handleKeyUp = (e) => {
const { value } = e.target;
@@ -18,17 +19,27 @@ const SearchBar = forwardRef((props, ref) => {
const onChange = (e) => {
const { value } = e.target;
setSearchQuery(value);
setShowClearIcon(value.length > 0);
};
useEffect(() => {
if (searchQuery.length === 0) {
setShowClearIcon(false);
} else {
setShowClearIcon(true);
}
}, [searchQuery])
return (
<div
ref={ref}
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700"
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700 relative"
>
{<Search className="h-4 w-4" />}
{<Search className="h-4 w-4 absolute left-3" />}
<input
type="text"
className="m-0 mr-0 w-full border-none bg-transparent p-0 text-sm leading-tight outline-none"
className="m-0 mr-0 w-full border-none bg-transparent p-0 text-sm leading-tight outline-none pl-7"
value={searchQuery}
onChange={onChange}
onKeyDown={(e) => {
@@ -37,8 +48,15 @@ const SearchBar = forwardRef((props, ref) => {
placeholder="Search messages"
onKeyUp={handleKeyUp}
/>
<X
className={`h-5 w-5 absolute right-3 cursor-pointer ${showClearIcon ? 'opacity-100' : 'opacity-0'} transition-opacity duration-1000`}
onClick={() => {
setSearchQuery('');
clearSearch();
}}
/>
</div>
);
});
export default SearchBar;
export default SearchBar;

View File

@@ -0,0 +1,170 @@
import { Dialog } from '../ui/Dialog.tsx';
import * as Tabs from '@radix-ui/react-tabs';
import { DialogContent, DialogHeader, DialogTitle } from '../ui/Dialog.tsx';
import { useEffect, useState, useContext } from 'react';
import { cn } from '~/utils/';
import { useClearConversationsMutation } from '~/data-provider';
import { ThemeContext } from '~/hooks/ThemeContext';
import store from '~/store';
import { CheckIcon } from 'lucide-react';
export default function Settings({ open, onOpenChange }) {
const { theme, setTheme } = useContext(ThemeContext);
const { newConversation } = store.useConversation();
const { refreshConversations } = store.useConversations();
const clearConvosMutation = useClearConversationsMutation();
const [isMobile, setIsMobile] = useState(false);
const [confirmClear, setConfirmClear] = useState(false);
const clearConvos = () => {
if (confirmClear) {
console.log('Clearing conversations...');
clearConvosMutation.mutate();
setConfirmClear(false);
} else {
setConfirmClear(true);
}
};
const changeTheme = (e) => {
setTheme(e.target.value);
};
// check if mobile dynamically and update
useEffect(() => {
const checkMobile = () => {
if (window.innerWidth <= 768) {
setIsMobile(true);
} else {
setIsMobile(false);
}
};
checkMobile();
window.addEventListener('resize', checkMobile);
}, []);
useEffect(() => {
if (clearConvosMutation.isSuccess) {
newConversation();
refreshConversations();
}
}, [clearConvosMutation.isSuccess]);
useEffect(() => {
// If the user clicks in the dialog when confirmClear is true, set it to false
const handleClick = (e) => {
if (confirmClear) {
if (e.target.id === 'clearConvosBtn' || e.target.id === 'clearConvosTxt') {
return;
}
setConfirmClear(false);
}
};
window.addEventListener('click', handleClick);
return () => window.removeEventListener('click', handleClick);
}, [confirmClear]);
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className={cn('shadow-2xl dark:bg-gray-900 dark:text-white')}>
<DialogHeader>
<DialogTitle className="text-gray-800 dark:text-white">Settings</DialogTitle>
</DialogHeader>
<div className="px-6">
<Tabs.Root
defaultValue="general"
className="flex flex-col gap-6 md:flex-row"
orientation="vertical"
>
<Tabs.List
aria-label="Settings"
role="tablist"
aria-orientation="vertical"
className={cn(
'-ml-[8px] flex min-w-[180px] flex-shrink-0 flex-col',
isMobile && 'flex-row rounded-lg bg-gray-100 p-1 dark:bg-gray-800/30'
)}
style={{ outline: 'none' }}
>
<Tabs.Trigger
className={cn(
'radix-state-active:bg-gray-800 radix-state-active:text-white flex items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm',
isMobile &&
'dark:radix-state-active:text-white group flex-1 items-center justify-center text-sm dark:text-gray-500'
)}
value="general"
>
<svg
stroke="currentColor"
fill="currentColor"
strokeWidth="0"
viewBox="0 0 20 20"
className="group-radix-state-active:fill-white h-4 h-5 w-4 w-5 fill-white dark:fill-gray-500"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
clipRule="evenodd"
/>
</svg>
General
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="general" role="tabpanel" className="w-full md:min-h-[300px]">
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-300">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="flex items-center justify-between">
<div>Theme</div>
<select
className="w-24 rounded border border-black/10 bg-transparent text-sm dark:border-white/20 dark:bg-gray-900"
onChange={changeTheme}
value={theme}
>
<option value="system">System</option>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
</div>
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="flex items-center justify-between">
<div>Clear all chats</div>
<button
className="btn relative bg-red-600 text-white hover:bg-red-800"
type="button"
id="clearConvosBtn"
onClick={clearConvos}
>
{confirmClear ? (
<div
className="flex w-full items-center justify-center gap-2"
id="clearConvosTxt"
>
<CheckIcon className="h-5 w-5" /> Confirm Clear
</div>
) : (
<div
className="flex w-full items-center justify-center gap-2"
id="clearConvosTxt"
>
Clear
</div>
)}
</button>
</div>
</div>
</div>
</Tabs.Content>
</Tabs.Root>
</div>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,19 @@
export default function GearIcon() {
return (
<svg
stroke="currentColor"
fill="none"
strokeWidth="2"
viewBox="0 0 24 24"
strokeLinecap="round"
strokeLinejoin="round"
className="h-4 w-4"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
);
}

View File

@@ -28,7 +28,7 @@ const AlertDialogOverlay = React.forwardRef<
>(({ className, children, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
'animate-in fade-in fixed inset-0 z-50 bg-black/50 backdrop-blur-sm transition-opacity',
'animate-in fade-in fixed inset-0 z-50 bg-gray-500/90 dark:bg-gray-800/90 transition-opacity',
className
)}
{...props}

View File

@@ -24,7 +24,7 @@ const DialogOverlay = React.forwardRef<
>(({ className, children, ...props }, ref) => (
<DialogPrimitive.Overlay
className={cn(
'data-[state=closed]:animate-out data-[state=open]:fade-in data-[state=closed]:fade-out fixed inset-0 z-[999] bg-black/50 backdrop-blur-sm transition-all duration-100',
'data-[state=closed]:animate-out data-[state=open]:fade-in data-[state=closed]:fade-out fixed inset-0 z-[999] bg-gray-500/90 dark:bg-gray-800/90 transition-all duration-100',
className
)}
{...props}
@@ -42,14 +42,14 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
'animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-[999] grid w-full gap-4 rounded-b-lg bg-white p-6 sm:max-w-lg sm:rounded-lg',
'animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-[999] grid w-full gap-4 rounded-b-lg bg-white pb-6 sm:rounded-lg md:w-[680px]',
'dark:bg-slate-900',
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900 dark:data-[state=open]:bg-slate-800">
<DialogPrimitive.Close className="absolute right-4 top-[1.88rem] rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900 dark:data-[state=open]:bg-slate-800">
<X className="h-4 w-4 text-black dark:text-white" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
@@ -59,13 +59,19 @@ const DialogContent = React.forwardRef<
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...props} />
<div
className={cn(
'flex flex-col space-y-2 border-b border-black/10 p-6 text-center dark:border-white/10 sm:text-left',
className
)}
{...props}
/>
);
DialogHeader.displayName = 'DialogHeader';
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn('flex flex-col-reverse sm:flex-row sm:justify-between sm:space-x-2', className)}
className={cn('flex flex-col-reverse sm:flex-row sm:justify-between sm:space-x-2 px-6', className)}
{...props}
/>
);

View File

@@ -10,28 +10,22 @@ import {
import { cn } from '~/utils/';
const DialogTemplate = forwardRef((props, ref) => {
const {
title,
description,
main,
buttons,
leftButtons,
selection,
className
} = props;
const { title, description, main, buttons, leftButtons, selection, className } = props;
const { selectHandler, selectClasses, selectText } = selection || {};
const defaultSelect =
'bg-gray-900 text-white transition-colors hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200 dark:focus:ring-gray-400 dark:focus:ring-offset-gray-900';
return (
<DialogContent ref={ref} className={cn('shadow-2xl dark:bg-gray-800', className || '')}>
<DialogContent ref={ref} className={cn('shadow-2xl dark:bg-gray-900', className || '')}>
<DialogHeader>
<DialogTitle className="text-gray-800 dark:text-white">{title}</DialogTitle>
<DialogDescription className="text-gray-600 dark:text-gray-300">
{description}
</DialogDescription>
{description && (
<DialogDescription className="text-gray-600 dark:text-gray-300">
{description}
</DialogDescription>
)}
</DialogHeader>
{main ? main : null}
<div className="px-6">{main ? main : null}</div>
<DialogFooter>
<div>{leftButtons ? leftButtons : null}</div>
<div className="flex gap-2">

View File

@@ -26,10 +26,14 @@ export const ThemeProvider = ({ initialTheme, children }) => {
const rawSetTheme = (rawTheme) => {
const root = window.document.documentElement;
const isDark = rawTheme === 'dark';
let isDark = rawTheme === 'dark';
if (rawTheme === 'system') {
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
}
root.classList.remove(isDark ? 'light' : 'dark');
root.classList.add(rawTheme);
root.classList.add(isDark ? 'dark' : 'light');
localStorage.setItem('color-theme', rawTheme);
};

View File

@@ -132,6 +132,24 @@
transition: all 1s ease-in-out;
} */
select {
--tw-shadow: 0 0 transparent;
-webkit-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%238e8ea0' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");
background-position: right .5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
background-color: #fff;
border-color: #8e8ea0;
border-radius: 0;
border-width: 1px;
font-size: 1rem;
line-height: 1.5rem;
padding: .5rem .75rem;
}
.overflow-y-auto {
overflow-y: overlay;
}

View File

@@ -1,7 +1,7 @@
const even =
'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 bg-white dark:text-gray-100 group dark:bg-gray-800 hover:bg-gray-100/25 hover:text-gray-700 dark:hover:bg-gray-900 dark:hover:text-gray-200';
const odd =
'w-full border-b border-black/10 bg-gray-50 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-[#444654] hover:bg-gray-100/40 hover:text-gray-700 dark:hover:bg-[#3b3d49] dark:hover:text-gray-200';
'w-full border-b border-black/10 bg-gray-50 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-gray-1000 hover:bg-gray-100/40 hover:text-gray-700 dark:hover:bg-[#3b3d49] dark:hover:text-gray-200';
export default function buildTree(messages, groupAll = false) {
if (messages === null) return null;

View File

@@ -0,0 +1,29 @@
const isJson = (str) => {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
const getError = (text) => {
const errorMessage = text.length > 512 ? text.slice(0, 512) + '...' : text;
const match = text.match(/\{[^{}]*\}/);
let json = match ? match[0] : '';
if (isJson(json)) {
json = JSON.parse(json);
if (json.code === 'invalid_api_key') {
return 'Invalid API key. Please check your API key and try again. You can do this by clicking on the model logo in the left corner of the textbox and selecting "Set Token" for the current selected endpoint. Thank you for your understanding.';
} else if (json.type === 'insufficient_quota') {
return 'We apologize for any inconvenience caused. The default API key has reached its limit. To continue using this service, please set up your own API key. You can do this by clicking on the model logo in the left corner of the textbox and selecting "Set Token" for the current selected endpoint. Thank you for your understanding.';
} else {
return `Oops! Something went wrong. Please try again in a few moments. Here's the specific error message we encountered: ${errorMessage}`;
}
} else {
return `Oops! Something went wrong. Please try again in a few moments. Here's the specific error message we encountered: ${errorMessage}`;
}
};
export default getError;

View File

@@ -39,7 +39,8 @@ module.exports = {
'600': '#565869',
'700': '#40414f', // Replacing .dark .dark:bg-gray-700 and .dark .dark:hover:bg-gray-700:hover
'800': '#343541', // Replacing .dark .dark:bg-gray-800, .bg-gray-800, and .dark .dark:hover:bg-gray-800\/90
'900': '#202123' // Replacing .dark .dark:bg-gray-900, .bg-gray-900, and .dark .dark:hover:bg-gray-900:hover
'900': '#202123', // Replacing .dark .dark:bg-gray-900, .bg-gray-900, and .dark .dark:hover:bg-gray-900:hover
'1000': '#444654'
},
green: {
50: "#f1f9f7",

View File

@@ -102,3 +102,6 @@ Use the conventions found in the `data-provider` directory for handling data ser
Use [Recoil](https://recoiljs.org/) for state management, but *DO NOT pollute the global state with unnecessary data*. Instead, use local state or props for data that is only used within a component or passed down from parent to child.
##
## [Go Back to ReadMe](../../README.md)

View File

@@ -32,15 +32,16 @@
**Create a MongoDB database**
- Navigate to https://www.mongodb.com/ and Sign In or Create an account
- Create a new project
- Build a Database using the free plan and name the cluster (example: chatgpt-clone)
- Use the "Username and Password" method for authentication
- Add your current IP to the access list
- In the Database Deployment tab, click on Connect
- "Choose a connection method" select "Connect your application"
- Driver = Node.js / Version = 4.1 or later
- Copy the connection string, fill in your password and remove `&w=majority` from default connection string.
Navigate to https://www.mongodb.com/ and Sign In or Create an account
- Create a new project
- Build a Database using the free plan and name the cluster (example: chatgpt-clone)
- Use the "Username and Password" method for authentication
- Add your current IP to the access list
- In the Database Deployment tab, click on Connect
- "Choose a connection method" select "Connect your application"
- Driver = Node.js / Version = 4.1 or later
- Copy the connection string, fill in your password and remove `&w=majority` from default connection string.
##
@@ -52,12 +53,14 @@
**Get your Bing Access Token**
- Using MS Edge, navigate to bing.com
- Make sure you are logged in
- Open the DevTools by pressing F12 on your keyboard
- Click on the tab "Application" (On the left of the DevTools)
- Expand the "Cookies" (Under "Storage")
- Copy the value of the "\_U" cookie
Please follow the **[updated instructions.](https://github.com/danny-avila/chatgpt-clone/issues/370#issuecomment-1560382302)**
~~Using MS Edge, navigate to bing.com~~
- ~~Make sure you are logged in~~
- ~~Open the DevTools by pressing F12 on your keyboard~~
- ~~Click on the tab "Application" (On the left of the DevTools)~~
- ~~Expand the "Cookies" (Under "Storage")~~
- ~~Copy the value of the "\_U" cookie~~
##

View File

@@ -1,7 +1,10 @@
# Linux Installation
Thanks to @DavidDev1334 !
## **Recommended : [Docker Install](docker_install.md)**
##
## **Manual Installation**
## Prerequisites
Before installing ChatGPT-Clone, make sure your machine has the following prerequisites installed:
@@ -12,13 +15,13 @@ Before installing ChatGPT-Clone, make sure your machine has the following prereq
## Installation Steps
1. Clone the repository:
## 1. Clone the repository:
```bash
git clone https://github.com/danny-avila/chatgpt-clone.git
```
2. Extract the content in your desired location:
## 2. Extract the content in your desired location:
```bash
cd chatgpt-clone
@@ -27,7 +30,7 @@ unzip chatgpt-clone.zip -d /usr/local/
Note: The above command extracts the files to "/usr/local/chatgpt-clone". If you want to install the files to a different location, modify the instructions accordingly.
3. Enable the Conversation search feature: (optional)
## 3. Enable the Conversation search feature: (optional)
- Download MeiliSearch latest release from: https://github.com/meilisearch/meilisearch/releases
- Copy it to "/usr/local/chatgpt-clone/"
@@ -41,7 +44,7 @@ Note: The above command extracts the files to "/usr/local/chatgpt-clone". If you
Note: Replace "YOUR_MASTER_KEY" with the generated master key, which you saved earlier.
4. Install Node.js:
## 4. Install Node.js:
Open a terminal and run the following commands:
@@ -50,7 +53,7 @@ curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
```
5. Create a MongoDB database:
## 5. Create a MongoDB database:
- Navigate to https://www.mongodb.com/ and sign in or create an account.
- Create a new project.
@@ -62,18 +65,21 @@ sudo apt-get install -y nodejs
- Driver = Node.js / Version = 4.1 or later.
- Copy the connection string and save it somewhere (you will need it later).
6. Get your OpenAI API key - Visit https://platform.openai.com/account/api-keys and save your API key somewhere safe (you will need it later)
## 6. Get your OpenAI API key
- Visit https://platform.openai.com/account/api-keys and save your API key somewhere safe (you will need it later)
7. Get your Bing Access Token
## 7. Get your Bing Access Token
Please follow the **[updated instructions.](https://github.com/danny-avila/chatgpt-clone/issues/370#issuecomment-1560382302)**
~~Using MS Edge, navigate to bing.com~~
- ~~Make sure you are logged in~~
- ~~Open the DevTools by pressing F12 on your keyboard~~
- ~~Click on the tab "Application" (On the left of the DevTools)~~
- ~~Expand the "Cookies" (Under "Storage")~~
- ~~Copy the value of the "\_U" cookie~~
- Using a web browser, navigate to bing.com
- Make sure you are logged in
- Open the browser DevTools by pressing F12 on your keyboard
- Click on the tab "Application" (On the left of the DevTools)
- Expand the "Cookies" (Under "Storage")
- You need to copy the value of the "_U" cookie, save it somewhere, you will need it later
8. Create the ".env" File
## 8. Create the ".env" File
You will need all your credentials, (API keys, access tokens, and MongoDB Connection String, MeiliSearch Master Key)
@@ -93,7 +99,7 @@ Setup the app:
1. Run `npm ci`
2. Run `npm run frontend`
Start the app:
## Start the app:
1. Run `npm run backend`
2. Run `meilisearch --master-key put_your_meilesearch_Master_Key_here` (Only if SEARCH=TRUE)
3. Visit http://localhost:3080 (default port) & enjoy

View File

@@ -1,10 +1,11 @@
# Mac Install
Thanks to @heathriel!
## **Recommended : [Docker Install](docker_install.md)**
##
**Recommended - [Docker Install](docker_install.md)**
## **Manual Installation**
## **Install the prerequisites**:
## Install the prerequisites:
- Install Homebrew (if not already installed) by following the instructions on https://brew.sh/
- Install Node.js and npm by running `brew install node`
- Install MongoDB (if not using Docker) by running `brew tap mongodb/brew` and `brew install mongodb-community`
@@ -22,14 +23,14 @@ Thanks to @heathriel!
- Copy the connection string and save it somewhere(you will need it later)
## **Instructions:**
## Instructions:
- Open Terminal and clone the repository by running git clone https://github.com/danny-avila/chatgpt-clone.git
- Change into the cloned directory by running cd chatgpt-clone
- If using MongoDB Atlas, remove &w=majority from the default connection string
Follow the instructions for setting up proxies, access tokens, and user system:
### **Access Tokens:**
### Access Tokens:
**Get your OpenAI API key**
@@ -40,16 +41,19 @@ Follow the instructions for setting up proxies, access tokens, and user system:
- To get your Access token for ChatGPT 'Free Version', log in to chat.openai.com, then visit https://chat.openai.com/api/auth/session.
- Warning: There may be a high chance of your account being banned with this method. Continue doing so at your own risk.
**BingAI Instructions:**
- To get the Bing Access Token, navigate to bing.com using a web browser such as Chrome or Safari, and ensure you're logged in.
- Open the Developer Tools (in Chrome or Safari, press Cmd + Option + I).
- Click on the "Application" tab (Chrome) or "Storage" tab (Safari).
- Expand the "Cookies" section under "Storage".
- Copy the value of the "_U" cookie and save it somewhere. You'll need it later.
**Get your Bing Access Token**
Please follow the **[updated instructions.](https://github.com/danny-avila/chatgpt-clone/issues/370#issuecomment-1560382302)**
~~Using MS Edge, navigate to bing.com~~
- ~~Make sure you are logged in~~
- ~~Open the DevTools by pressing F12 on your keyboard~~
- ~~Click on the tab "Application" (On the left of the DevTools)~~
- ~~Expand the "Cookies" (Under "Storage")~~
- ~~Copy the value of the "\_U" cookie~~
## **Setup Instruction**
## Setup Instruction
- Create a .env file in the api directory by running cp api/.env.example api/.env and edit the file with your preferred text editor, adding the required API keys, access tokens, and MongoDB connection string
- Run npm ci from root directory `npm ci`
- Build the client by running `npm run frontend`
@@ -85,7 +89,7 @@ MEILISEARCH_KEY=your_master_key_goes_here
- In the chatgpt-clone directory, start the application by running `npm run backend`
Visit http://localhost:3080 (default port) & enjoy
## **Optional but recommended:**
## Optional but recommended:
- Create a script to automate the starting process by creating a new file named start_chatgpt.sh in the chatgpt-clone directory and pasting the following code:

View File

@@ -1,7 +1,7 @@
# Windows Install
### Recommended:
### **[Docker](docker.md)**
### **[Docker](docker_install.md)**
or
### **[Automated Installer (Windows)](https://github.com/fuegovic/chatgpt-clone-local-installer)**
(Includes a Startup and Update Utility)
@@ -46,13 +46,16 @@ or
- **Get your OpenAI API key**
- here: https://platform.openai.com/account/api-keys and save it somewhere safe (you will need it later)
- **Get your Bing Access Token**
- Using MS Edge, navigate to bing.com
- Make sure you are logged in
- Open the DevTools by pressing F12 on your keyboard
- Click on the tab "Application" (On the left of the DevTools)
- Expand the "Cookies" (Under "Storage")
- You need to copy the value of the "\_U" cookie, save it somewhere, you will need it later
**Get your Bing Access Token**
Please follow the **[updated instructions.](https://github.com/danny-avila/chatgpt-clone/issues/370#issuecomment-1560382302)**
~~Using MS Edge, navigate to bing.com~~
- ~~Make sure you are logged in~~
- ~~Open the DevTools by pressing F12 on your keyboard~~
- ~~Click on the tab "Application" (On the left of the DevTools)~~
- ~~Expand the "Cookies" (Under "Storage")~~
- ~~Copy the value of the "\_U" cookie~~
- **Create the ".env" File**
You will need all your credentials, (API keys, access tokens, and Mongo Connection String, MeileSearch Master Key)

View File

@@ -12,7 +12,41 @@ test.describe('Landing suite', () => {
test('Landing title', async () => {
const page = await myBrowser.newPage();
await page.goto('http://localhost:3080/');
const pageTitle = await page.textContent('#landing-title')
const pageTitle = await page.textContent('#landing-title');
expect(pageTitle.length).toBeGreaterThan(0);
});
test('Create Conversation', async () => {
const page = await myBrowser.newPage();
await page.goto('http://localhost:3080/');
async function getItems() {
const navDiv = await page.waitForSelector('nav > div');
if (!navDiv) {
return [];
}
const items = await navDiv.$$('a.group');
return items || [];
}
// Wait for the page to load and the SVG loader to disappear
await page.waitForSelector('nav > div');
await page.waitForSelector('nav > div > div > svg', { state: 'detached' });
let beforeAdding = (await getItems()).length;
const input = await page.locator('form').getByRole('textbox');
await input.click();
await input.fill('Hi!');
// Send the message
await page.locator('form').getByRole('button').nth(1).click();
// Wait for the message to be sent
await page.waitForTimeout(15000);
let afterAdding = (await getItems()).length;
expect(afterAdding).toBeGreaterThan(beforeAdding);
});
});

59
e2e/specs/nav.spec.js Normal file
View File

@@ -0,0 +1,59 @@
import { expect, test } from '@playwright/test';
test.describe('Navigation suite', () => {
let myBrowser;
test.beforeEach(async ({ browser }) => {
myBrowser = await browser.newContext({
storageState: 'e2e/auth.json',
});
});
test('Navigation bar', async () => {
const page = await myBrowser.newPage();
await page.goto('http://localhost:3080/');
await page.locator('[id="headlessui-menu-button-\\:r0\\:"]').click();
const navBar = await page.locator('[id="headlessui-menu-button-\\:r0\\:"]').isVisible();
expect(navBar).toBeTruthy();
});
test('Settings modal', async () => {
const page = await myBrowser.newPage();
await page.goto('http://localhost:3080/');
await page.locator('[id="headlessui-menu-button-\\:r0\\:"]').click();
await page.getByText('Settings').click();
const modal = await page.getByRole('dialog', { name: 'Settings' }).isVisible();
expect(modal).toBeTruthy();
const modalTitle = await page.getByRole('heading', { name: 'Settings' }).textContent();
expect(modalTitle.length).toBeGreaterThan(0);
expect(modalTitle).toEqual('Settings');
const modalTabList = await page.getByRole('tablist', { name: 'Settings' }).isVisible();
expect(modalTabList).toBeTruthy();
const generalTabPanel = await page.getByRole('tabpanel', { name: 'General' }).isVisible();
expect(generalTabPanel).toBeTruthy();
const modalClearConvos = await page.getByRole('button', { name: 'Clear' }).isVisible();
expect(modalClearConvos).toBeTruthy();
const modalTheme = await page.getByRole('combobox');
expect(modalTheme.isVisible()).toBeTruthy();
async function changeMode(theme) {
// change the value to 'dark' and 'light' and see if the theme changes
await modalTheme.selectOption({ label: theme });
await page.waitForTimeout(1000);
// Check if the HTML element has the theme class
const html = await page.$eval('html', (element, theme) => element.classList.contains(theme.toLowerCase()), theme);
expect(html).toBeTruthy();
}
await changeMode('Dark');
await changeMode('Light');
});
});

24
e2e/specs/popup.spec.js Normal file
View File

@@ -0,0 +1,24 @@
import { expect, test } from '@playwright/test';
test.describe('Endpoints Presets suite', () => {
let myBrowser;
test.beforeEach(async ({ browser }) => {
myBrowser = await browser.newContext({
storageState: 'e2e/auth.json',
});
});
test('Endpoints Suite', async () => {
const page = await myBrowser.newPage();
await page.goto('http://localhost:3080/');
await page.getByRole('button', { name: 'New Topic' }).click();
const endpointItem = await page.getByRole('menuitemradio', { name: 'OpenAI' })
await endpointItem.click();
await page.getByRole('button', { name: 'New Topic' }).click();
// Check if the active class is set on the selected endpoint
expect(await endpointItem.getAttribute('class')).toContain('active');
});
});

98
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "chatgpt-clone",
"version": "0.4.6",
"version": "0.4.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "chatgpt-clone",
"version": "0.4.6",
"version": "0.4.7",
"license": "ISC",
"workspaces": [
"api",
@@ -35,12 +35,12 @@
},
"api": {
"name": "chat-backend",
"version": "0.4.6",
"version": "0.4.7",
"license": "ISC",
"dependencies": {
"@dqbd/tiktoken": "^1.0.2",
"@keyv/mongo": "^2.1.8",
"@waylaidwanderer/chatgpt-api": "^1.36.0",
"@waylaidwanderer/chatgpt-api": "^1.37.0",
"axios": "^1.3.4",
"bcryptjs": "^2.4.3",
"cookie": "^0.5.0",
@@ -57,7 +57,7 @@
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
"lodash": "^4.17.21",
"meilisearch": "^0.32.3",
"meilisearch": "^0.33.0",
"mongoose": "^7.1.1",
"nodemailer": "^6.9.1",
"openai": "^3.1.0",
@@ -76,7 +76,7 @@
},
"client": {
"name": "chat-frontend",
"version": "0.4.6",
"version": "0.4.7",
"license": "ISC",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0",
@@ -1646,12 +1646,12 @@
}
},
"node_modules/@babel/helper-member-expression-to-functions": {
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz",
"integrity": "sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==",
"version": "7.22.3",
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz",
"integrity": "sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA==",
"dev": true,
"dependencies": {
"@babel/types": "^7.21.5"
"@babel/types": "^7.22.3"
},
"engines": {
"node": ">=6.9.0"
@@ -2774,16 +2774,16 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx": {
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.5.tgz",
"integrity": "sha512-ELdlq61FpoEkHO6gFRpfj0kUgSwQTGoaEU8eMRoS8Dv3v6e7BjEAj5WMtIBRdHUeAioMhKP5HyxNzNnP+heKbA==",
"version": "7.22.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.3.tgz",
"integrity": "sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ==",
"dev": true,
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-module-imports": "^7.21.4",
"@babel/helper-plugin-utils": "^7.21.5",
"@babel/plugin-syntax-jsx": "^7.21.4",
"@babel/types": "^7.21.5"
"@babel/types": "^7.22.3"
},
"engines": {
"node": ">=6.9.0"
@@ -3245,9 +3245,9 @@
}
},
"node_modules/@babel/types": {
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz",
"integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==",
"version": "7.22.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz",
"integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.21.5",
@@ -3756,9 +3756,9 @@
}
},
"node_modules/@csstools/postcss-progressive-custom-properties": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-2.2.0.tgz",
"integrity": "sha512-qtJ2Jgf5bQW65OK7JaR0dw+XL3tc3BN99g+I5cRdik++HpyZitrKKxIwDGb3OHp2Yo3PZKuiX8pXljqmLHT/eg==",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-2.3.0.tgz",
"integrity": "sha512-Zd8ojyMlsL919TBExQ1I0CTpBDdyCpH/yOdqatZpuC3sd22K4SwC7+Yez3Q/vmXMWSAl+shjNeFZ7JMyxMjK+Q==",
"dev": true,
"funding": [
{
@@ -6495,6 +6495,15 @@
"@testing-library/dom": ">=7.21.4"
}
},
"node_modules/@timefox/bic-sydney": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@timefox/bic-sydney/-/bic-sydney-1.1.4.tgz",
"integrity": "sha512-ONeS0weT+ZoE471TDdzPqkKRk+VFr7sEL5+qEq1nIur6XMuVZ8cvlBicUNHfhYKIavkOM8xmBnk2dfVFQ54aiQ==",
"dependencies": {
"fetch-undici": "^3.0.1",
"undici": "^5.22.1"
}
},
"node_modules/@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -7029,12 +7038,13 @@
}
},
"node_modules/@waylaidwanderer/chatgpt-api": {
"version": "1.36.0",
"resolved": "https://registry.npmjs.org/@waylaidwanderer/chatgpt-api/-/chatgpt-api-1.36.0.tgz",
"integrity": "sha512-3QRNqLXT4ZhuH5Rd6siKR8E04SWFps7k8JqR2sniro4axFvPZEnBiEGhP97xGP9akYF3N8lMMTiNwJMsLSOeHg==",
"version": "1.37.0",
"resolved": "https://registry.npmjs.org/@waylaidwanderer/chatgpt-api/-/chatgpt-api-1.37.0.tgz",
"integrity": "sha512-fJfNvfZliuzRlTEY2kb3u6psfAaFI88YSU1RJ6J865BRk90Km3k62br/npCGLCtbREklTR18bMYhNXVM/BuFow==",
"dependencies": {
"@dqbd/tiktoken": "^1.0.2",
"@fastify/cors": "^8.2.0",
"@timefox/bic-sydney": "^1.1.2",
"@waylaidwanderer/fastify-sse-v2": "^3.1.0",
"@waylaidwanderer/fetch-event-source": "^3.0.1",
"boxen": "^7.0.1",
@@ -7042,7 +7052,7 @@
"dotenv": "^16.0.3",
"fastify": "^4.11.0",
"fetch-undici": "^3.0.1",
"https-proxy-agent": "^6.0.0",
"https-proxy-agent": "^7.0.0",
"inquirer": "^9.1.4",
"inquirer-autocomplete-prompt": "^3.0.0",
"keyv": "^4.5.2",
@@ -7349,9 +7359,9 @@
}
},
"node_modules/agent-base": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.0.2.tgz",
"integrity": "sha512-k2/tQ1+8Zf50dEUJWklUP80LcE/+Ph+OJ6cf2Ff2fD/c/TtCe6ofnCoNMz9UnyxOQYlaAALZtEWETzn+1JjfHg==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
"integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
"dependencies": {
"debug": "^4.3.4"
},
@@ -12620,9 +12630,9 @@
}
},
"node_modules/https-proxy-agent": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-6.2.0.tgz",
"integrity": "sha512-4xhCnMpxR9fupa7leh9uJK2P/qjYIeaM9uZ9c1bi1JDSwX2VH9NDk/oKSToNX4gBKa2WT31Mldne7e26ckohLQ==",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz",
"integrity": "sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==",
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
@@ -16822,9 +16832,9 @@
}
},
"node_modules/mdast-util-from-markdown": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.0.tgz",
"integrity": "sha512-HN3W1gRIuN/ZW295c7zi7g9lVBllMgZE40RxCX37wrTPWXCWtpvOZdfnuK+1WNpvZje6XuJeI3Wnb4TJEUem+g==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz",
"integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==",
"dependencies": {
"@types/mdast": "^3.0.0",
"@types/unist": "^2.0.0",
@@ -17018,11 +17028,11 @@
}
},
"node_modules/meilisearch": {
"version": "0.32.3",
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.32.3.tgz",
"integrity": "sha512-EOgfBuRE5SiIPIpEDYe2HO0D7a4z5bexIgaAdJFma/dH5hx1kwO+u/qb2g3qKyjG+iA3l8MlmTj/Xd72uahaAw==",
"version": "0.33.0",
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.33.0.tgz",
"integrity": "sha512-bYPb9WyITnJfzf92e7QFK8Rc50DmshFWxypXCs3ILlpNh8pT15A7KSu9Xgnnk/K3G/4vb3wkxxtFS4sxNkWB8w==",
"dependencies": {
"cross-fetch": "^3.1.5"
"cross-fetch": "^3.1.6"
}
},
"node_modules/memory-pager": {
@@ -17223,9 +17233,9 @@
}
},
"node_modules/micromark-extension-gfm-task-list-item": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.4.tgz",
"integrity": "sha512-9XlIUUVnYXHsFF2HZ9jby4h3npfX10S1coXTnV035QGPgrtNYQq3J6IfIvcCIUAJrrqBVi5BqA/LmaOMJqPwMQ==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz",
"integrity": "sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==",
"dependencies": {
"micromark-factory-space": "^1.0.0",
"micromark-util-character": "^1.0.0",
@@ -19165,9 +19175,9 @@
}
},
"node_modules/postcss-double-position-gradients": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-4.0.3.tgz",
"integrity": "sha512-Td1+C+kFCadnhRBMMf6D/eiQxjp33eAgwgMcLNYzZPcgXt1iU6vi/qEJ/YObp4nwn3QOtudFBMUOVHoGqmpfiA==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-4.0.4.tgz",
"integrity": "sha512-nUAbUXURemLXIrl4Xoia2tiu5z/n8sY+BVDZApoeT9BlpByyrp02P/lFCRrRvZ/zrGRE+MOGLhk8o7VcMCtPtQ==",
"dev": true,
"funding": [
{
@@ -19180,7 +19190,7 @@
}
],
"dependencies": {
"@csstools/postcss-progressive-custom-properties": "^2.2.0",
"@csstools/postcss-progressive-custom-properties": "^2.3.0",
"postcss-value-parser": "^4.2.0"
},
"engines": {

View File

@@ -1,6 +1,6 @@
{
"name": "chatgpt-clone",
"version": "0.4.6",
"version": "0.4.7",
"description": "",
"workspaces": [
"api",