Compare commits

...

29 Commits

Author SHA1 Message Date
dependabot[bot]
27d34000b0 chore(deps-dev): bump babel-loader in /extensions/react-widget
Bumps [babel-loader](https://github.com/babel/babel-loader) from 8.3.0 to 10.0.0.
- [Release notes](https://github.com/babel/babel-loader/releases)
- [Changelog](https://github.com/babel/babel-loader/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel-loader/compare/v8.3.0...v10.0.0)

---
updated-dependencies:
- dependency-name: babel-loader
  dependency-version: 10.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-07 15:18:55 +00:00
Manish Madan
fbf7cf874b chore(dependabot): add react-widget npm dependency updates (#2146) 2025-11-07 17:17:46 +02:00
Manish Madan
ba7278b80f Merge pull request #2140 from arc53/dependabot/npm_and_yarn/frontend/husky-9.1.7
chore(deps-dev): bump husky from 8.0.3 to 9.1.7 in /frontend
2025-11-07 03:02:52 +05:30
ManishMadan2882
9d649de6f9 chore(eslint): migrate to ESLint 9 flat config format
- Add eslint.config.js with ESLint 9 flat config format
- Remove deprecated .eslintrc.cjs file
- Remove deprecated .eslintignore file (replaced by ignores in config)
- Maintain all existing ESLint rules and configurations
- Ensure compatibility with Husky 9.1.7
2025-11-07 02:59:51 +05:30
dependabot[bot]
7929afbf58 chore(deps-dev): bump husky from 8.0.3 to 9.1.7 in /frontend
Bumps [husky](https://github.com/typicode/husky) from 8.0.3 to 9.1.7.
- [Release notes](https://github.com/typicode/husky/releases)
- [Commits](https://github.com/typicode/husky/compare/v8.0.3...v9.1.7)

---
updated-dependencies:
- dependency-name: husky
  dependency-version: 9.1.7
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-06 21:27:39 +00:00
Manish Madan
ceaf942e70 Merge pull request #2139 from arc53/dependabot/npm_and_yarn/frontend/eslint-9.39.1
chore(deps-dev): bump eslint from 8.57.1 to 9.39.1 in /frontend
2025-11-07 02:33:32 +05:30
dependabot[bot]
f355601a44 chore(deps-dev): bump eslint from 8.57.1 to 9.39.1 in /frontend
Bumps [eslint](https://github.com/eslint/eslint) from 8.57.1 to 9.39.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v8.57.1...v9.39.1)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.39.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-06 20:00:14 +00:00
Manish Madan
4ff99a1e86 Merge pull request #2138 from arc53/dependabot/npm_and_yarn/frontend/reduxjs/toolkit-2.10.1
chore(deps): bump @reduxjs/toolkit from 2.9.2 to 2.10.1 in /frontend
2025-11-07 01:28:58 +05:30
dependabot[bot]
129084ba92 chore(deps): bump @reduxjs/toolkit from 2.9.2 to 2.10.1 in /frontend
Bumps [@reduxjs/toolkit](https://github.com/reduxjs/redux-toolkit) from 2.9.2 to 2.10.1.
- [Release notes](https://github.com/reduxjs/redux-toolkit/releases)
- [Commits](https://github.com/reduxjs/redux-toolkit/compare/v2.9.2...v2.10.1)

---
updated-dependencies:
- dependency-name: "@reduxjs/toolkit"
  dependency-version: 2.10.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-06 19:56:28 +00:00
Manish Madan
2288df1293 Merge pull request #2141 from arc53/dependabot/npm_and_yarn/frontend/vite-7.2.0
chore(deps-dev): bump vite from 7.1.12 to 7.2.0 in /frontend
2025-11-07 01:05:29 +05:30
Manish Madan
d9dfac55e7 Merge pull request #2134 from arc53/dependabot/npm_and_yarn/frontend/types/mermaid-9.2.0
chore(deps-dev): bump @types/mermaid from 9.1.0 to 9.2.0 in /frontend
2025-11-06 17:46:59 +05:30
Nick
404cf4b7c7 Update quickstart.mdx (#2142)
Added missing **
2025-11-06 12:37:27 +02:00
dependabot[bot]
f1c1fc123b chore(deps-dev): bump vite from 7.1.12 to 7.2.0 in /frontend
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.12 to 7.2.0.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.2.0/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 20:08:29 +00:00
ManishMadan2882
9f19c7ee4c Remove deprecated @types/mermaid dependency - mermaid provides its own types 2025-11-05 20:43:47 +05:30
dependabot[bot]
155e74eca1 chore(deps-dev): bump @types/mermaid from 9.1.0 to 9.2.0 in /frontend
Bumps [@types/mermaid](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mermaid) from 9.1.0 to 9.2.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/mermaid)

---
updated-dependencies:
- dependency-name: "@types/mermaid"
  dependency-version: 9.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 15:10:19 +00:00
Manish Madan
ea2dc4dbcb Merge pull request #2133 from arc53/dependabot/npm_and_yarn/frontend/react-i18next-16.2.4
chore(deps): bump react-i18next from 15.7.4 to 16.2.4 in /frontend
2025-11-05 20:23:15 +05:30
dependabot[bot]
616edc97de chore(deps): bump react-i18next from 15.7.4 to 16.2.4 in /frontend
Bumps [react-i18next](https://github.com/i18next/react-i18next) from 15.7.4 to 16.2.4.
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v15.7.4...v16.2.4)

---
updated-dependencies:
- dependency-name: react-i18next
  dependency-version: 16.2.4
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 14:48:28 +00:00
Manish Madan
b017e99c79 Merge pull request #2132 from arc53/dependabot/npm_and_yarn/frontend/eslint-plugin-n-17.23.1
chore(deps-dev): bump eslint-plugin-n from 16.6.2 to 17.23.1 in /frontend
2025-11-05 20:14:18 +05:30
dependabot[bot]
f698e9d3e1 chore(deps-dev): bump eslint-plugin-n in /frontend
Bumps [eslint-plugin-n](https://github.com/eslint-community/eslint-plugin-n) from 16.6.2 to 17.23.1.
- [Release notes](https://github.com/eslint-community/eslint-plugin-n/releases)
- [Changelog](https://github.com/eslint-community/eslint-plugin-n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint-community/eslint-plugin-n/compare/16.6.2...v17.23.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-n
  dependency-version: 17.23.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 14:35:17 +00:00
Manish Madan
d366502850 Merge pull request #2131 from arc53/dependabot/npm_and_yarn/frontend/typescript-eslint/parser-8.46.3
chore(deps-dev): bump @typescript-eslint/parser from 6.21.0 to 8.46.3 in /frontend
2025-11-05 20:03:59 +05:30
ManishMadan2882
3d6757c170 (chore:lint) relax rules, build fix 2025-11-05 20:02:01 +05:30
Manish Madan
cb8302add8 Fixes shared conversation on cloud version (#2135)
* (fix:shared) conv as id, not dbref

* (chore) script to migrate dbref to id

* (chore): ruff fix

---------

Co-authored-by: GH Action - Upstream Sync <action@github.com>
2025-11-05 16:08:10 +02:00
dependabot[bot]
9d266e9fad chore(deps-dev): bump @typescript-eslint/parser in /frontend
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.21.0 to 8.46.3.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.3/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.46.3
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 13:45:18 +00:00
Manish Madan
ae94c9d31e Merge pull request #2130 from arc53/dependabot/npm_and_yarn/frontend/vite-7.1.12
chore(deps-dev): bump vite from 6.4.1 to 7.1.12 in /frontend
2025-11-05 19:13:59 +05:30
ManishMadan2882
83ab232dcd (chore:fe) pkg lock 2025-11-05 19:12:20 +05:30
dependabot[bot]
eea85772a3 chore(deps-dev): bump vite from 6.4.1 to 7.1.12 in /frontend
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.4.1 to 7.1.12.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.1.12/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.12/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.12
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 19:10:27 +05:30
Alex
0fe7e223cc fix: update Discord invite link across documentation and navigation 2025-11-04 09:27:22 +00:00
Heisenberg Vader
3789d2eb03 Updated the technique for handling multiple file uploads from the user (#2126)
* Fixed multiple file uploads to be sent through a single request to backend for further processing and storing

* Fixed multiple file uploads to be sent through a single request to backend for further processing and storing

* Fixed multiple file uploads to be sent through a single request to backend for further processing and storing

* Made duplicate multiple keyword fixes

* Added back drag and drop functionality and it keeps the multiple file uploads
2025-11-04 01:12:35 +02:00
Manish Madan
d54469532e fix: adjust ESLint rules to warnings for strict type checking (#2129)
- Changed @typescript-eslint/no-explicit-any from error to warning
- Changed @typescript-eslint/no-unused-vars from error to warning
- Allows codebase to pass linting while maintaining code quality checks
- These rules can be gradually enforced as code is refactored
- Verified with npm run build - successful
2025-11-04 01:09:39 +02:00
17 changed files with 1378 additions and 1060 deletions

View File

@@ -13,7 +13,11 @@ updates:
directory: "/frontend" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "npm"
directory: "/extensions/react-widget"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
interval: "daily"

View File

@@ -147,5 +147,5 @@ Here's a step-by-step guide on how to contribute to DocsGPT:
Thank you for considering contributing to DocsGPT! 🙏
## Questions/collaboration
Feel free to join our [Discord](https://discord.gg/n5BX8dh8rU). We're very friendly and welcoming to new contributors, so don't hesitate to reach out.
Feel free to join our [Discord](https://discord.gg/vN7YFfdMpj). We're very friendly and welcoming to new contributors, so don't hesitate to reach out.
# Thank you so much for considering to contributing DocsGPT!🙏

View File

@@ -32,7 +32,7 @@ Non-Code Contributions:
- Before contributing check existing [issues](https://github.com/arc53/DocsGPT/issues) or [create](https://github.com/arc53/DocsGPT/issues/new/choose) an issue and wait to get assigned.
- Once you are finished with your contribution, please fill in this [form](https://forms.gle/Npaba4n9Epfyx56S8).
- Refer to the [Documentation](https://docs.docsgpt.cloud/).
- Feel free to join our [Discord](https://discord.gg/n5BX8dh8rU) server. We're here to help newcomers, so don't hesitate to jump in! Join us [here](https://discord.gg/n5BX8dh8rU).
- Feel free to join our [Discord](https://discord.gg/vN7YFfdMpj) server. We're here to help newcomers, so don't hesitate to jump in! Join us [here](https://discord.gg/vN7YFfdMpj).
Thank you very much for considering contributing to DocsGPT during Hacktoberfest! 🙏 Your contributions (not just simple typos) could earn you a stylish new t-shirt.

View File

@@ -16,10 +16,10 @@
<a href="https://github.com/arc53/DocsGPT">![link to main GitHub showing Forks number](https://img.shields.io/github/forks/arc53/docsgpt?style=social)</a>
<a href="https://github.com/arc53/DocsGPT/blob/main/LICENSE">![link to license file](https://img.shields.io/github/license/arc53/docsgpt)</a>
<a href="https://www.bestpractices.dev/projects/9907"><img src="https://www.bestpractices.dev/projects/9907/badge"></a>
<a href="https://discord.gg/n5BX8dh8rU">![link to discord](https://img.shields.io/discord/1070046503302877216)</a>
<a href="https://discord.gg/vN7YFfdMpj">![link to discord](https://img.shields.io/discord/1070046503302877216)</a>
<a href="https://x.com/docsgptai">![X (formerly Twitter) URL](https://img.shields.io/twitter/follow/docsgptai)</a>
<a href="https://docs.docsgpt.cloud/quickstart">⚡️ Quickstart</a><a href="https://app.docsgpt.cloud/">☁️ Cloud Version</a><a href="https://discord.gg/n5BX8dh8rU">💬 Discord</a>
<a href="https://docs.docsgpt.cloud/quickstart">⚡️ Quickstart</a><a href="https://app.docsgpt.cloud/">☁️ Cloud Version</a><a href="https://discord.gg/vN7YFfdMpj">💬 Discord</a>
<br>
<a href="https://docs.docsgpt.cloud/">📖 Documentation</a><a href="https://github.com/arc53/DocsGPT/blob/main/CONTRIBUTING.md">👫 Contribute</a><a href="https://blog.docsgpt.cloud/">🗞 Blog</a>
<br>

View File

@@ -25,7 +25,7 @@ class StoreAttachment(Resource):
api.model(
"AttachmentModel",
{
"file": fields.Raw(required=True, description="File to upload"),
"file": fields.Raw(required=True, description="File(s) to upload"),
"api_key": fields.String(
required=False, description="API key (optional)"
),
@@ -33,18 +33,24 @@ class StoreAttachment(Resource):
)
)
@api.doc(
description="Stores a single attachment without vectorization or training. Supports user or API key authentication."
description="Stores one or multiple attachments without vectorization or training. Supports user or API key authentication."
)
def post(self):
decoded_token = getattr(request, "decoded_token", None)
api_key = request.form.get("api_key") or request.args.get("api_key")
file = request.files.get("file")
if not file or file.filename == "":
files = request.files.getlist("file")
if not files:
single_file = request.files.get("file")
if single_file:
files = [single_file]
if not files or all(f.filename == "" for f in files):
return make_response(
jsonify({"status": "error", "message": "Missing file"}),
jsonify({"status": "error", "message": "Missing file(s)"}),
400,
)
user = None
if decoded_token:
user = safe_filename(decoded_token.get("sub"))
@@ -59,32 +65,74 @@ class StoreAttachment(Resource):
return make_response(
jsonify({"success": False, "message": "Authentication required"}), 401
)
try:
attachment_id = ObjectId()
original_filename = safe_filename(os.path.basename(file.filename))
relative_path = f"{settings.UPLOAD_FOLDER}/{user}/attachments/{str(attachment_id)}/{original_filename}"
tasks = []
errors = []
original_file_count = len(files)
for idx, file in enumerate(files):
try:
attachment_id = ObjectId()
original_filename = safe_filename(os.path.basename(file.filename))
relative_path = f"{settings.UPLOAD_FOLDER}/{user}/attachments/{str(attachment_id)}/{original_filename}"
metadata = storage.save_file(file, relative_path)
file_info = {
"filename": original_filename,
"attachment_id": str(attachment_id),
"path": relative_path,
"metadata": metadata,
}
task = store_attachment.delay(file_info, user)
return make_response(
jsonify(
{
"success": True,
"task_id": task.id,
"message": "File uploaded successfully. Processing started.",
metadata = storage.save_file(file, relative_path)
file_info = {
"filename": original_filename,
"attachment_id": str(attachment_id),
"path": relative_path,
"metadata": metadata,
}
),
200,
)
task = store_attachment.delay(file_info, user)
tasks.append({
"task_id": task.id,
"filename": original_filename,
"attachment_id": str(attachment_id),
})
except Exception as file_err:
current_app.logger.error(f"Error processing file {idx} ({file.filename}): {file_err}", exc_info=True)
errors.append({
"filename": file.filename,
"error": str(file_err)
})
if not tasks:
error_msg = "No valid files to upload"
if errors:
error_msg += f". Errors: {errors}"
return make_response(
jsonify({"status": "error", "message": error_msg, "errors": errors}),
400,
)
if original_file_count == 1 and len(tasks) == 1:
current_app.logger.info("Returning single task_id response")
return make_response(
jsonify(
{
"success": True,
"task_id": tasks[0]["task_id"],
"message": "File uploaded successfully. Processing started.",
}
),
200,
)
else:
response_data = {
"success": True,
"tasks": tasks,
"message": f"{len(tasks)} file(s) uploaded successfully. Processing started.",
}
if errors:
response_data["errors"] = errors
response_data["message"] += f" {len(errors)} file(s) failed."
return make_response(
jsonify(response_data),
200,
)
except Exception as err:
current_app.logger.error(f"Error storing attachment: {err}", exc_info=True)
return make_response(jsonify({"success": False, "error": str(err)}), 400)
@@ -130,15 +178,11 @@ class TextToSpeech(Resource):
@api.expect(tts_model)
@api.doc(description="Synthesize audio speech from text")
def post(self):
from application.utils import clean_text_for_tts
data = request.get_json()
text = data["text"]
cleaned_text = clean_text_for_tts(text)
try:
tts_instance = TTSCreator.create_tts(settings.TTS_PROVIDER)
audio_base64, detected_language = tts_instance.text_to_speech(cleaned_text)
audio_base64, detected_language = tts_instance.text_to_speech(text)
return make_response(
jsonify(
{

View File

@@ -13,7 +13,6 @@ from application.api.user.base import (
agents_collection,
attachments_collection,
conversations_collection,
db,
shared_conversations_collections,
)
from application.utils import check_required_fields
@@ -97,9 +96,7 @@ class ShareConversation(Resource):
api_uuid = pre_existing_api_document["key"]
pre_existing = shared_conversations_collections.find_one(
{
"conversation_id": DBRef(
"conversations", ObjectId(conversation_id)
),
"conversation_id": ObjectId(conversation_id),
"isPromptable": is_promptable,
"first_n_queries": current_n_queries,
"user": user,
@@ -120,10 +117,7 @@ class ShareConversation(Resource):
shared_conversations_collections.insert_one(
{
"uuid": explicit_binary,
"conversation_id": {
"$ref": "conversations",
"$id": ObjectId(conversation_id),
},
"conversation_id": ObjectId(conversation_id),
"isPromptable": is_promptable,
"first_n_queries": current_n_queries,
"user": user,
@@ -154,10 +148,7 @@ class ShareConversation(Resource):
shared_conversations_collections.insert_one(
{
"uuid": explicit_binary,
"conversation_id": {
"$ref": "conversations",
"$id": ObjectId(conversation_id),
},
"conversation_id": ObjectId(conversation_id),
"isPromptable": is_promptable,
"first_n_queries": current_n_queries,
"user": user,
@@ -175,9 +166,7 @@ class ShareConversation(Resource):
)
pre_existing = shared_conversations_collections.find_one(
{
"conversation_id": DBRef(
"conversations", ObjectId(conversation_id)
),
"conversation_id": ObjectId(conversation_id),
"isPromptable": is_promptable,
"first_n_queries": current_n_queries,
"user": user,
@@ -197,10 +186,7 @@ class ShareConversation(Resource):
shared_conversations_collections.insert_one(
{
"uuid": explicit_binary,
"conversation_id": {
"$ref": "conversations",
"$id": ObjectId(conversation_id),
},
"conversation_id": ObjectId(conversation_id),
"isPromptable": is_promptable,
"first_n_queries": current_n_queries,
"user": user,
@@ -233,10 +219,12 @@ class GetPubliclySharedConversations(Resource):
if (
shared
and "conversation_id" in shared
and isinstance(shared["conversation_id"], DBRef)
):
conversation_ref = shared["conversation_id"]
conversation = db.dereference(conversation_ref)
# conversation_id is now stored as an ObjectId, not a DBRef
conversation_id = shared["conversation_id"]
conversation = conversations_collection.find_one(
{"_id": conversation_id}
)
if conversation is None:
return make_response(
jsonify(

View File

@@ -57,7 +57,7 @@ The easiest way to launch DocsGPT is using the provided `setup.sh` script. This
* **4) Connect Cloud API Provider:** This option lets you connect DocsGPT to a commercial Cloud API provider such as OpenAI, Google (Vertex AI/Gemini), Anthropic (Claude), Groq, HuggingFace Inference API, or Azure OpenAI. You will need an API key from your chosen provider. Select this if you prefer to use a powerful cloud-based LLM.
* **5) Modify DocsGPT's source code and rebuild the Docker images locally. Instead of pulling prebuilt images from Docker Hub or using the hosted/public API, you build the entire backend and frontend from source, customizing how DocsGPT works internally, or run it in an environment without internet access.
* **5) Modify DocsGPT's source code and rebuild the Docker images locally.** Instead of pulling prebuilt images from Docker Hub or using the hosted/public API, you build the entire backend and frontend from source, customizing how DocsGPT works internally, or run it in an environment without internet access.
After selecting an option and providing any required information (like API keys or model names), the script will configure your `.env` file and start DocsGPT using Docker Compose.
@@ -119,4 +119,4 @@ If you prefer a more manual approach, you can follow our [Docker Deployment docu
For more advanced customization of DocsGPT settings, such as configuring vector stores, embedding models, and other parameters, please refer to the [DocsGPT Settings documentation](/Deploying/DocsGPT-Settings). This guide explains how to modify the `.env` file or `settings.py` for deeper configuration.
Enjoy using DocsGPT!
Enjoy using DocsGPT!

View File

@@ -38,7 +38,7 @@
"@types/markdown-it": "^14.1.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"babel-loader": "^8.0.4",
"babel-loader": "^10.0.0",
"parcel": "^2.12.0",
"process": "^0.11.10",
"typescript": "^5.3.3"
@@ -4639,7 +4639,8 @@
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/@types/linkify-it": {
"version": "5.0.0",
@@ -4929,22 +4930,6 @@
"node": ">=0.4.0"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
@@ -4990,37 +4975,26 @@
"license": "MIT",
"peer": true
},
"node_modules/ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true,
"peerDependencies": {
"ajv": "^6.9.1"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/babel-loader": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz",
"integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.0.0.tgz",
"integrity": "sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==",
"dev": true,
"license": "MIT",
"dependencies": {
"find-cache-dir": "^3.3.1",
"loader-utils": "^2.0.0",
"make-dir": "^3.1.0",
"schema-utils": "^2.6.5"
"find-up": "^5.0.0"
},
"engines": {
"node": ">= 8.9"
"node": "^18.20.0 || ^20.10.0 || >=22.0.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0",
"webpack": ">=2"
"@babel/core": "^7.12.0",
"webpack": ">=5.61.0"
}
},
"node_modules/babel-plugin-polyfill-corejs2": {
@@ -5071,15 +5045,6 @@
"safe-buffer": "^5.0.1"
}
},
"node_modules/big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@@ -5233,12 +5198,6 @@
"node": ">= 10"
}
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true
},
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -5427,15 +5386,6 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.72.tgz",
"integrity": "sha512-ZpSAUOZ2Izby7qnZluSrAlGgGQzucmFbN0n64dYzocYxnxV5ufurpj3VgEe4cUp7ir9LmeLxNYo8bVnlM8bQHw=="
},
"node_modules/emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"dev": true,
"engines": {
"node": ">= 4"
}
},
"node_modules/enhanced-resolve": {
"version": "5.18.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
@@ -5570,13 +5520,8 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/fast-uri": {
"version": "3.0.6",
@@ -5607,34 +5552,21 @@
"node": ">=8"
}
},
"node_modules/find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
"dev": true,
"dependencies": {
"commondir": "^1.0.1",
"make-dir": "^3.0.2",
"pkg-dir": "^4.1.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
}
},
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/flow-bin": {
@@ -5972,12 +5904,6 @@
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -6250,30 +6176,20 @@
"node": ">=6.11.5"
}
},
"node_modules/loader-utils": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dev": true,
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
},
"engines": {
"node": ">=8.9.0"
}
},
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
"p-locate": "^5.0.0"
},
"engines": {
"node": ">=8"
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lodash.debounce": {
@@ -6301,21 +6217,6 @@
"yallist": "^3.0.2"
}
},
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/markdown-it": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
@@ -8904,39 +8805,35 @@
"integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A=="
},
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
"yocto-queue": "^0.1.0"
},
"engines": {
"node": ">=6"
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
"p-limit": "^3.0.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"engines": {
"node": ">=6"
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/parcel": {
@@ -9074,6 +8971,7 @@
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
@@ -9108,18 +9006,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"dependencies": {
"find-up": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/postcss": {
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
@@ -9210,15 +9096,6 @@
"node": ">= 0.6.0"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
@@ -9411,24 +9288,6 @@
"loose-envify": "^1.1.0"
}
},
"node_modules/schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
"integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.5",
"ajv": "^6.12.4",
"ajv-keywords": "^3.5.2"
},
"engines": {
"node": ">= 8.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -9813,15 +9672,6 @@
"browserslist": ">= 4.21.0"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/utility-types": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz",
@@ -9983,6 +9833,19 @@
"engines": {
"node": ">= 6"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
}
}
}

View File

@@ -69,7 +69,7 @@
"@types/markdown-it": "^14.1.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"babel-loader": "^8.0.4",
"babel-loader": "^10.0.0",
"parcel": "^2.12.0",
"process": "^0.11.10",
"typescript": "^5.3.3"

View File

@@ -1,17 +0,0 @@
node_modules/
dist/
prettier.config.cjs
.eslintrc.cjs
env.d.ts
public/
assets/
vite-env.d.ts
.prettierignore
package-lock.json
package.json
postcss.config.cjs
prettier.config.cjs
tailwind.config.cjs
tsconfig.json
tsconfig.node.json
vite.config.ts

View File

@@ -1,45 +0,0 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:prettier/recommended',
],
overrides: [],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['react', 'unused-imports'],
rules: {
'react/prop-types': 'off',
'unused-imports/no-unused-imports': 'error',
'react/react-in-jsx-scope': 'off',
'prettier/prettier': [
'error',
{
endOfLine: 'auto',
},
],
},
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
react: {
version: 'detect',
},
'import/resolver': {
node: {
paths: ['src'],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
};

78
frontend/eslint.config.js Normal file
View File

@@ -0,0 +1,78 @@
import js from '@eslint/js'
import tsParser from '@typescript-eslint/parser'
import tsPlugin from '@typescript-eslint/eslint-plugin'
import react from 'eslint-plugin-react'
import unusedImports from 'eslint-plugin-unused-imports'
import prettier from 'eslint-plugin-prettier'
import globals from 'globals'
export default [
{
ignores: [
'node_modules/',
'dist/',
'prettier.config.cjs',
'.eslintrc.cjs',
'env.d.ts',
'public/',
'assets/',
'vite-env.d.ts',
'.prettierignore',
'package-lock.json',
'package.json',
'postcss.config.cjs',
'tailwind.config.cjs',
'tsconfig.json',
'tsconfig.node.json',
'vite.config.ts',
],
},
{
files: ['**/*.{js,jsx,ts,tsx}'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: tsParser,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
globals: {
...globals.browser,
...globals.es2021,
...globals.node,
},
},
plugins: {
'@typescript-eslint': tsPlugin,
react,
'unused-imports': unusedImports,
prettier,
},
rules: {
...js.configs.recommended.rules,
...tsPlugin.configs.recommended.rules,
...react.configs.recommended.rules,
...prettier.configs.recommended.rules,
'react/prop-types': 'off',
'unused-imports/no-unused-imports': 'error',
'react/react-in-jsx-scope': 'off',
'no-undef': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-unused-expressions': 'warn',
'prettier/prettier': [
'error',
{
endOfLine: 'auto',
},
],
},
settings: {
react: {
version: 'detect',
},
},
},
]

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@
]
},
"dependencies": {
"@reduxjs/toolkit": "^2.8.2",
"@reduxjs/toolkit": "^2.10.1",
"chart.js": "^4.4.4",
"clsx": "^2.1.1",
"copy-to-clipboard": "^3.3.3",
@@ -33,7 +33,7 @@
"react-dom": "^19.1.1",
"react-dropzone": "^14.3.8",
"react-google-drive-picker": "^1.2.2",
"react-i18next": "^15.4.0",
"react-i18next": "^16.2.4",
"react-markdown": "^9.0.1",
"react-redux": "^9.2.0",
"react-router-dom": "^7.6.1",
@@ -46,30 +46,28 @@
"devDependencies": {
"@tailwindcss/postcss": "^4.1.10",
"@types/lodash": "^4.17.20",
"@types/mermaid": "^9.1.0",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.7",
"@types/react-syntax-highlighter": "^15.5.13",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@typescript-eslint/eslint-plugin": "^8.46.3",
"@typescript-eslint/parser": "^8.46.3",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^8.57.1",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.5",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-n": "^17.23.1",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-promise": "^6.6.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-unused-imports": "^4.1.4",
"husky": "^8.0.0",
"husky": "^9.1.7",
"lint-staged": "^15.3.0",
"postcss": "^8.4.49",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.7.1",
"tailwindcss": "^4.1.11",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"vite": "^7.2.0",
"vite-plugin-svgr": "^4.3.0"
}
}

View File

@@ -567,7 +567,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
<div className="flex items-center gap-1 pr-4">
<NavLink
target="_blank"
to={'https://discord.gg/WHJdfbQDR4'}
to={'https://discord.gg/vN7YFfdMpj'}
className={
'rounded-full hover:bg-gray-100 dark:hover:bg-[#28292E]'
}

View File

@@ -19,8 +19,8 @@ import {
removeAttachment,
selectAttachments,
updateAttachment,
reorderAttachments,
} from '../upload/uploadSlice';
import { reorderAttachments } from '../upload/uploadSlice';
import { ActiveState } from '../models/misc';
import {
@@ -77,7 +77,7 @@ export default function MessageInput({
(browserOS === 'mac' && event.metaKey && event.key === 'k')
) {
event.preventDefault();
setIsSourcesPopupOpen(!isSourcesPopupOpen);
setIsSourcesPopupOpen((s) => !s);
}
};
@@ -89,8 +89,198 @@ export default function MessageInput({
const uploadFiles = useCallback(
(files: File[]) => {
if (!files || files.length === 0) return;
const apiHost = import.meta.env.VITE_API_HOST;
if (files.length > 1) {
const formData = new FormData();
const indexToUiId: Record<number, string> = {};
files.forEach((file, i) => {
formData.append('file', file);
const uiId = crypto.randomUUID();
indexToUiId[i] = uiId;
dispatch(
addAttachment({
id: uiId,
fileName: file.name,
progress: 0,
status: 'uploading' as const,
taskId: '',
}),
);
});
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const progress = Math.round((event.loaded / event.total) * 100);
Object.values(indexToUiId).forEach((uiId) =>
dispatch(
updateAttachment({
id: uiId,
updates: { progress },
}),
),
);
}
});
xhr.onload = () => {
const status = xhr.status;
if (status === 200) {
try {
const response = JSON.parse(xhr.responseText);
if (Array.isArray(response?.tasks)) {
const tasks = response.tasks as Array<{
task_id?: string;
filename?: string;
attachment_id?: string;
path?: string;
}>;
tasks.forEach((t, idx) => {
const uiId = indexToUiId[idx];
if (!uiId) return;
if (t?.task_id) {
dispatch(
updateAttachment({
id: uiId,
updates: {
taskId: t.task_id,
status: 'processing',
progress: 10,
},
}),
);
} else {
dispatch(
updateAttachment({
id: uiId,
updates: { status: 'failed' },
}),
);
}
});
if (tasks.length < files.length) {
for (let i = tasks.length; i < files.length; i++) {
const uiId = indexToUiId[i];
if (uiId) {
dispatch(
updateAttachment({
id: uiId,
updates: { status: 'failed' },
}),
);
}
}
}
} else if (response?.task_id) {
if (files.length === 1) {
const uiId = indexToUiId[0];
if (uiId) {
dispatch(
updateAttachment({
id: uiId,
updates: {
taskId: response.task_id,
status: 'processing',
progress: 10,
},
}),
);
}
} else {
console.warn(
'Server returned a single task_id for multiple files. Update backend to return tasks[].',
);
const firstUi = indexToUiId[0];
if (firstUi) {
dispatch(
updateAttachment({
id: firstUi,
updates: {
taskId: response.task_id,
status: 'processing',
progress: 10,
},
}),
);
}
for (let i = 1; i < files.length; i++) {
const uiId = indexToUiId[i];
if (uiId) {
dispatch(
updateAttachment({
id: uiId,
updates: { status: 'failed' },
}),
);
}
}
}
} else {
console.error('Unexpected upload response shape', response);
Object.values(indexToUiId).forEach((id) =>
dispatch(
updateAttachment({
id,
updates: { status: 'failed' },
}),
),
);
}
} catch (err) {
console.error(
'Failed to parse upload response',
err,
xhr.responseText,
);
Object.values(indexToUiId).forEach((id) =>
dispatch(
updateAttachment({
id,
updates: { status: 'failed' },
}),
),
);
}
} else {
console.error('Upload failed', status, xhr.responseText);
Object.values(indexToUiId).forEach((id) =>
dispatch(
updateAttachment({
id,
updates: { status: 'failed' },
}),
),
);
}
};
xhr.onerror = () => {
console.error('Upload network error');
Object.values(indexToUiId).forEach((id) =>
dispatch(
updateAttachment({
id,
updates: { status: 'failed' },
}),
),
);
};
xhr.open('POST', `${apiHost}${endpoints.USER.STORE_ATTACHMENT}`);
if (token) xhr.setRequestHeader('Authorization', `Bearer ${token}`);
xhr.send(formData);
return;
}
// Single-file path: upload each file individually (original repo behavior)
files.forEach((file) => {
const formData = new FormData();
formData.append('file', file);
@@ -121,16 +311,54 @@ export default function MessageInput({
xhr.onload = () => {
if (xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
if (response.task_id) {
try {
const response = JSON.parse(xhr.responseText);
if (response.task_id) {
dispatch(
updateAttachment({
id: uniqueId,
updates: {
taskId: response.task_id,
status: 'processing',
progress: 10,
},
}),
);
} else {
// If backend returned tasks[] for single-file, handle gracefully:
if (
Array.isArray(response?.tasks) &&
response.tasks[0]?.task_id
) {
dispatch(
updateAttachment({
id: uniqueId,
updates: {
taskId: response.tasks[0].task_id,
status: 'processing',
progress: 10,
},
}),
);
} else {
dispatch(
updateAttachment({
id: uniqueId,
updates: { status: 'failed' },
}),
);
}
}
} catch (err) {
console.error(
'Failed to parse upload response',
err,
xhr.responseText,
);
dispatch(
updateAttachment({
id: uniqueId,
updates: {
taskId: response.task_id,
status: 'processing',
progress: 10,
},
updates: { status: 'failed' },
}),
);
}
@@ -154,7 +382,7 @@ export default function MessageInput({
};
xhr.open('POST', `${apiHost}${endpoints.USER.STORE_ATTACHMENT}`);
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
if (token) xhr.setRequestHeader('Authorization', `Bearer ${token}`);
xhr.send(formData);
});
},
@@ -163,15 +391,13 @@ export default function MessageInput({
const handleFileAttachment = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!e.target.files || e.target.files.length === 0) return;
const files = Array.from(e.target.files);
uploadFiles(files);
// clear input so same file can be selected again
e.target.value = '';
};
// Drag and drop handler
// Drag & drop via react-dropzone
const onDrop = useCallback(
(acceptedFiles: File[]) => {
uploadFiles(acceptedFiles);
@@ -321,11 +547,8 @@ export default function MessageInput({
handleAbort();
};
// Drag state for reordering
const [draggingId, setDraggingId] = useState<string | null>(null);
// no preview object URLs to revoke (preview removed per reviewer request)
const findIndexById = (id: string) =>
attachments.findIndex((a) => a.id === id);
@@ -359,7 +582,9 @@ export default function MessageInput({
return (
<div {...getRootProps()} className="flex w-full flex-col">
{/* react-dropzone input (for drag/drop) */}
<input {...getInputProps()} />
<div className="border-dark-gray bg-lotion dark:border-grey relative flex w-full flex-col rounded-[23px] border dark:bg-transparent">
<div className="flex flex-wrap gap-1.5 px-2 py-2 sm:gap-2 sm:px-3">
{attachments.map((attachment) => {
@@ -374,7 +599,11 @@ export default function MessageInput({
attachment.status !== 'completed'
? 'opacity-70'
: 'opacity-100'
} ${draggingId === attachment.id ? 'ring-dashed opacity-60 ring-2 ring-purple-200' : ''}`}
} ${
draggingId === attachment.id
? 'ring-dashed opacity-60 ring-2 ring-purple-200'
: ''
}`}
title={attachment.fileName}
>
<div className="bg-purple-30 mr-2 flex h-8 w-8 items-center justify-center rounded-md p-1">

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""
Migration script to convert conversation_id from DBRef to ObjectId in shared_conversations collection.
"""
import pymongo
import logging
from tqdm import tqdm
from bson.dbref import DBRef
from bson.objectid import ObjectId
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger()
# Configuration
MONGO_URI = "mongodb://localhost:27017/"
DB_NAME = "docsgpt"
def backup_collection(collection, backup_collection_name):
"""Backup collection before migration."""
logger.info(f"Backing up collection {collection.name} to {backup_collection_name}")
collection.aggregate([{"$out": backup_collection_name}])
logger.info("Backup completed")
def migrate_conversation_id_dbref_to_objectid():
"""Migrate conversation_id from DBRef to ObjectId."""
client = pymongo.MongoClient(MONGO_URI)
db = client[DB_NAME]
shared_conversations_collection = db["shared_conversations"]
try:
# Backup collection before migration
backup_collection(shared_conversations_collection, "shared_conversations_backup")
# Find all documents and filter for DBRef conversation_id in Python
all_documents = list(shared_conversations_collection.find({}))
documents_with_dbref = []
for doc in all_documents:
conversation_id_field = doc.get("conversation_id")
if isinstance(conversation_id_field, DBRef):
documents_with_dbref.append(doc)
if not documents_with_dbref:
logger.info("No documents with DBRef conversation_id found. Migration not needed.")
return
logger.info(f"Found {len(documents_with_dbref)} documents with DBRef conversation_id")
# Process each document
migrated_count = 0
error_count = 0
for doc in tqdm(documents_with_dbref, desc="Migrating conversation_id"):
try:
conversation_id_field = doc.get("conversation_id")
# Extract the ObjectId from the DBRef
dbref_id = conversation_id_field.id
if dbref_id and ObjectId.is_valid(dbref_id):
# Update the document to use direct ObjectId
result = shared_conversations_collection.update_one(
{"_id": doc["_id"]},
{"$set": {"conversation_id": dbref_id}}
)
if result.modified_count > 0:
migrated_count += 1
logger.debug(f"Successfully migrated document {doc['_id']}")
else:
error_count += 1
logger.warning(f"Failed to update document {doc['_id']}")
else:
error_count += 1
logger.warning(f"Invalid ObjectId in DBRef for document {doc['_id']}: {dbref_id}")
except Exception as e:
error_count += 1
logger.error(f"Error migrating document {doc['_id']}: {e}")
# Final verification
all_docs_after = list(shared_conversations_collection.find({}))
remaining_dbref = 0
for doc in all_docs_after:
if isinstance(doc.get("conversation_id"), DBRef):
remaining_dbref += 1
logger.info("Migration completed:")
logger.info(f" - Total documents processed: {len(documents_with_dbref)}")
logger.info(f" - Successfully migrated: {migrated_count}")
logger.info(f" - Errors encountered: {error_count}")
logger.info(f" - Remaining DBRef documents: {remaining_dbref}")
if remaining_dbref == 0:
logger.info("✅ Migration successful: All DBRef conversation_id fields have been converted to ObjectId")
else:
logger.warning(f"⚠️ Migration incomplete: {remaining_dbref} DBRef documents still exist")
except Exception as e:
logger.error(f"Migration failed: {e}")
raise
finally:
client.close()
if __name__ == "__main__":
try:
logger.info("Starting conversation_id DBRef to ObjectId migration...")
migrate_conversation_id_dbref_to_objectid()
logger.info("Migration completed successfully!")
except Exception as e:
logger.error(f"Migration failed due to error: {e}")
logger.warning("Please verify database state or restore from backups if necessary.")