Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
501a15a18f | ||
|
|
6049c9e3ff | ||
|
|
262b402606 | ||
|
|
56ea9563b8 | ||
|
|
2cd6612620 | ||
|
|
5d40396fb2 | ||
|
|
93dd1eb036 | ||
|
|
542a46dc7c | ||
|
|
bf31b1fea0 | ||
|
|
25d4529ff9 | ||
|
|
33d7c67c04 | ||
|
|
dc8f762bac |
53
CHANGELOG.md
53
CHANGELOG.md
@@ -1,5 +1,58 @@
|
||||
# # Changelog
|
||||
<details open>
|
||||
<summary><strong>2023-05-14</strong></summary>
|
||||
|
||||
**Released [v0.4.4](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.4):**
|
||||
|
||||
1. The Msg Clipboard was changed to a checkmark for improved user experience by @techwithanirudh in PR [#247](https://github.com/danny-avila/chatgpt-clone/pull/247).
|
||||
2. A typo in the auth.json path for accessing Google Palm was corrected by @antonme in PR [#266](https://github.com/danny-avila/chatgpt-clone/pull/266).
|
||||
3. @techwithanirudh added a Popup Menu to save sidebar space in PR [#260](https://github.com/danny-avila/chatgpt-clone/pull/260).
|
||||
4. The default pageSize in Conversation.js was increased from 12 to 14 by @danny-avila in PR [#267](https://github.com/danny-avila/chatgpt-clone/pull/267).
|
||||
5. Fonts were updated by @techwithanirudh in PR [#261](https://github.com/danny-avila/chatgpt-clone/pull/261).
|
||||
6. Font file paths in style.css were changed by @danny-avila in PR [#268](https://github.com/danny-avila/chatgpt-clone/pull/268).
|
||||
7. Code was fixed to adjust max_tokens according to model selection by @p4w4n in PR [#263](https://github.com/danny-avila/chatgpt-clone/pull/263).
|
||||
8. Various improvements were made, such as fixing react errors and adjusting the mobile view, by @danny-avila in PR [#269](https://github.com/danny-avila/chatgpt-clone/pull/269).
|
||||
|
||||
New contributors to the project include:
|
||||
|
||||
- @techwithanirudh, who made their first contribution in PR [#247](https://github.com/danny-avila/chatgpt-clone/pull/247).
|
||||
- @antonme, who made their first contribution in PR [#266](https://github.com/danny-avila/chatgpt-clone/pull/266).
|
||||
- @p4w4n, who made their first contribution in PR [#263](https://github.com/danny-avila/chatgpt-clone/pull/263).
|
||||
|
||||
The [full changelog can be found here](https://github.com/danny-avila/chatgpt-clone/compare/v0.4.3...v0.4.4)
|
||||
</details>
|
||||
<details>
|
||||
<summary><strong>2023-05-13</strong></summary>
|
||||
|
||||
**Released [v0.4.3](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.3) which now supports Google's PaLM 2!**
|
||||
|
||||

|
||||
|
||||
**How to Setup PaLM 2 (via Google Cloud Vertex AI API)**
|
||||
|
||||
- Enable the Vertex AI API on Google Cloud:
|
||||
- - https://console.cloud.google.com/vertex-ai
|
||||
- Create a Service Account:
|
||||
- - https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts/create?walkthrough_id=iam--create-service-account#step_index=1
|
||||
- Make sure to click 'Create and Continue' to give at least the 'Vertex AI User' role.
|
||||
- Create a JSON key, rename as 'auth.json' and save it in /api/data/.
|
||||
|
||||
**Alternatively**
|
||||
|
||||
- In your ./api/.env file, set PALM_KEY as "user_provided" to allow the user to provide a Service Account key JSON from the UI.
|
||||
- They will follow the steps above except for renaming the file, simply importing the JSON when prompted.
|
||||
- The key is sent to the server but never saved except in your local storage
|
||||
|
||||
**Note:**
|
||||
|
||||
- Vertex AI does not (yet) support response streaming for text generations, so response may seem to take long when generating a lot of text.
|
||||
- Text streaming is simulated
|
||||
|
||||
|
||||
You can check the full changelog in between v0.4.2 and v0.4.3 [here](https://github.com/danny-avila/chatgpt-clone/compare/v0.4.2...v0.4.3).
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>2023-05-11</strong></summary>
|
||||
|
||||
**Released [v0.4.2](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.2)**
|
||||
|
||||
55
README.md
55
README.md
@@ -40,16 +40,54 @@
|
||||
|
||||
##
|
||||
|
||||
## **Google's PaLM 2 is now supported as of [v0.4.3](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.3)**
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><strong>How to Setup PaLM 2 (via Google Cloud Vertex AI API)</strong></summary>
|
||||
- Enable the Vertex AI API on Google Cloud:
|
||||
- - https://console.cloud.google.com/vertex-ai
|
||||
- Create a Service Account:
|
||||
- - https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts/create?walkthrough_id=iam--create-service-account#step_index=1
|
||||
- Make sure to click 'Create and Continue' to give at least the 'Vertex AI User' role.
|
||||
- Create a JSON key, rename as 'auth.json' and save it in /api/data/.
|
||||
|
||||
**Alternatively**
|
||||
|
||||
- In your ./api/.env file, set PALM_KEY as "user_provided" to allow the user to provide a Service Account key JSON from the UI.
|
||||
- They will follow the steps above except for renaming the file, simply importing the JSON when prompted.
|
||||
- The key is sent to the server but never saved except in your local storage
|
||||
|
||||
**Note:**
|
||||
|
||||
- Vertex AI does not (yet) support response streaming for text generations, so response may seem to take long when generating a lot of text.
|
||||
- Text streaming is simulated
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details open>
|
||||
<summary><strong>2023-05-11</strong></summary>
|
||||
|
||||
**Released [v0.4.2](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.2)**
|
||||
|
||||
ChatGPT-Clone received some important upgrades and improvements. A new contributor, [@qcgm1978](https://github.com/qcgm1978), makes their first contribution by adding a null check for adaptiveCards variable. Additionally, support for titling conversations with the Azure endpoint is added by [@danny-avila](https://github.com/danny-avila) in PR [#234](https://github.com/danny-avila/chatgpt-clone/pull/234). In PR [#235](https://github.com/danny-avila/chatgpt-clone/pull/235), [@danny-avila](https://github.com/danny-avila) also makes some necessary fixes to titling, quotation marks, and endpoints being unavailable with only the Azure key provided. The logging system is now powered by Pino and sanitization, thanks to [@danorlando](https://github.com/danorlando) in PR [#227](https://github.com/danny-avila/chatgpt-clone/pull/227). To bulletproof the Docker container, the .dockerignore file is updated to include the client/.env file by [@danny-avila](https://github.com/danny-avila) in PR [#241](https://github.com/danny-avila/chatgpt-clone/pull/241). This issue was brought to our attention on discord.
|
||||
<summary><strong>2023-05-14</strong></summary>
|
||||
|
||||
There is active work on the new Plugins feature, converting the frontend to Typescript, and looking to integrate Palm2, google's new generative AI accessible via API, to the project as a new endpoint.
|
||||
**Released [v0.4.4](https://github.com/danny-avila/chatgpt-clone/releases/tag/v0.4.4):**
|
||||
|
||||
You can check the full changelog in between v0.4.1 and v0.4.2 [here](https://github.com/danny-avila/chatgpt-clone/compare/v0.4.1...v0.4.2).
|
||||
1. The Msg Clipboard was changed to a checkmark for improved user experience by @techwithanirudh in PR [#247](https://github.com/danny-avila/chatgpt-clone/pull/247).
|
||||
2. A typo in the auth.json path for accessing Google Palm was corrected by @antonme in PR [#266](https://github.com/danny-avila/chatgpt-clone/pull/266).
|
||||
3. @techwithanirudh added a Popup Menu to save sidebar space in PR [#260](https://github.com/danny-avila/chatgpt-clone/pull/260).
|
||||
4. The default pageSize in Conversation.js was increased from 12 to 14 by @danny-avila in PR [#267](https://github.com/danny-avila/chatgpt-clone/pull/267).
|
||||
5. Fonts were updated by @techwithanirudh in PR [#261](https://github.com/danny-avila/chatgpt-clone/pull/261).
|
||||
6. Font file paths in style.css were changed by @danny-avila in PR [#268](https://github.com/danny-avila/chatgpt-clone/pull/268).
|
||||
7. Code was fixed to adjust max_tokens according to model selection by @p4w4n in PR [#263](https://github.com/danny-avila/chatgpt-clone/pull/263).
|
||||
8. Various improvements were made, such as fixing react errors and adjusting the mobile view, by @danny-avila in PR [#269](https://github.com/danny-avila/chatgpt-clone/pull/269).
|
||||
|
||||
New contributors to the project include:
|
||||
|
||||
- @techwithanirudh, who made their first contribution in PR [#247](https://github.com/danny-avila/chatgpt-clone/pull/247).
|
||||
- @antonme, who made their first contribution in PR [#266](https://github.com/danny-avila/chatgpt-clone/pull/266).
|
||||
- @p4w4n, who made their first contribution in PR [#263](https://github.com/danny-avila/chatgpt-clone/pull/263).
|
||||
|
||||
The [full changelog can be found here](https://github.com/danny-avila/chatgpt-clone/compare/v0.4.3...v0.4.4)
|
||||
|
||||
⚠️ **IMPORTANT :** Since V0.4.0 You should register and login with a local account (email and password) for the first time sign-up. if you use login for the first time with a social login account (eg. Google, facebook, etc.), the conversations and presets that you created before the user system was implemented will NOT be migrated to that account.
|
||||
|
||||
@@ -133,3 +171,6 @@ For new features, components, or extensions, please open an issue and discuss be
|
||||
This project is licensed under the [MIT License](LICENSE.md).
|
||||
##
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#danny-avila/chatgpt-clone&Date)
|
||||
|
||||
@@ -23,12 +23,13 @@ const askClient = async ({
|
||||
};
|
||||
|
||||
const azure = process.env.AZURE_OPENAI_API_KEY ? true : false;
|
||||
|
||||
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,
|
||||
azure,
|
||||
maxContextTokens,
|
||||
modelOptions: {
|
||||
model: model,
|
||||
model,
|
||||
temperature,
|
||||
top_p,
|
||||
presence_penalty,
|
||||
@@ -37,22 +38,22 @@ const askClient = async ({
|
||||
chatGptLabel,
|
||||
promptPrefix,
|
||||
proxy: process.env.PROXY || null,
|
||||
debug: false
|
||||
// debug: true
|
||||
};
|
||||
|
||||
let apiKey = process.env.OPENAI_KEY;
|
||||
|
||||
if (azure) {
|
||||
apiKey = process.env.AZURE_OPENAI_API_KEY;
|
||||
clientOptions.reverseProxyUrl = genAzureEndpoint({
|
||||
azureOpenAIApiInstanceName: process.env.AZURE_OPENAI_API_INSTANCE_NAME,
|
||||
azureOpenAIApiDeploymentName: process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME,
|
||||
clientOptions.reverseProxyUrl = genAzureEndpoint({
|
||||
azureOpenAIApiInstanceName: process.env.AZURE_OPENAI_API_INSTANCE_NAME,
|
||||
azureOpenAIApiDeploymentName: process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME,
|
||||
azureOpenAIApiVersion: process.env.AZURE_OPENAI_API_VERSION
|
||||
});
|
||||
}
|
||||
|
||||
const client = new ChatGPTClient(apiKey, clientOptions, store);
|
||||
|
||||
|
||||
const options = {
|
||||
onProgress,
|
||||
abortController,
|
||||
|
||||
@@ -30,7 +30,7 @@ module.exports = {
|
||||
return { message: 'Error saving conversation' };
|
||||
}
|
||||
},
|
||||
getConvosByPage: async (user, pageNumber = 1, pageSize = 12) => {
|
||||
getConvosByPage: async (user, pageNumber = 1, pageSize = 14) => {
|
||||
try {
|
||||
const totalConvos = (await Conversation.countDocuments({ user })) || 1;
|
||||
const totalPages = Math.ceil(totalConvos / pageSize);
|
||||
@@ -45,7 +45,7 @@ module.exports = {
|
||||
return { message: 'Error getting conversations' };
|
||||
}
|
||||
},
|
||||
getConvosQueried: async (user, convoIds, pageNumber = 1, pageSize = 12) => {
|
||||
getConvosQueried: async (user, convoIds, pageNumber = 1, pageSize = 14) => {
|
||||
try {
|
||||
if (!convoIds || convoIds.length === 0) {
|
||||
return { conversations: [], pages: 1, pageNumber, pageSize };
|
||||
@@ -57,7 +57,7 @@ module.exports = {
|
||||
// will handle a syncing solution soon
|
||||
const deletedConvoIds = [];
|
||||
|
||||
convoIds.forEach((convo) =>
|
||||
convoIds.forEach(convo =>
|
||||
promises.push(
|
||||
Conversation.findOne({
|
||||
user,
|
||||
@@ -120,7 +120,7 @@ module.exports = {
|
||||
},
|
||||
deleteConvos: async (user, filter) => {
|
||||
let toRemove = await Conversation.find({ ...filter, user }).select('conversationId');
|
||||
const ids = toRemove.map((instance) => instance.conversationId);
|
||||
const ids = toRemove.map(instance => instance.conversationId);
|
||||
let deleteCount = await Conversation.deleteMany({ ...filter, user }).exec();
|
||||
deleteCount.messages = await deleteMessages({ conversationId: { $in: ids } });
|
||||
return deleteCount;
|
||||
|
||||
4
api/package-lock.json
generated
4
api/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "chatgpt-clone",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "chatgpt-clone",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@dqbd/tiktoken": "^1.0.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-backend",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"description": "",
|
||||
"main": "server/index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -95,7 +95,7 @@ const ask = async ({ text, endpointOption, parentMessageId = null, conversationI
|
||||
|
||||
try {
|
||||
if (!key) {
|
||||
key = require('../../data/auth.json');
|
||||
key = require('../../../data/auth.json');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("No 'auth.json' file (service account key) found in /api/data/ for PaLM models");
|
||||
|
||||
6
client/package-lock.json
generated
6
client/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "chatgpt-clone",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "chatgpt-clone",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
@@ -20257,4 +20257,4 @@
|
||||
"version": "2.0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-frontend",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
BIN
client/public/fonts/signifier-bold-italic.woff2
Normal file
BIN
client/public/fonts/signifier-bold-italic.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/signifier-bold.woff2
Normal file
BIN
client/public/fonts/signifier-bold.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/signifier-light-italic.woff2
Normal file
BIN
client/public/fonts/signifier-light-italic.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/signifier-light.woff2
Normal file
BIN
client/public/fonts/signifier-light.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/soehne-buch-kursiv.woff2
Normal file
BIN
client/public/fonts/soehne-buch-kursiv.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/soehne-buch.woff2
Normal file
BIN
client/public/fonts/soehne-buch.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/soehne-halbfett-kursiv.woff2
Normal file
BIN
client/public/fonts/soehne-halbfett-kursiv.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/soehne-halbfett.woff2
Normal file
BIN
client/public/fonts/soehne-halbfett.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/soehne-kraftig-kursiv.woff2
Normal file
BIN
client/public/fonts/soehne-kraftig-kursiv.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/soehne-kraftig.woff2
Normal file
BIN
client/public/fonts/soehne-kraftig.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/soehne-mono-buch-kursiv.woff2
Normal file
BIN
client/public/fonts/soehne-mono-buch-kursiv.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/soehne-mono-buch.woff2
Normal file
BIN
client/public/fonts/soehne-mono-buch.woff2
Normal file
Binary file not shown.
BIN
client/public/fonts/soehne-mono-halbfett.woff2
Normal file
BIN
client/public/fonts/soehne-mono-halbfett.woff2
Normal file
Binary file not shown.
@@ -126,7 +126,7 @@ export default function Conversation({ conversation, retainView }) {
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute inset-y-0 right-0 z-10 w-8 bg-gradient-to-l from-gray-900 group-hover:from-[#2A2B32]" />
|
||||
<div className="absolute inset-y-0 right-0 z-10 w-8 bg-gradient-to-l from-gray-900 group-hover:from-[#2A2B32] rounded-r-md" />
|
||||
)}
|
||||
</a>
|
||||
);
|
||||
|
||||
@@ -24,6 +24,7 @@ import store from '~/store';
|
||||
|
||||
export default function NewConversationMenu() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [showPresets, setShowPresets] = useState(true);
|
||||
const [presetModelVisible, setPresetModelVisible] = useState(false);
|
||||
const [preset, setPreset] = useState(false);
|
||||
|
||||
@@ -71,7 +72,10 @@ export default function NewConversationMenu() {
|
||||
if (endpoint) {
|
||||
const lastSelectedModel = JSON.parse(localStorage.getItem('lastSelectedModel')) || {};
|
||||
localStorage.setItem('lastConversationSetup', JSON.stringify(conversation));
|
||||
localStorage.setItem('lastSelectedModel', JSON.stringify({ ...lastSelectedModel, [endpoint] : conversation.model }));
|
||||
localStorage.setItem(
|
||||
'lastSelectedModel',
|
||||
JSON.stringify({ ...lastSelectedModel, [endpoint]: conversation.model })
|
||||
);
|
||||
}
|
||||
}, [conversation]);
|
||||
|
||||
@@ -156,7 +160,12 @@ export default function NewConversationMenu() {
|
||||
<div className="mt-6 w-full" />
|
||||
|
||||
<DropdownMenuLabel className="flex items-center dark:text-gray-300">
|
||||
<span>Select a Preset</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() => setShowPresets(prev => !prev)}
|
||||
>
|
||||
{showPresets ? 'Hide ' : 'Show '} Presets
|
||||
</span>
|
||||
<div className="flex-1" />
|
||||
<FileUpload onFileSelected={onFileSelected} />
|
||||
<Dialog>
|
||||
@@ -188,18 +197,19 @@ export default function NewConversationMenu() {
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuRadioGroup
|
||||
onValueChange={onSelectPreset}
|
||||
className="overflow-y-auto"
|
||||
className="max-h-[150px] overflow-y-auto"
|
||||
>
|
||||
{presets.length ? (
|
||||
<PresetItems
|
||||
presets={presets}
|
||||
onSelect={onSelectPreset}
|
||||
onChangePreset={onChangePreset}
|
||||
onDeletePreset={onDeletePreset}
|
||||
/>
|
||||
) : (
|
||||
<DropdownMenuLabel className="dark:text-gray-300">No preset yet.</DropdownMenuLabel>
|
||||
)}
|
||||
{showPresets &&
|
||||
(presets.length ? (
|
||||
<PresetItems
|
||||
presets={presets}
|
||||
onSelect={onSelectPreset}
|
||||
onChangePreset={onChangePreset}
|
||||
onDeletePreset={onDeletePreset}
|
||||
/>
|
||||
) : (
|
||||
<DropdownMenuLabel className="dark:text-gray-300">No preset yet.</DropdownMenuLabel>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import Clipboard from '../svg/Clipboard';
|
||||
import CheckMark from '../svg/CheckMark';
|
||||
import EditIcon from '../svg/EditIcon';
|
||||
import RegenerateIcon from '../svg/RegenerateIcon';
|
||||
|
||||
@@ -13,6 +14,7 @@ export default function HoverButtons({
|
||||
regenerate
|
||||
}) {
|
||||
const { endpoint, jailbreak = false } = conversation;
|
||||
const [isCopied, setIsCopied] = React.useState(false);
|
||||
|
||||
const branchingSupported =
|
||||
// azureOpenAI, openAI, chatGPTBrowser support branching, so edit enabled
|
||||
@@ -59,11 +61,11 @@ export default function HoverButtons({
|
||||
|
||||
<button
|
||||
className="hover-button rounded-md p-1 hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400 md:invisible md:group-hover:visible"
|
||||
onClick={copyToClipboard}
|
||||
onClick={() => copyToClipboard(setIsCopied)}
|
||||
type="button"
|
||||
title="copy to clipboard"
|
||||
title={isCopied ? 'Copied to clipboard' : 'Copy to clipboard'}
|
||||
>
|
||||
<Clipboard />
|
||||
{isCopied ? <CheckMark /> : <Clipboard />}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -98,8 +98,13 @@ export default function Message({
|
||||
if (!isSubmitting && !message?.isCreatedByUser) regenerate(message);
|
||||
};
|
||||
|
||||
const copyToClipboard = () => {
|
||||
const copyToClipboard = (setIsCopied) => {
|
||||
setIsCopied(true);
|
||||
copy(message?.text);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsCopied(false);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
const clickSearchResult = async () => {
|
||||
@@ -217,7 +222,7 @@ export default function Message({
|
||||
conversation={conversation}
|
||||
enterEdit={() => enterEdit()}
|
||||
regenerate={() => regenerateMessage()}
|
||||
copyToClipboard={() => copyToClipboard()}
|
||||
copyToClipboard={copyToClipboard}
|
||||
/>
|
||||
<SubRow subclasses="switch-container">
|
||||
<SiblingSwitch
|
||||
|
||||
@@ -26,8 +26,7 @@ export default function ClearConvos() {
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<button
|
||||
className="flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
|
||||
// onClick={clickHandler}
|
||||
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"
|
||||
>
|
||||
<TrashIcon />
|
||||
Clear conversations
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function DarkMode() {
|
||||
|
||||
return (
|
||||
<button
|
||||
className="flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
|
||||
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 />}
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function ExportConversation() {
|
||||
<>
|
||||
<button
|
||||
className={cn(
|
||||
'flex items-center gap-3 rounded-md py-3 px-3 text-sm transition-colors duration-200 hover:bg-gray-500/10',
|
||||
'flex py-3 px-3 items-center gap-3 transition-colors duration-200 text-white cursor-pointer text-sm hover:bg-gray-700 w-full',
|
||||
exportable ? 'cursor-pointer text-white' : 'cursor-not-allowed text-gray-400'
|
||||
)}
|
||||
onClick={clickHandler}
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function Logout() {
|
||||
|
||||
return (
|
||||
<button
|
||||
className="flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
|
||||
className="flex py-3 px-3 items-center gap-3 transition-colors duration-200 text-white cursor-pointer text-sm hover:bg-gray-700 w-full"
|
||||
onClick={handleLogout}
|
||||
>
|
||||
<LogOutIcon />
|
||||
|
||||
@@ -1,21 +1,77 @@
|
||||
import { Menu, Transition } from '@headlessui/react';
|
||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import SearchBar from './SearchBar';
|
||||
import ClearConvos from './ClearConvos';
|
||||
import DarkMode from './DarkMode';
|
||||
import Logout from './Logout';
|
||||
import ExportConversation from './ExportConversation';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
import { cn } from '~/utils/';
|
||||
import DotsIcon from '../svg/DotsIcon';
|
||||
|
||||
export default function NavLinks({ clearSearch, isSearchEnabled }) {
|
||||
const { user, logout } = useAuthContext();
|
||||
return (
|
||||
<>
|
||||
{!!isSearchEnabled && (
|
||||
<SearchBar
|
||||
clearSearch={clearSearch}
|
||||
/>
|
||||
<Menu
|
||||
as="div"
|
||||
className="group relative"
|
||||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Menu.Button
|
||||
className={cn(
|
||||
'group-ui-open:bg-gray-800 flex w-full items-center gap-2.5 rounded-md px-3 py-3 text-sm transition-colors duration-200 hover:bg-gray-800',
|
||||
open ? 'bg-gray-800' : ''
|
||||
)}
|
||||
>
|
||||
<div className="-ml-0.5 h-5 w-5 flex-shrink-0">
|
||||
<div className="relative flex">
|
||||
<img
|
||||
className="rounded-sm"
|
||||
src={user?.avatar || `https://avatars.dicebear.com/api/initials/${user?.name}.svg`}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-white">
|
||||
{user?.name || 'USER'}
|
||||
</div>
|
||||
<DotsIcon />
|
||||
</Menu.Button>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute bottom-full left-0 z-20 mb-2 w-full translate-y-0 overflow-hidden rounded-md bg-[#050509] py-1.5 opacity-100 outline-none">
|
||||
<Menu.Item>
|
||||
{({}) => <>{!!isSearchEnabled && <SearchBar clearSearch={clearSearch} />}</>}
|
||||
</Menu.Item>
|
||||
<Menu.Item>{({}) => <ExportConversation />}</Menu.Item>
|
||||
|
||||
<div
|
||||
className="my-1.5 h-px bg-white/20"
|
||||
role="none"
|
||||
></div>
|
||||
<Menu.Item>{({}) => <DarkMode />}</Menu.Item>
|
||||
<Menu.Item>{({}) => <ClearConvos />}</Menu.Item>
|
||||
|
||||
<div
|
||||
className="my-1.5 h-px bg-white/20"
|
||||
role="none"
|
||||
></div>
|
||||
<Menu.Item>
|
||||
<Logout />
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
<ExportConversation />
|
||||
<DarkMode />
|
||||
<ClearConvos />
|
||||
<Logout />
|
||||
</>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,8 +142,8 @@ export default function Nav({ navVisible, setNavVisible }) {
|
||||
}
|
||||
>
|
||||
<div className="flex h-full min-h-0 flex-col ">
|
||||
<div className="scrollbar-trigger flex h-full w-full flex-1 items-start border-white/20">
|
||||
<nav className="flex h-full flex-1 flex-col space-y-1 p-2">
|
||||
<div className="scrollbar-trigger flex h-full w-full flex-1 items-start border-white/20 relative">
|
||||
<nav className="flex h-full flex-1 flex-col space-y-1 p-2 relative">
|
||||
<NewChat />
|
||||
<div
|
||||
className={`flex-1 flex-col overflow-y-auto ${
|
||||
|
||||
34
client/src/components/svg/DotsIcon.jsx
Normal file
34
client/src/components/svg/DotsIcon.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function DotsIcon() {
|
||||
return (
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
strokeWidth="2"
|
||||
viewBox="0 0 24 24"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="h-4 w-4 flex-shrink-0 text-gray-500"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="1"
|
||||
/>
|
||||
<circle
|
||||
cx="19"
|
||||
cy="12"
|
||||
r="1"
|
||||
/>
|
||||
<circle
|
||||
cx="5"
|
||||
cy="12"
|
||||
r="1"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,110 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Signifier;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("../fonts/signifier-light.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Signifier;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url("../fonts/signifier-light-italic.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Signifier;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url("../fonts/signifier-bold.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Signifier;
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: url("../fonts/signifier-bold-italic.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Söhne;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("../fonts/soehne-buch.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Söhne;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url("../fonts/soehne-buch-kursiv.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Söhne;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url("../fonts/soehne-kraftig.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Söhne;
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: url("../fonts/soehne-kraftig-kursiv.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Söhne;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url("../fonts/soehne-halbfett.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Söhne;
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: url("../fonts/soehne-halbfett-kursiv.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Söhne Mono;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("../fonts/soehne-mono-buch.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Söhne Mono;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url("../fonts/soehne-mono-halbfett.woff2") format("woff2")
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: Söhne Mono;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url("../fonts/soehne-mono-buch-kursiv.woff2") format("woff2")
|
||||
}
|
||||
|
||||
/* * {
|
||||
box-sizing: border-box;
|
||||
outline: 1px solid limegreen !important;
|
||||
|
||||
@@ -9,10 +9,11 @@ module.exports = {
|
||||
// colors: {
|
||||
// 'gpt-dark-gray': '#343541',
|
||||
// },
|
||||
fontFamily: {
|
||||
sans: ['Söhne', 'sans-serif'],
|
||||
mono: ['Söhne Mono', 'monospace'],
|
||||
},
|
||||
extend: {
|
||||
// fontFamily: {
|
||||
// sans: ['var(--font-sans)', ...fontFamily.sans]
|
||||
// },
|
||||
keyframes: {
|
||||
'accordion-down': {
|
||||
from: { height: 0 },
|
||||
@@ -52,7 +53,7 @@ module.exports = {
|
||||
800: "#06373e",
|
||||
900: "#031f29",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "chatgpt-clone",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "chatgpt-clone",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"license": "ISC",
|
||||
"workspaces": [
|
||||
"api",
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"api": {
|
||||
"name": "chat-backend",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@dqbd/tiktoken": "^1.0.2",
|
||||
@@ -63,7 +63,7 @@
|
||||
},
|
||||
"client": {
|
||||
"name": "chat-frontend",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chatgpt-clone",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"description": "",
|
||||
"workspaces": [
|
||||
"api",
|
||||
|
||||
Reference in New Issue
Block a user