mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-05-04 00:40:44 +02:00
My docs cleanup (#4519)
* update * improved my docs * nit * nit * k * push changes * update * looking good * k * fix preprocessing * try a fix * k * update * nit * k * quick nits * Cleanup / fixes * Fixes * Fix build * fix * fix quality checks --------- Co-authored-by: Weves <chrisweaver101@gmail.com>
This commit is contained in:
parent
115cfb6ae9
commit
df67ca18d8
@ -11,6 +11,7 @@ from onyx.server.features.persona.models import PersonaSharedNotificationData
|
|||||||
|
|
||||||
def make_persona_private(
|
def make_persona_private(
|
||||||
persona_id: int,
|
persona_id: int,
|
||||||
|
creator_user_id: UUID | None,
|
||||||
user_ids: list[UUID] | None,
|
user_ids: list[UUID] | None,
|
||||||
group_ids: list[int] | None,
|
group_ids: list[int] | None,
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
@ -29,15 +30,15 @@ def make_persona_private(
|
|||||||
user_ids_set = set(user_ids)
|
user_ids_set = set(user_ids)
|
||||||
for user_id in user_ids_set:
|
for user_id in user_ids_set:
|
||||||
db_session.add(Persona__User(persona_id=persona_id, user_id=user_id))
|
db_session.add(Persona__User(persona_id=persona_id, user_id=user_id))
|
||||||
|
if user_id != creator_user_id:
|
||||||
create_notification(
|
create_notification(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
notif_type=NotificationType.PERSONA_SHARED,
|
notif_type=NotificationType.PERSONA_SHARED,
|
||||||
db_session=db_session,
|
db_session=db_session,
|
||||||
additional_data=PersonaSharedNotificationData(
|
additional_data=PersonaSharedNotificationData(
|
||||||
persona_id=persona_id,
|
persona_id=persona_id,
|
||||||
).model_dump(),
|
).model_dump(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if group_ids:
|
if group_ids:
|
||||||
group_ids_set = set(group_ids)
|
group_ids_set = set(group_ids)
|
||||||
|
@ -40,6 +40,7 @@ def process_llm_stream(
|
|||||||
# This stream will be the llm answer if no tool is chosen. When a tool is chosen,
|
# This stream will be the llm answer if no tool is chosen. When a tool is chosen,
|
||||||
# the stream will contain AIMessageChunks with tool call information.
|
# the stream will contain AIMessageChunks with tool call information.
|
||||||
for message in messages:
|
for message in messages:
|
||||||
|
|
||||||
answer_piece = message.content
|
answer_piece = message.content
|
||||||
if not isinstance(answer_piece, str):
|
if not isinstance(answer_piece, str):
|
||||||
# this is only used for logging, so fine to
|
# this is only used for logging, so fine to
|
||||||
|
@ -51,7 +51,6 @@ def _parse_agent_event(
|
|||||||
Parse the event into a typed object.
|
Parse the event into a typed object.
|
||||||
Return None if we are not interested in the event.
|
Return None if we are not interested in the event.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
event_type = event["event"]
|
event_type = event["event"]
|
||||||
|
|
||||||
# We always just yield the event data, but this piece is useful for two development reasons:
|
# We always just yield the event data, but this piece is useful for two development reasons:
|
||||||
|
@ -112,7 +112,7 @@ def pre_provision_tenant() -> None:
|
|||||||
|
|
||||||
r = get_redis_client(tenant_id=ONYX_CLOUD_TENANT_ID)
|
r = get_redis_client(tenant_id=ONYX_CLOUD_TENANT_ID)
|
||||||
lock_provision: RedisLock = r.lock(
|
lock_provision: RedisLock = r.lock(
|
||||||
OnyxRedisLocks.PRE_PROVISION_TENANT_LOCK,
|
OnyxRedisLocks.CLOUD_PRE_PROVISION_TENANT_LOCK,
|
||||||
timeout=_TENANT_PROVISIONING_SOFT_TIME_LIMIT,
|
timeout=_TENANT_PROVISIONING_SOFT_TIME_LIMIT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -167,7 +167,6 @@ class Answer:
|
|||||||
break
|
break
|
||||||
processed_stream.append(packet)
|
processed_stream.append(packet)
|
||||||
yield packet
|
yield packet
|
||||||
|
|
||||||
self._processed_stream = processed_stream
|
self._processed_stream = processed_stream
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -334,7 +334,7 @@ class OnyxRedisLocks:
|
|||||||
CHECK_USER_FILE_FOLDER_SYNC_BEAT_LOCK = "da_lock:check_user_file_folder_sync_beat"
|
CHECK_USER_FILE_FOLDER_SYNC_BEAT_LOCK = "da_lock:check_user_file_folder_sync_beat"
|
||||||
MONITOR_BACKGROUND_PROCESSES_LOCK = "da_lock:monitor_background_processes"
|
MONITOR_BACKGROUND_PROCESSES_LOCK = "da_lock:monitor_background_processes"
|
||||||
CHECK_AVAILABLE_TENANTS_LOCK = "da_lock:check_available_tenants"
|
CHECK_AVAILABLE_TENANTS_LOCK = "da_lock:check_available_tenants"
|
||||||
PRE_PROVISION_TENANT_LOCK = "da_lock:pre_provision_tenant"
|
CLOUD_PRE_PROVISION_TENANT_LOCK = "da_lock:pre_provision_tenant"
|
||||||
|
|
||||||
CONNECTOR_DOC_PERMISSIONS_SYNC_LOCK_PREFIX = (
|
CONNECTOR_DOC_PERMISSIONS_SYNC_LOCK_PREFIX = (
|
||||||
"da_lock:connector_doc_permissions_sync"
|
"da_lock:connector_doc_permissions_sync"
|
||||||
@ -405,7 +405,6 @@ class OnyxCeleryTask:
|
|||||||
f"{ONYX_CLOUD_CELERY_TASK_PREFIX}_monitor_celery_pidbox"
|
f"{ONYX_CLOUD_CELERY_TASK_PREFIX}_monitor_celery_pidbox"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Tenant pre-provisioning
|
|
||||||
UPDATE_USER_FILE_FOLDER_METADATA = "update_user_file_folder_metadata"
|
UPDATE_USER_FILE_FOLDER_METADATA = "update_user_file_folder_metadata"
|
||||||
|
|
||||||
CHECK_FOR_CONNECTOR_DELETION = "check_for_connector_deletion_task"
|
CHECK_FOR_CONNECTOR_DELETION = "check_for_connector_deletion_task"
|
||||||
|
@ -3,7 +3,6 @@ from datetime import datetime
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from sqlalchemy import delete
|
|
||||||
from sqlalchemy import exists
|
from sqlalchemy import exists
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from sqlalchemy import not_
|
from sqlalchemy import not_
|
||||||
@ -168,6 +167,7 @@ def _get_persona_by_name(
|
|||||||
|
|
||||||
def make_persona_private(
|
def make_persona_private(
|
||||||
persona_id: int,
|
persona_id: int,
|
||||||
|
creator_user_id: UUID | None,
|
||||||
user_ids: list[UUID] | None,
|
user_ids: list[UUID] | None,
|
||||||
group_ids: list[int] | None,
|
group_ids: list[int] | None,
|
||||||
db_session: Session,
|
db_session: Session,
|
||||||
@ -179,15 +179,15 @@ def make_persona_private(
|
|||||||
|
|
||||||
for user_uuid in user_ids:
|
for user_uuid in user_ids:
|
||||||
db_session.add(Persona__User(persona_id=persona_id, user_id=user_uuid))
|
db_session.add(Persona__User(persona_id=persona_id, user_id=user_uuid))
|
||||||
|
if user_uuid != creator_user_id:
|
||||||
create_notification(
|
create_notification(
|
||||||
user_id=user_uuid,
|
user_id=user_uuid,
|
||||||
notif_type=NotificationType.PERSONA_SHARED,
|
notif_type=NotificationType.PERSONA_SHARED,
|
||||||
db_session=db_session,
|
db_session=db_session,
|
||||||
additional_data=PersonaSharedNotificationData(
|
additional_data=PersonaSharedNotificationData(
|
||||||
persona_id=persona_id,
|
persona_id=persona_id,
|
||||||
).model_dump(),
|
).model_dump(),
|
||||||
)
|
)
|
||||||
|
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|
||||||
@ -262,6 +262,7 @@ def create_update_persona(
|
|||||||
# Privatize Persona
|
# Privatize Persona
|
||||||
versioned_make_persona_private(
|
versioned_make_persona_private(
|
||||||
persona_id=persona.id,
|
persona_id=persona.id,
|
||||||
|
creator_user_id=user.id if user else None,
|
||||||
user_ids=create_persona_request.users,
|
user_ids=create_persona_request.users,
|
||||||
group_ids=create_persona_request.groups,
|
group_ids=create_persona_request.groups,
|
||||||
db_session=db_session,
|
db_session=db_session,
|
||||||
@ -297,6 +298,7 @@ def update_persona_shared_users(
|
|||||||
# Privatize Persona
|
# Privatize Persona
|
||||||
versioned_make_persona_private(
|
versioned_make_persona_private(
|
||||||
persona_id=persona_id,
|
persona_id=persona_id,
|
||||||
|
creator_user_id=user.id if user else None,
|
||||||
user_ids=user_ids,
|
user_ids=user_ids,
|
||||||
group_ids=None,
|
group_ids=None,
|
||||||
db_session=db_session,
|
db_session=db_session,
|
||||||
@ -770,8 +772,10 @@ def get_personas_by_ids(
|
|||||||
def delete_persona_by_name(
|
def delete_persona_by_name(
|
||||||
persona_name: str, db_session: Session, is_default: bool = True
|
persona_name: str, db_session: Session, is_default: bool = True
|
||||||
) -> None:
|
) -> None:
|
||||||
stmt = delete(Persona).where(
|
stmt = (
|
||||||
Persona.name == persona_name, Persona.builtin_persona == is_default
|
update(Persona)
|
||||||
|
.where(Persona.name == persona_name, Persona.builtin_persona == is_default)
|
||||||
|
.values(deleted=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
db_session.execute(stmt)
|
db_session.execute(stmt)
|
||||||
|
@ -69,7 +69,8 @@ def test_llm_configuration(
|
|||||||
existing_provider = fetch_existing_llm_provider(
|
existing_provider = fetch_existing_llm_provider(
|
||||||
name=test_llm_request.name, db_session=db_session
|
name=test_llm_request.name, db_session=db_session
|
||||||
)
|
)
|
||||||
if existing_provider:
|
# if an API key is not provided, use the existing provider's API key
|
||||||
|
if existing_provider and test_api_key is None:
|
||||||
test_api_key = existing_provider.api_key
|
test_api_key = existing_provider.api_key
|
||||||
|
|
||||||
# For this "testing" workflow, we do *not* need the actual `max_input_tokens`.
|
# For this "testing" workflow, we do *not* need the actual `max_input_tokens`.
|
||||||
|
@ -90,8 +90,25 @@ def get_folders(
|
|||||||
db_session: Session = Depends(get_session),
|
db_session: Session = Depends(get_session),
|
||||||
) -> list[UserFolderSnapshot]:
|
) -> list[UserFolderSnapshot]:
|
||||||
user_id = user.id if user else None
|
user_id = user.id if user else None
|
||||||
folders = db_session.query(UserFolder).filter(UserFolder.user_id == user_id).all()
|
# Get folders that belong to the user or have the RECENT_DOCS_FOLDER_ID
|
||||||
return [UserFolderSnapshot.from_model(folder) for folder in folders]
|
folders = (
|
||||||
|
db_session.query(UserFolder)
|
||||||
|
.filter(
|
||||||
|
(UserFolder.user_id == user_id) | (UserFolder.id == RECENT_DOCS_FOLDER_ID)
|
||||||
|
)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
# For each folder, filter files to only include those belonging to the current user
|
||||||
|
result = []
|
||||||
|
for folder in folders:
|
||||||
|
folder_snapshot = UserFolderSnapshot.from_model(folder)
|
||||||
|
folder_snapshot.files = [
|
||||||
|
file for file in folder_snapshot.files if file.user_id == user_id
|
||||||
|
]
|
||||||
|
result.append(folder_snapshot)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
@router.get("/user/folder/{folder_id}")
|
@router.get("/user/folder/{folder_id}")
|
||||||
@ -103,13 +120,25 @@ def get_folder(
|
|||||||
user_id = user.id if user else None
|
user_id = user.id if user else None
|
||||||
folder = (
|
folder = (
|
||||||
db_session.query(UserFolder)
|
db_session.query(UserFolder)
|
||||||
.filter(UserFolder.id == folder_id, UserFolder.user_id == user_id)
|
.filter(
|
||||||
|
UserFolder.id == folder_id,
|
||||||
|
(
|
||||||
|
(UserFolder.user_id == user_id)
|
||||||
|
| (UserFolder.id == RECENT_DOCS_FOLDER_ID)
|
||||||
|
),
|
||||||
|
)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
if not folder:
|
if not folder:
|
||||||
raise HTTPException(status_code=404, detail="Folder not found")
|
raise HTTPException(status_code=404, detail="Folder not found")
|
||||||
|
|
||||||
return UserFolderSnapshot.from_model(folder)
|
folder_snapshot = UserFolderSnapshot.from_model(folder)
|
||||||
|
# Filter files to only include those belonging to the current user
|
||||||
|
folder_snapshot.files = [
|
||||||
|
file for file in folder_snapshot.files if file.user_id == user_id
|
||||||
|
]
|
||||||
|
|
||||||
|
return folder_snapshot
|
||||||
|
|
||||||
|
|
||||||
RECENT_DOCS_FOLDER_ID = -1
|
RECENT_DOCS_FOLDER_ID = -1
|
||||||
|
@ -27,7 +27,15 @@ ONYX_REQUEST_ID_CONTEXTVAR: contextvars.ContextVar[str | None] = contextvars.Con
|
|||||||
def get_current_tenant_id() -> str:
|
def get_current_tenant_id() -> str:
|
||||||
tenant_id = CURRENT_TENANT_ID_CONTEXTVAR.get()
|
tenant_id = CURRENT_TENANT_ID_CONTEXTVAR.get()
|
||||||
if tenant_id is None:
|
if tenant_id is None:
|
||||||
|
import traceback
|
||||||
|
|
||||||
if not MULTI_TENANT:
|
if not MULTI_TENANT:
|
||||||
return POSTGRES_DEFAULT_SCHEMA
|
return POSTGRES_DEFAULT_SCHEMA
|
||||||
raise RuntimeError("Tenant ID is not set. This should never happen.")
|
|
||||||
|
stack_trace = traceback.format_stack()
|
||||||
|
error_message = (
|
||||||
|
"Tenant ID is not set. This should never happen.\nStack trace:\n"
|
||||||
|
+ "".join(stack_trace)
|
||||||
|
)
|
||||||
|
raise RuntimeError(error_message)
|
||||||
return tenant_id
|
return tenant_id
|
||||||
|
70
web/package-lock.json
generated
70
web/package-lock.json
generated
@ -1886,7 +1886,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@jridgewell/source-map": {
|
"node_modules/@jridgewell/source-map": {
|
||||||
"version": "0.3.6",
|
"version": "0.3.6",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.5",
|
"@jridgewell/gen-mapping": "^0.3.5",
|
||||||
@ -2711,7 +2710,7 @@
|
|||||||
"version": "1.51.1",
|
"version": "1.51.1",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.1.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.1.tgz",
|
||||||
"integrity": "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==",
|
"integrity": "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.51.1"
|
"playwright": "1.51.1"
|
||||||
@ -4812,7 +4811,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "9.6.1",
|
"version": "9.6.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "*",
|
"@types/estree": "*",
|
||||||
@ -4821,7 +4819,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@types/eslint-scope": {
|
"node_modules/@types/eslint-scope": {
|
||||||
"version": "3.7.7",
|
"version": "3.7.7",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/eslint": "*",
|
"@types/eslint": "*",
|
||||||
@ -4921,7 +4918,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.15",
|
"version": "7.0.15",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/json5": {
|
"node_modules/@types/json5": {
|
||||||
@ -5276,7 +5272,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/ast": {
|
"node_modules/@webassemblyjs/ast": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@webassemblyjs/helper-numbers": "1.13.2",
|
"@webassemblyjs/helper-numbers": "1.13.2",
|
||||||
@ -5285,22 +5280,18 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/floating-point-hex-parser": {
|
"node_modules/@webassemblyjs/floating-point-hex-parser": {
|
||||||
"version": "1.13.2",
|
"version": "1.13.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/helper-api-error": {
|
"node_modules/@webassemblyjs/helper-api-error": {
|
||||||
"version": "1.13.2",
|
"version": "1.13.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/helper-buffer": {
|
"node_modules/@webassemblyjs/helper-buffer": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/helper-numbers": {
|
"node_modules/@webassemblyjs/helper-numbers": {
|
||||||
"version": "1.13.2",
|
"version": "1.13.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
|
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
|
||||||
@ -5310,12 +5301,10 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
|
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
|
||||||
"version": "1.13.2",
|
"version": "1.13.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/helper-wasm-section": {
|
"node_modules/@webassemblyjs/helper-wasm-section": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@webassemblyjs/ast": "1.14.1",
|
"@webassemblyjs/ast": "1.14.1",
|
||||||
@ -5326,7 +5315,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/ieee754": {
|
"node_modules/@webassemblyjs/ieee754": {
|
||||||
"version": "1.13.2",
|
"version": "1.13.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xtuc/ieee754": "^1.2.0"
|
"@xtuc/ieee754": "^1.2.0"
|
||||||
@ -5334,7 +5322,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/leb128": {
|
"node_modules/@webassemblyjs/leb128": {
|
||||||
"version": "1.13.2",
|
"version": "1.13.2",
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
@ -5342,12 +5329,10 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/utf8": {
|
"node_modules/@webassemblyjs/utf8": {
|
||||||
"version": "1.13.2",
|
"version": "1.13.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/wasm-edit": {
|
"node_modules/@webassemblyjs/wasm-edit": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@webassemblyjs/ast": "1.14.1",
|
"@webassemblyjs/ast": "1.14.1",
|
||||||
@ -5362,7 +5347,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/wasm-gen": {
|
"node_modules/@webassemblyjs/wasm-gen": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@webassemblyjs/ast": "1.14.1",
|
"@webassemblyjs/ast": "1.14.1",
|
||||||
@ -5374,7 +5358,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/wasm-opt": {
|
"node_modules/@webassemblyjs/wasm-opt": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@webassemblyjs/ast": "1.14.1",
|
"@webassemblyjs/ast": "1.14.1",
|
||||||
@ -5385,7 +5368,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/wasm-parser": {
|
"node_modules/@webassemblyjs/wasm-parser": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@webassemblyjs/ast": "1.14.1",
|
"@webassemblyjs/ast": "1.14.1",
|
||||||
@ -5398,7 +5380,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/wast-printer": {
|
"node_modules/@webassemblyjs/wast-printer": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@webassemblyjs/ast": "1.14.1",
|
"@webassemblyjs/ast": "1.14.1",
|
||||||
@ -5407,12 +5388,10 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@xtuc/ieee754": {
|
"node_modules/@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/@xtuc/long": {
|
"node_modules/@xtuc/long": {
|
||||||
"version": "4.2.2",
|
"version": "4.2.2",
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
@ -5467,7 +5446,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/ajv-formats": {
|
"node_modules/ajv-formats": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^8.0.0"
|
"ajv": "^8.0.0"
|
||||||
@ -5483,7 +5461,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/ajv-formats/node_modules/ajv": {
|
"node_modules/ajv-formats/node_modules/ajv": {
|
||||||
"version": "8.17.1",
|
"version": "8.17.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
@ -5498,7 +5475,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ajv-keywords": {
|
"node_modules/ajv-keywords": {
|
||||||
@ -6186,7 +6162,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/buffer-from": {
|
"node_modules/buffer-from": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/busboy": {
|
"node_modules/busboy": {
|
||||||
@ -6419,7 +6394,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/chrome-trace-event": {
|
"node_modules/chrome-trace-event": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0"
|
"node": ">=6.0"
|
||||||
@ -7253,7 +7227,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/enhanced-resolve": {
|
"node_modules/enhanced-resolve": {
|
||||||
"version": "5.18.1",
|
"version": "5.18.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.4",
|
"graceful-fs": "^4.2.4",
|
||||||
@ -7386,7 +7359,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/es-module-lexer": {
|
"node_modules/es-module-lexer": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/es-object-atoms": {
|
"node_modules/es-object-atoms": {
|
||||||
@ -7949,7 +7921,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/esrecurse": {
|
"node_modules/esrecurse": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"estraverse": "^5.2.0"
|
"estraverse": "^5.2.0"
|
||||||
@ -7960,7 +7931,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/estraverse": {
|
"node_modules/estraverse": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
@ -7992,7 +7962,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/events": {
|
"node_modules/events": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.x"
|
"node": ">=0.8.x"
|
||||||
@ -8048,7 +8017,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-equals": {
|
"node_modules/fast-equals": {
|
||||||
@ -8094,7 +8062,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/fast-uri": {
|
"node_modules/fast-uri": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -8560,7 +8527,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/glob-to-regexp": {
|
"node_modules/glob-to-regexp": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/glob/node_modules/brace-expansion": {
|
"node_modules/glob/node_modules/brace-expansion": {
|
||||||
@ -8617,7 +8583,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/graceful-fs": {
|
"node_modules/graceful-fs": {
|
||||||
"version": "4.2.11",
|
"version": "4.2.11",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/graphemer": {
|
"node_modules/graphemer": {
|
||||||
@ -10752,7 +10717,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/loader-runner": {
|
"node_modules/loader-runner": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.11.5"
|
"node": ">=6.11.5"
|
||||||
@ -11198,7 +11162,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
@ -11755,7 +11718,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/mime-db": {
|
"node_modules/mime-db": {
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
@ -11763,7 +11725,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/mime-types": {
|
"node_modules/mime-types": {
|
||||||
"version": "2.1.35",
|
"version": "2.1.35",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-db": "1.52.0"
|
"mime-db": "1.52.0"
|
||||||
@ -11846,7 +11807,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/neo-async": {
|
"node_modules/neo-async": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/next": {
|
"node_modules/next": {
|
||||||
@ -15033,7 +14993,7 @@
|
|||||||
"version": "1.51.1",
|
"version": "1.51.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz",
|
||||||
"integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==",
|
"integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.51.1"
|
"playwright-core": "1.51.1"
|
||||||
@ -15052,7 +15012,7 @@
|
|||||||
"version": "1.51.1",
|
"version": "1.51.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz",
|
||||||
"integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==",
|
"integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright-core": "cli.js"
|
"playwright-core": "cli.js"
|
||||||
@ -15531,7 +15491,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/randombytes": {
|
"node_modules/randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "^5.1.0"
|
"safe-buffer": "^5.1.0"
|
||||||
@ -16123,7 +16082,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/require-from-string": {
|
"node_modules/require-from-string": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@ -16294,7 +16252,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -16383,7 +16340,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/serialize-javascript": {
|
"node_modules/serialize-javascript": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"randombytes": "^2.1.0"
|
"randombytes": "^2.1.0"
|
||||||
@ -16591,7 +16547,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@ -17205,7 +17160,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/tapable": {
|
"node_modules/tapable": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
@ -17213,7 +17167,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.39.0",
|
"version": "5.39.0",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
@ -17230,7 +17183,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser-webpack-plugin": {
|
"node_modules/terser-webpack-plugin": {
|
||||||
"version": "5.3.14",
|
"version": "5.3.14",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/trace-mapping": "^0.3.25",
|
"@jridgewell/trace-mapping": "^0.3.25",
|
||||||
@ -17263,7 +17215,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/ajv": {
|
"node_modules/terser-webpack-plugin/node_modules/ajv": {
|
||||||
"version": "8.17.1",
|
"version": "8.17.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
@ -17278,7 +17229,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
|
"node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3"
|
"fast-deep-equal": "^3.1.3"
|
||||||
@ -17289,7 +17239,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/jest-worker": {
|
"node_modules/terser-webpack-plugin/node_modules/jest-worker": {
|
||||||
"version": "27.5.1",
|
"version": "27.5.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@ -17302,12 +17251,10 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
|
"node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
|
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/json-schema": "^7.0.9",
|
"@types/json-schema": "^7.0.9",
|
||||||
@ -17325,7 +17272,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/supports-color": {
|
"node_modules/terser-webpack-plugin/node_modules/supports-color": {
|
||||||
"version": "8.1.1",
|
"version": "8.1.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-flag": "^4.0.0"
|
"has-flag": "^4.0.0"
|
||||||
@ -17339,12 +17285,10 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser/node_modules/commander": {
|
"node_modules/terser/node_modules/commander": {
|
||||||
"version": "2.20.3",
|
"version": "2.20.3",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/terser/node_modules/source-map-support": {
|
"node_modules/terser/node_modules/source-map-support": {
|
||||||
"version": "0.5.21",
|
"version": "0.5.21",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer-from": "^1.0.0",
|
"buffer-from": "^1.0.0",
|
||||||
@ -18144,7 +18088,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/watchpack": {
|
"node_modules/watchpack": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob-to-regexp": "^0.4.1",
|
"glob-to-regexp": "^0.4.1",
|
||||||
@ -18172,7 +18115,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/webpack": {
|
"node_modules/webpack": {
|
||||||
"version": "5.98.0",
|
"version": "5.98.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/eslint-scope": "^3.7.7",
|
"@types/eslint-scope": "^3.7.7",
|
||||||
@ -18315,7 +18257,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/webpack/node_modules/ajv": {
|
"node_modules/webpack/node_modules/ajv": {
|
||||||
"version": "8.17.1",
|
"version": "8.17.1",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
@ -18330,7 +18271,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/webpack/node_modules/ajv-keywords": {
|
"node_modules/webpack/node_modules/ajv-keywords": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3"
|
"fast-deep-equal": "^3.1.3"
|
||||||
@ -18341,7 +18281,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/webpack/node_modules/eslint-scope": {
|
"node_modules/webpack/node_modules/eslint-scope": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esrecurse": "^4.3.0",
|
"esrecurse": "^4.3.0",
|
||||||
@ -18353,7 +18292,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/webpack/node_modules/estraverse": {
|
"node_modules/webpack/node_modules/estraverse": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
@ -18361,12 +18299,10 @@
|
|||||||
},
|
},
|
||||||
"node_modules/webpack/node_modules/json-schema-traverse": {
|
"node_modules/webpack/node_modules/json-schema-traverse": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/webpack/node_modules/schema-utils": {
|
"node_modules/webpack/node_modules/schema-utils": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/json-schema": "^7.0.9",
|
"@types/json-schema": "^7.0.9",
|
||||||
|
@ -25,6 +25,7 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { useAuthType } from "@/lib/hooks";
|
import { useAuthType } from "@/lib/hooks";
|
||||||
|
import { InfoIcon } from "lucide-react";
|
||||||
|
|
||||||
function parseJsonWithTrailingCommas(jsonString: string) {
|
function parseJsonWithTrailingCommas(jsonString: string) {
|
||||||
// Regular expression to remove trailing commas before } or ]
|
// Regular expression to remove trailing commas before } or ]
|
||||||
@ -159,25 +160,14 @@ function ActionForm({
|
|||||||
component="div"
|
component="div"
|
||||||
className="mb-4 text-error text-sm"
|
className="mb-4 text-error text-sm"
|
||||||
/>
|
/>
|
||||||
<div className="mt-4 text-sm bg-blue-50 p-4 rounded-md border border-blue-200">
|
<div className="mt-4 text-sm bg-blue-50 text-blue-700 dark:text-blue-300 dark:bg-blue-900 p-4 rounded-md border border-blue-200 dark:border-blue-800">
|
||||||
<Link
|
<Link
|
||||||
href="https://docs.onyx.app/tools/custom"
|
href="https://docs.onyx.app/tools/custom"
|
||||||
className="text-link hover:underline flex items-center"
|
className="text-link hover:underline flex items-center"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<svg
|
<InfoIcon className="w-4 h-4 mr-2 " />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
className="h-5 w-5 mr-2"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
|
||||||
clipRule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Learn more about actions in our documentation
|
Learn more about actions in our documentation
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@ -367,7 +357,7 @@ interface ToolFormValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ToolSchema = Yup.object().shape({
|
const ToolSchema = Yup.object().shape({
|
||||||
definition: Yup.string().required("Tool definition is required"),
|
definition: Yup.string().required("Action definition is required"),
|
||||||
customHeaders: Yup.array()
|
customHeaders: Yup.array()
|
||||||
.of(
|
.of(
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
|
@ -27,15 +27,17 @@ export default async function Page(props: {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
body = (
|
body = (
|
||||||
<div className="w-full my-8">
|
<div className="w-full mt-8 pb-8">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<CardSection>
|
<CardSection>
|
||||||
<ActionEditor tool={tool} />
|
<ActionEditor tool={tool} />
|
||||||
</CardSection>
|
</CardSection>
|
||||||
|
|
||||||
<Title className="mt-12">Delete Tool</Title>
|
<Title className="mt-12">Delete Action</Title>
|
||||||
<Text>Click the button below to permanently delete this tool.</Text>
|
<Text>
|
||||||
|
Click the button below to permanently delete this action.
|
||||||
|
</Text>
|
||||||
<div className="flex mt-6">
|
<div className="flex mt-6">
|
||||||
<DeleteToolButton toolId={tool.id} />
|
<DeleteToolButton toolId={tool.id} />
|
||||||
</div>
|
</div>
|
||||||
@ -50,7 +52,7 @@ export default async function Page(props: {
|
|||||||
<BackButton />
|
<BackButton />
|
||||||
|
|
||||||
<AdminPageTitle
|
<AdminPageTitle
|
||||||
title="Edit Tool"
|
title="Edit Action"
|
||||||
icon={<ToolIcon size={32} className="my-auto" />}
|
icon={<ToolIcon size={32} className="my-auto" />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -41,11 +41,8 @@ function NewApiKeyModal({
|
|||||||
const [copyClicked, setCopyClicked] = useState(false);
|
const [copyClicked, setCopyClicked] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal onOutsideClick={onClose}>
|
<Modal title="New API Key" onOutsideClick={onClose}>
|
||||||
<div className="px-8 py-8">
|
<div className="px-8 py-8">
|
||||||
<div className="flex w-full border-b border-border mb-4 pb-4">
|
|
||||||
<Title>New API Key</Title>
|
|
||||||
</div>
|
|
||||||
<div className="h-32">
|
<div className="h-32">
|
||||||
<Text className="mb-4">
|
<Text className="mb-4">
|
||||||
Make sure you copy your new API key. You won’t be able to see this
|
Make sure you copy your new API key. You won’t be able to see this
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@ import {
|
|||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { SlackBot } from "@/lib/types";
|
import { SlackBot } from "@/lib/types";
|
||||||
|
import { EditIcon } from "@/components/icons/icons";
|
||||||
|
|
||||||
const NUM_IN_PAGE = 20;
|
const NUM_IN_PAGE = 20;
|
||||||
|
|
||||||
@ -83,7 +84,7 @@ export const SlackBotTable = ({ slackBots }: { slackBots: SlackBot[] }) => {
|
|||||||
>
|
>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<FiEdit className="mr-4" />
|
<EditIcon className="mr-4" />
|
||||||
{slackBot.name}
|
{slackBot.name}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -424,7 +424,6 @@ export function LLMProviderUpdateForm({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<IsPublicGroupSelector
|
<IsPublicGroupSelector
|
||||||
formikProps={formikProps}
|
formikProps={formikProps}
|
||||||
objectName="LLM Provider"
|
objectName="LLM Provider"
|
||||||
|
@ -168,7 +168,7 @@ export function Explorer({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{popup}
|
{popup}
|
||||||
<div className="justify-center py-2">
|
<div className="justify-center pt-2">
|
||||||
<div className="flex items-center w-full border-2 border-border rounded-lg px-4 py-2 focus-within:border-accent bg-background-search dark:bg-transparent">
|
<div className="flex items-center w-full border-2 border-border rounded-lg px-4 py-2 focus-within:border-accent bg-background-search dark:bg-transparent">
|
||||||
<MagnifyingGlass />
|
<MagnifyingGlass />
|
||||||
<textarea
|
<textarea
|
||||||
|
@ -17,6 +17,7 @@ import { getErrorMsg } from "@/lib/fetchUtils";
|
|||||||
import { HoverPopup } from "@/components/HoverPopup";
|
import { HoverPopup } from "@/components/HoverPopup";
|
||||||
import { CustomCheckbox } from "@/components/CustomCheckbox";
|
import { CustomCheckbox } from "@/components/CustomCheckbox";
|
||||||
import { ScoreSection } from "../ScoreEditor";
|
import { ScoreSection } from "../ScoreEditor";
|
||||||
|
import { truncateString } from "@/lib/utils";
|
||||||
|
|
||||||
const IsVisibleSection = ({
|
const IsVisibleSection = ({
|
||||||
document,
|
document,
|
||||||
@ -109,12 +110,12 @@ export const DocumentFeedbackTable = ({
|
|||||||
<TableRow key={document.document_id}>
|
<TableRow key={document.document_id}>
|
||||||
<TableCell className="whitespace-normal break-all">
|
<TableCell className="whitespace-normal break-all">
|
||||||
<a
|
<a
|
||||||
className="text-blue-600"
|
className="text-blue-600 dark:text-blue-300"
|
||||||
href={document.link}
|
href={document.link}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
{document.semantic_id}
|
{truncateString(document.semantic_id, 100)}
|
||||||
</a>
|
</a>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
@ -144,6 +144,7 @@ function Main() {
|
|||||||
/>
|
/>
|
||||||
{isPaidEnterpriseFeaturesEnabled && (
|
{isPaidEnterpriseFeaturesEnabled && (
|
||||||
<Tabs
|
<Tabs
|
||||||
|
className="mt-2"
|
||||||
value={tabIndex.toString()}
|
value={tabIndex.toString()}
|
||||||
onValueChange={(val) => setTabIndex(parseInt(val))}
|
onValueChange={(val) => setTabIndex(parseInt(val))}
|
||||||
>
|
>
|
||||||
|
@ -14,7 +14,7 @@ const Page = () => {
|
|||||||
Authentication Error
|
Authentication Error
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-text-700 text-center">
|
<p className="text-text-700 text-center">
|
||||||
We encountered an issue while attempting to log you in.
|
There was a problem with your login attempt.
|
||||||
</p>
|
</p>
|
||||||
<div className="bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-800 rounded-lg p-4 shadow-sm">
|
<div className="bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-800 rounded-lg p-4 shadow-sm">
|
||||||
<h3 className="text-red-800 dark:text-red-400 font-semibold mb-2">
|
<h3 className="text-red-800 dark:text-red-400 font-semibold mb-2">
|
||||||
@ -46,8 +46,11 @@ const Page = () => {
|
|||||||
please reach out to your system administrator for assistance.
|
please reach out to your system administrator for assistance.
|
||||||
{NEXT_PUBLIC_CLOUD_ENABLED && (
|
{NEXT_PUBLIC_CLOUD_ENABLED && (
|
||||||
<span className="block mt-1 text-blue-600">
|
<span className="block mt-1 text-blue-600">
|
||||||
A member of our team has been automatically notified about this
|
If you continue to experience problems please reach out to the
|
||||||
issue.
|
Onyx team at{" "}
|
||||||
|
<a href="mailto:support@onyx.app" className="text-blue-600">
|
||||||
|
support@onyx.app
|
||||||
|
</a>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
@ -141,9 +141,6 @@ export function EmailPasswordForm({
|
|||||||
name="password"
|
name="password"
|
||||||
label="Password"
|
label="Password"
|
||||||
type="password"
|
type="password"
|
||||||
includeForgotPassword={
|
|
||||||
NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED && !isSignup
|
|
||||||
}
|
|
||||||
placeholder="**************"
|
placeholder="**************"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -29,38 +29,52 @@ export default function LoginPage({
|
|||||||
useSendAuthRequiredMessage();
|
useSendAuthRequiredMessage();
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-full justify-center">
|
<div className="flex flex-col w-full justify-center">
|
||||||
{authUrl && authTypeMetadata && (
|
{authUrl &&
|
||||||
<>
|
authTypeMetadata &&
|
||||||
|
authTypeMetadata.authType !== "cloud" &&
|
||||||
|
// basic auth is handled below w/ the EmailPasswordForm
|
||||||
|
authTypeMetadata.authType !== "basic" && (
|
||||||
|
<>
|
||||||
|
<h2 className="text-center text-xl text-strong font-bold">
|
||||||
|
<LoginText />
|
||||||
|
</h2>
|
||||||
|
<SignInButton
|
||||||
|
authorizeUrl={authUrl}
|
||||||
|
authType={authTypeMetadata?.authType}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{authTypeMetadata?.authType === "cloud" && (
|
||||||
|
<div className="w-full justify-center">
|
||||||
<h2 className="text-center text-xl text-strong font-bold">
|
<h2 className="text-center text-xl text-strong font-bold">
|
||||||
<LoginText />
|
<LoginText />
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<SignInButton
|
|
||||||
authorizeUrl={authUrl}
|
|
||||||
authType={authTypeMetadata?.authType}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{authTypeMetadata?.authType === "cloud" && (
|
|
||||||
<div className="mt-4 w-full justify-center">
|
|
||||||
<div className="flex items-center w-full my-4">
|
|
||||||
<div className="flex-grow border-t border-background-300"></div>
|
|
||||||
<span className="px-4 text-text-500">or</span>
|
|
||||||
<div className="flex-grow border-t border-background-300"></div>
|
|
||||||
</div>
|
|
||||||
<EmailPasswordForm shouldVerify={true} nextUrl={nextUrl} />
|
<EmailPasswordForm shouldVerify={true} nextUrl={nextUrl} />
|
||||||
|
|
||||||
{NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED && (
|
{NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED && (
|
||||||
<div className="flex mt-4 justify-between">
|
<div className="flex mt-4 justify-between">
|
||||||
<Link
|
<Link
|
||||||
href="/auth/forgot-password"
|
href="/auth/forgot-password"
|
||||||
className="text-link font-medium"
|
className="ml-auto text-link font-medium"
|
||||||
>
|
>
|
||||||
Reset Password
|
Reset Password
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{authUrl && authTypeMetadata && (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center w-full my-4">
|
||||||
|
<div className="flex-grow border-t border-background-300"></div>
|
||||||
|
<span className="px-4 text-text-500">or</span>
|
||||||
|
<div className="flex-grow border-t border-background-300"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SignInButton
|
||||||
|
authorizeUrl={authUrl}
|
||||||
|
authType={authTypeMetadata?.authType}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -40,10 +40,19 @@ const Page = async (props: {
|
|||||||
|
|
||||||
// if user is already logged in, take them to the main app page
|
// if user is already logged in, take them to the main app page
|
||||||
if (currentUser && currentUser.is_active && !currentUser.is_anonymous_user) {
|
if (currentUser && currentUser.is_active && !currentUser.is_anonymous_user) {
|
||||||
|
console.log("Login page: User is logged in, redirecting to chat", {
|
||||||
|
userId: currentUser.id,
|
||||||
|
is_active: currentUser.is_active,
|
||||||
|
is_anonymous: currentUser.is_anonymous_user,
|
||||||
|
});
|
||||||
|
|
||||||
if (authTypeMetadata?.requiresVerification && !currentUser.is_verified) {
|
if (authTypeMetadata?.requiresVerification && !currentUser.is_verified) {
|
||||||
return redirect("/auth/waiting-on-verification");
|
return redirect("/auth/waiting-on-verification");
|
||||||
}
|
}
|
||||||
return redirect("/chat");
|
|
||||||
|
// Add a query parameter to indicate this is a redirect from login
|
||||||
|
// This will help prevent redirect loops
|
||||||
|
return redirect("/chat?from=login");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get where to send the user to authenticate
|
// get where to send the user to authenticate
|
||||||
|
@ -82,23 +82,22 @@ const Page = async (props: {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{cloud && authUrl && (
|
|
||||||
<div className="w-full justify-center">
|
|
||||||
<SignInButton authorizeUrl={authUrl} authType="cloud" />
|
|
||||||
<div className="flex items-center w-full my-4">
|
|
||||||
<div className="flex-grow border-t border-background-300"></div>
|
|
||||||
<span className="px-4 text-text-500">or</span>
|
|
||||||
<div className="flex-grow border-t border-background-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<EmailPasswordForm
|
<EmailPasswordForm
|
||||||
isSignup
|
isSignup
|
||||||
shouldVerify={authTypeMetadata?.requiresVerification}
|
shouldVerify={authTypeMetadata?.requiresVerification}
|
||||||
nextUrl={nextUrl}
|
nextUrl={nextUrl}
|
||||||
defaultEmail={defaultEmail}
|
defaultEmail={defaultEmail}
|
||||||
/>
|
/>
|
||||||
|
{cloud && authUrl && (
|
||||||
|
<div className="w-full justify-center">
|
||||||
|
<div className="flex items-center w-full my-4">
|
||||||
|
<div className="flex-grow border-t border-background-300"></div>
|
||||||
|
<span className="px-4 text-text-500">or</span>
|
||||||
|
<div className="flex-grow border-t border-background-300"></div>
|
||||||
|
</div>
|
||||||
|
<SignInButton authorizeUrl={authUrl} authType="cloud" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
</AuthFlowContainer>
|
</AuthFlowContainer>
|
||||||
|
@ -104,9 +104,7 @@ import {
|
|||||||
SIDEBAR_TOGGLED_COOKIE_NAME,
|
SIDEBAR_TOGGLED_COOKIE_NAME,
|
||||||
} from "@/components/resizable/constants";
|
} from "@/components/resizable/constants";
|
||||||
import FixedLogo from "@/components/logo/FixedLogo";
|
import FixedLogo from "@/components/logo/FixedLogo";
|
||||||
|
|
||||||
import ExceptionTraceModal from "@/components/modals/ExceptionTraceModal";
|
import ExceptionTraceModal from "@/components/modals/ExceptionTraceModal";
|
||||||
|
|
||||||
import { SEARCH_TOOL_ID, SEARCH_TOOL_NAME } from "./tools/constants";
|
import { SEARCH_TOOL_ID, SEARCH_TOOL_NAME } from "./tools/constants";
|
||||||
import { useUser } from "@/components/user/UserProvider";
|
import { useUser } from "@/components/user/UserProvider";
|
||||||
import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
|
import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
|
||||||
@ -178,12 +176,10 @@ export function ChatPage({
|
|||||||
selectedFolders,
|
selectedFolders,
|
||||||
addSelectedFile,
|
addSelectedFile,
|
||||||
addSelectedFolder,
|
addSelectedFolder,
|
||||||
removeSelectedFolder,
|
|
||||||
clearSelectedItems,
|
clearSelectedItems,
|
||||||
folders: userFolders,
|
folders: userFolders,
|
||||||
files: allUserFiles,
|
files: allUserFiles,
|
||||||
uploadFile,
|
uploadFile,
|
||||||
removeSelectedFile,
|
|
||||||
currentMessageFiles,
|
currentMessageFiles,
|
||||||
setCurrentMessageFiles,
|
setCurrentMessageFiles,
|
||||||
} = useDocumentsContext();
|
} = useDocumentsContext();
|
||||||
@ -224,7 +220,6 @@ export function ChatPage({
|
|||||||
const settings = useContext(SettingsContext);
|
const settings = useContext(SettingsContext);
|
||||||
const enterpriseSettings = settings?.enterpriseSettings;
|
const enterpriseSettings = settings?.enterpriseSettings;
|
||||||
|
|
||||||
const [viewingFilePicker, setViewingFilePicker] = useState(false);
|
|
||||||
const [toggleDocSelection, setToggleDocSelection] = useState(false);
|
const [toggleDocSelection, setToggleDocSelection] = useState(false);
|
||||||
const [documentSidebarVisible, setDocumentSidebarVisible] = useState(false);
|
const [documentSidebarVisible, setDocumentSidebarVisible] = useState(false);
|
||||||
const [proSearchEnabled, setProSearchEnabled] = useState(proSearchToggled);
|
const [proSearchEnabled, setProSearchEnabled] = useState(proSearchToggled);
|
||||||
@ -706,6 +701,7 @@ export function ChatPage({
|
|||||||
chatSessionId || currentSessionId(),
|
chatSessionId || currentSessionId(),
|
||||||
newCompleteMessageMap
|
newCompleteMessageMap
|
||||||
);
|
);
|
||||||
|
console.log(newCompleteMessageDetail);
|
||||||
return newCompleteMessageDetail;
|
return newCompleteMessageDetail;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1171,6 +1167,13 @@ export function ChatPage({
|
|||||||
};
|
};
|
||||||
}, [autoScrollEnabled, screenHeight, currentSessionHasSentLocalUserMessage]);
|
}, [autoScrollEnabled, screenHeight, currentSessionHasSentLocalUserMessage]);
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
setMessage("");
|
||||||
|
setCurrentMessageFiles([]);
|
||||||
|
clearSelectedItems();
|
||||||
|
setLoadingError(null);
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = async ({
|
const onSubmit = async ({
|
||||||
messageIdToResend,
|
messageIdToResend,
|
||||||
messageOverride,
|
messageOverride,
|
||||||
@ -1201,6 +1204,61 @@ export function ChatPage({
|
|||||||
// Mark that we've sent a message for this session in the current page load
|
// Mark that we've sent a message for this session in the current page load
|
||||||
markSessionMessageSent(frozenSessionId);
|
markSessionMessageSent(frozenSessionId);
|
||||||
|
|
||||||
|
// Check if the last message was an error and remove it before proceeding with a new message
|
||||||
|
// Ensure this isn't a regeneration or resend, as those operations should preserve the history leading up to the point of regeneration/resend.
|
||||||
|
let currentMap = currentMessageMap(completeMessageDetail);
|
||||||
|
let currentHistory = buildLatestMessageChain(currentMap);
|
||||||
|
let lastMessage = currentHistory[currentHistory.length - 1];
|
||||||
|
|
||||||
|
if (
|
||||||
|
lastMessage &&
|
||||||
|
lastMessage.type === "error" &&
|
||||||
|
!messageIdToResend &&
|
||||||
|
!regenerationRequest
|
||||||
|
) {
|
||||||
|
const newMap = new Map(currentMap);
|
||||||
|
const parentId = lastMessage.parentMessageId;
|
||||||
|
|
||||||
|
// Remove the error message itself
|
||||||
|
newMap.delete(lastMessage.messageId);
|
||||||
|
|
||||||
|
// Remove the parent message + update the parent of the parent to no longer
|
||||||
|
// link to the parent
|
||||||
|
if (parentId !== null && parentId !== undefined) {
|
||||||
|
const parentOfError = newMap.get(parentId);
|
||||||
|
if (parentOfError) {
|
||||||
|
const grandparentId = parentOfError.parentMessageId;
|
||||||
|
if (grandparentId !== null && grandparentId !== undefined) {
|
||||||
|
const grandparent = newMap.get(grandparentId);
|
||||||
|
if (grandparent) {
|
||||||
|
// Update grandparent to no longer link to parent
|
||||||
|
const updatedGrandparent = {
|
||||||
|
...grandparent,
|
||||||
|
childrenMessageIds: (
|
||||||
|
grandparent.childrenMessageIds || []
|
||||||
|
).filter((id) => id !== parentId),
|
||||||
|
latestChildMessageId:
|
||||||
|
grandparent.latestChildMessageId === parentId
|
||||||
|
? null
|
||||||
|
: grandparent.latestChildMessageId,
|
||||||
|
};
|
||||||
|
newMap.set(grandparentId, updatedGrandparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove the parent message
|
||||||
|
newMap.delete(parentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update the state immediately so subsequent logic uses the cleaned map
|
||||||
|
updateCompleteMessageDetail(frozenSessionId, newMap);
|
||||||
|
console.log("Removed previous error message ID:", lastMessage.messageId);
|
||||||
|
|
||||||
|
// update state for the new world (with the error message removed)
|
||||||
|
currentHistory = buildLatestMessageChain(newMap);
|
||||||
|
currentMap = newMap;
|
||||||
|
lastMessage = currentHistory[currentHistory.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
if (currentChatState() != "input") {
|
if (currentChatState() != "input") {
|
||||||
if (currentChatState() == "uploading") {
|
if (currentChatState() == "uploading") {
|
||||||
setPopup({
|
setPopup({
|
||||||
@ -1270,11 +1328,10 @@ export function ChatPage({
|
|||||||
currentSessionId()
|
currentSessionId()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const messageMap = currentMessageMap(completeMessageDetail);
|
|
||||||
const messageToResendParent =
|
const messageToResendParent =
|
||||||
messageToResend?.parentMessageId !== null &&
|
messageToResend?.parentMessageId !== null &&
|
||||||
messageToResend?.parentMessageId !== undefined
|
messageToResend?.parentMessageId !== undefined
|
||||||
? messageMap.get(messageToResend.parentMessageId)
|
? currentMap.get(messageToResend.parentMessageId)
|
||||||
: null;
|
: null;
|
||||||
const messageToResendIndex = messageToResend
|
const messageToResendIndex = messageToResend
|
||||||
? messageHistory.indexOf(messageToResend)
|
? messageHistory.indexOf(messageToResend)
|
||||||
@ -1301,15 +1358,15 @@ export function ChatPage({
|
|||||||
|
|
||||||
const currMessageHistory =
|
const currMessageHistory =
|
||||||
messageToResendIndex !== null
|
messageToResendIndex !== null
|
||||||
? messageHistory.slice(0, messageToResendIndex)
|
? currentHistory.slice(0, messageToResendIndex)
|
||||||
: messageHistory;
|
: currentHistory;
|
||||||
|
|
||||||
let parentMessage =
|
let parentMessage =
|
||||||
messageToResendParent ||
|
messageToResendParent ||
|
||||||
(currMessageHistory.length > 0
|
(currMessageHistory.length > 0
|
||||||
? currMessageHistory[currMessageHistory.length - 1]
|
? currMessageHistory[currMessageHistory.length - 1]
|
||||||
: null) ||
|
: null) ||
|
||||||
(messageMap.size === 1 ? Array.from(messageMap.values())[0] : null);
|
(currentMap.size === 1 ? Array.from(currentMap.values())[0] : null);
|
||||||
|
|
||||||
let currentAssistantId;
|
let currentAssistantId;
|
||||||
if (alternativeAssistantOverride) {
|
if (alternativeAssistantOverride) {
|
||||||
@ -1356,13 +1413,9 @@ export function ChatPage({
|
|||||||
frozenMessageMap: Map<number, Message>;
|
frozenMessageMap: Map<number, Message>;
|
||||||
} = null;
|
} = null;
|
||||||
try {
|
try {
|
||||||
const mapKeys = Array.from(
|
const mapKeys = Array.from(currentMap.keys());
|
||||||
currentMessageMap(completeMessageDetail).keys()
|
|
||||||
);
|
|
||||||
const systemMessage = Math.min(...mapKeys);
|
|
||||||
|
|
||||||
const lastSuccessfulMessageId =
|
const lastSuccessfulMessageId =
|
||||||
getLastSuccessfulMessageId(currMessageHistory) || systemMessage;
|
getLastSuccessfulMessageId(currMessageHistory);
|
||||||
|
|
||||||
const stack = new CurrentMessageFIFO();
|
const stack = new CurrentMessageFIFO();
|
||||||
|
|
||||||
@ -1479,11 +1532,12 @@ export function ChatPage({
|
|||||||
upsertToCompleteMessageMap({
|
upsertToCompleteMessageMap({
|
||||||
messages: messageUpdates,
|
messages: messageUpdates,
|
||||||
chatSessionId: currChatSessionId,
|
chatSessionId: currChatSessionId,
|
||||||
|
completeMessageMapOverride: currentMap,
|
||||||
});
|
});
|
||||||
|
currentMap = currentFrozenMessageMap;
|
||||||
|
|
||||||
const frozenMessageMap = currentFrozenMessageMap;
|
|
||||||
initialFetchDetails = {
|
initialFetchDetails = {
|
||||||
frozenMessageMap,
|
frozenMessageMap: currentMap,
|
||||||
assistant_message_id,
|
assistant_message_id,
|
||||||
user_message_id,
|
user_message_id,
|
||||||
};
|
};
|
||||||
@ -1715,14 +1769,18 @@ export function ChatPage({
|
|||||||
] as [number, number][])
|
] as [number, number][])
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return upsertToCompleteMessageMap({
|
const newMessageDetails = upsertToCompleteMessageMap({
|
||||||
messages: messages,
|
messages: messages,
|
||||||
replacementsMap: replacementsMap,
|
replacementsMap: replacementsMap,
|
||||||
completeMessageMapOverride: frozenMessageMap,
|
// Pass the latest map state
|
||||||
|
completeMessageMapOverride: currentMap,
|
||||||
chatSessionId: frozenSessionId!,
|
chatSessionId: frozenSessionId!,
|
||||||
});
|
});
|
||||||
|
currentMap = newMessageDetails.messageMap;
|
||||||
|
return newMessageDetails;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const systemMessageId = Math.min(...mapKeys);
|
||||||
updateFn([
|
updateFn([
|
||||||
{
|
{
|
||||||
messageId: regenerationRequest
|
messageId: regenerationRequest
|
||||||
@ -1732,7 +1790,8 @@ export function ChatPage({
|
|||||||
type: "user",
|
type: "user",
|
||||||
files: files,
|
files: files,
|
||||||
toolCall: null,
|
toolCall: null,
|
||||||
parentMessageId: error ? null : lastSuccessfulMessageId,
|
// in the frontend, every message should have a parent ID
|
||||||
|
parentMessageId: lastSuccessfulMessageId ?? systemMessageId,
|
||||||
childrenMessageIds: [
|
childrenMessageIds: [
|
||||||
...(regenerationRequest?.parentMessage?.childrenMessageIds ||
|
...(regenerationRequest?.parentMessage?.childrenMessageIds ||
|
||||||
[]),
|
[]),
|
||||||
@ -1786,7 +1845,7 @@ export function ChatPage({
|
|||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log("Error:", e);
|
console.log("Error:", e);
|
||||||
const errorMsg = e.message;
|
const errorMsg = e.message;
|
||||||
upsertToCompleteMessageMap({
|
const newMessageDetails = upsertToCompleteMessageMap({
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
messageId:
|
messageId:
|
||||||
@ -1809,8 +1868,9 @@ export function ChatPage({
|
|||||||
initialFetchDetails?.user_message_id || TEMP_USER_MESSAGE_ID,
|
initialFetchDetails?.user_message_id || TEMP_USER_MESSAGE_ID,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
completeMessageMapOverride: currentMessageMap(completeMessageDetail),
|
completeMessageMapOverride: currentMap,
|
||||||
});
|
});
|
||||||
|
currentMap = newMessageDetails.messageMap;
|
||||||
}
|
}
|
||||||
console.log("Finished streaming");
|
console.log("Finished streaming");
|
||||||
setAgenticGenerating(false);
|
setAgenticGenerating(false);
|
||||||
@ -1909,15 +1969,21 @@ export function ChatPage({
|
|||||||
|
|
||||||
updateChatState("uploading", currentSessionId());
|
updateChatState("uploading", currentSessionId());
|
||||||
|
|
||||||
const [uploadedFiles, error] = await uploadFilesForChat(acceptedFiles);
|
for (let file of acceptedFiles) {
|
||||||
if (error) {
|
const formData = new FormData();
|
||||||
setPopup({
|
formData.append("files", file);
|
||||||
type: "error",
|
const response = await uploadFile(formData, null);
|
||||||
message: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurrentMessageFiles((prev) => [...prev, ...uploadedFiles]);
|
if (response.length > 0) {
|
||||||
|
const uploadedFile = response[0];
|
||||||
|
addSelectedFile(uploadedFile);
|
||||||
|
} else {
|
||||||
|
setPopup({
|
||||||
|
type: "error",
|
||||||
|
message: "Failed to upload file",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateChatState("input", currentSessionId());
|
updateChatState("input", currentSessionId());
|
||||||
};
|
};
|
||||||
@ -2396,7 +2462,7 @@ export function ChatPage({
|
|||||||
liveAssistant={liveAssistant}
|
liveAssistant={liveAssistant}
|
||||||
setShowAssistantsModal={setShowAssistantsModal}
|
setShowAssistantsModal={setShowAssistantsModal}
|
||||||
explicitlyUntoggle={explicitlyUntoggle}
|
explicitlyUntoggle={explicitlyUntoggle}
|
||||||
reset={() => setMessage("")}
|
reset={reset}
|
||||||
page="chat"
|
page="chat"
|
||||||
ref={innerSidebarElementRef}
|
ref={innerSidebarElementRef}
|
||||||
toggleSidebar={toggleSidebar}
|
toggleSidebar={toggleSidebar}
|
||||||
@ -3294,7 +3360,7 @@ export function ChatPage({
|
|||||||
: "w-[0px]"
|
: "w-[0px]"
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
></div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Dropzone>
|
</Dropzone>
|
||||||
|
@ -149,7 +149,7 @@ export const FolderDropdown = forwardRef<HTMLDivElement, FolderDropdownProps>(
|
|||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
style={style}
|
style={style}
|
||||||
{...attributes}
|
{...attributes}
|
||||||
className="overflow-visible mt-2 w-full"
|
className="overflow-visible pt-2 w-full"
|
||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
>
|
>
|
||||||
@ -159,13 +159,13 @@ export const FolderDropdown = forwardRef<HTMLDivElement, FolderDropdownProps>(
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="flex overflow-visible items-center w-full text-text-darker rounded-md p-1 relative sticky top-0"
|
className="flex overflow-visible items-center w-full text-text-darker rounded-md p-1 bg-background-sidebar dark:bg-[#000] relative sticky top-0"
|
||||||
style={{ zIndex: 10 - index }}
|
style={{ zIndex: 10 - index }}
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
className="flex overflow-hidden items-center flex-grow"
|
className="flex overflow-hidden bg-background-sidebar dark:bg-[#000] items-center flex-grow"
|
||||||
onClick={() => !isEditing && setIsOpen(!isOpen)}
|
onClick={() => !isEditing && setIsOpen(!isOpen)}
|
||||||
{...(isEditing ? {} : listeners)}
|
{...(isEditing ? {} : listeners)}
|
||||||
>
|
>
|
||||||
|
@ -402,17 +402,16 @@ export function ChatInputBar({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!showPrompts && !showSuggestions) {
|
if (!showPrompts && !showSuggestions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.key === "ArrowDown") {
|
if (e.key === "ArrowDown") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setTabbingIconIndex((tabbingIconIndex) =>
|
setTabbingIconIndex((tabbingIconIndex) =>
|
||||||
Math.min(
|
Math.min(
|
||||||
tabbingIconIndex + 1,
|
tabbingIconIndex + 1,
|
||||||
// showPrompts ? filteredPrompts.length :
|
showPrompts ? filteredPrompts.length : assistantTagOptions.length
|
||||||
assistantTagOptions.length
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else if (e.key === "ArrowUp") {
|
} else if (e.key === "ArrowUp") {
|
||||||
@ -440,13 +439,14 @@ export function ChatInputBar({
|
|||||||
ref={suggestionsRef}
|
ref={suggestionsRef}
|
||||||
className="text-sm absolute w-[calc(100%-2rem)] top-0 transform -translate-y-full"
|
className="text-sm absolute w-[calc(100%-2rem)] top-0 transform -translate-y-full"
|
||||||
>
|
>
|
||||||
<div className="rounded-lg py-1 sm-1.5 bg-input-background border border-border dark:border-none shadow-lg px-1.5 mt-2 z-10">
|
<div className="rounded-lg py-1 overflow-y-auto max-h-[200px] sm-1.5 bg-input-background border border-border dark:border-none shadow-lg px-1.5 mt-2 z-10">
|
||||||
{assistantTagOptions.map((currentAssistant, index) => (
|
{assistantTagOptions.map((currentAssistant, index) => (
|
||||||
<button
|
<button
|
||||||
key={index}
|
key={index}
|
||||||
className={`px-2 ${
|
className={`px-2 ${
|
||||||
tabbingIconIndex == index && "bg-neutral-200"
|
tabbingIconIndex == index &&
|
||||||
} rounded items-center rounded-lg content-start flex gap-x-1 py-2 w-full hover:bg-neutral-200/90 cursor-pointer`}
|
"bg-neutral-200 dark:bg-neutral-800"
|
||||||
|
} rounded items-center rounded-lg content-start flex gap-x-1 py-2 w-full hover:bg-neutral-200/90 dark:hover:bg-neutral-800/90 cursor-pointer`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updatedTaggedAssistant(currentAssistant);
|
updatedTaggedAssistant(currentAssistant);
|
||||||
}}
|
}}
|
||||||
@ -468,8 +468,8 @@ export function ChatInputBar({
|
|||||||
target="_self"
|
target="_self"
|
||||||
className={`${
|
className={`${
|
||||||
tabbingIconIndex == assistantTagOptions.length &&
|
tabbingIconIndex == assistantTagOptions.length &&
|
||||||
"bg-neutral-200"
|
"bg-neutral-200 dark:bg-neutral-800"
|
||||||
} rounded rounded-lg px-3 flex gap-x-1 py-2 w-full items-center hover:bg-neutral-200/90 cursor-pointer`}
|
} rounded rounded-lg px-3 flex gap-x-1 py-2 w-full items-center hover:bg-neutral-200/90 dark:hover:bg-neutral-800/90 cursor-pointer`}
|
||||||
href="/assistants/new"
|
href="/assistants/new"
|
||||||
>
|
>
|
||||||
<FiPlus size={17} />
|
<FiPlus size={17} />
|
||||||
@ -484,14 +484,15 @@ export function ChatInputBar({
|
|||||||
ref={suggestionsRef}
|
ref={suggestionsRef}
|
||||||
className="text-sm absolute inset-x-0 top-0 w-full transform -translate-y-full"
|
className="text-sm absolute inset-x-0 top-0 w-full transform -translate-y-full"
|
||||||
>
|
>
|
||||||
<div className="rounded-lg py-1.5 bg-input-background dark:border-none border border-border shadow-lg mx-2 px-1.5 mt-2 rounded z-10">
|
<div className="rounded-lg overflow-y-auto max-h-[200px] py-1.5 bg-input-background dark:border-none border border-border shadow-lg mx-2 px-1.5 mt-2 rounded z-10">
|
||||||
{filteredPrompts.map(
|
{filteredPrompts.map(
|
||||||
(currentPrompt: InputPrompt, index: number) => (
|
(currentPrompt: InputPrompt, index: number) => (
|
||||||
<button
|
<button
|
||||||
key={index}
|
key={index}
|
||||||
className={`px-2 ${
|
className={`px-2 ${
|
||||||
tabbingIconIndex == index && "bg-background-dark/75"
|
tabbingIconIndex == index &&
|
||||||
} rounded content-start flex gap-x-1 py-1.5 w-full hover:bg-background-dark/90 cursor-pointer`}
|
"bg-background-dark/75 dark:bg-neutral-800/75"
|
||||||
|
} rounded content-start flex gap-x-1 py-1.5 w-full hover:bg-background-dark/90 dark:hover:bg-neutral-800/90 cursor-pointer`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updateInputPrompt(currentPrompt);
|
updateInputPrompt(currentPrompt);
|
||||||
}}
|
}}
|
||||||
@ -509,8 +510,8 @@ export function ChatInputBar({
|
|||||||
target="_self"
|
target="_self"
|
||||||
className={`${
|
className={`${
|
||||||
tabbingIconIndex == filteredPrompts.length &&
|
tabbingIconIndex == filteredPrompts.length &&
|
||||||
"bg-background-dark/75"
|
"bg-background-dark/75 dark:bg-neutral-800/75"
|
||||||
} px-3 flex gap-x-1 py-2 w-full rounded-lg items-center hover:bg-background-dark/90 cursor-pointer`}
|
} px-3 flex gap-x-1 py-2 w-full rounded-lg items-center hover:bg-background-dark/90 dark:hover:bg-neutral-800/90 cursor-pointer`}
|
||||||
href="/chat/input-prompts"
|
href="/chat/input-prompts"
|
||||||
>
|
>
|
||||||
<FiPlus size={17} />
|
<FiPlus size={17} />
|
||||||
|
@ -18,6 +18,7 @@ export default async function Layout({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ("redirect" in data) {
|
if ("redirect" in data) {
|
||||||
|
console.log("redirect", data.redirect);
|
||||||
redirect(data.redirect);
|
redirect(data.redirect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,10 @@ export const MemoizedAnchor = memo(
|
|||||||
if (match) {
|
if (match) {
|
||||||
const isUserFileCitation = userFiles?.length && userFiles.length > 0;
|
const isUserFileCitation = userFiles?.length && userFiles.length > 0;
|
||||||
if (isUserFileCitation) {
|
if (isUserFileCitation) {
|
||||||
const index = parseInt(match[2], 10) - 1;
|
const index = Math.min(
|
||||||
|
parseInt(match[2], 10) - 1,
|
||||||
|
userFiles?.length - 1
|
||||||
|
);
|
||||||
const associatedUserFile = userFiles?.[index];
|
const associatedUserFile = userFiles?.[index];
|
||||||
if (!associatedUserFile) {
|
if (!associatedUserFile) {
|
||||||
return <a href={children as string}>{children}</a>;
|
return <a href={children as string}>{children}</a>;
|
||||||
|
@ -342,12 +342,7 @@ export const AIMessage = ({
|
|||||||
}
|
}
|
||||||
const processed = preprocessLaTeX(content);
|
const processed = preprocessLaTeX(content);
|
||||||
|
|
||||||
// Escape $ that are preceded by a space and followed by a non-$ character
|
return processed + (!isComplete && !toolCallGenerating ? " [*]() " : "");
|
||||||
const escapedDollarSigns = processed.replace(/([\s])\$([^\$])/g, "$1\\$$2");
|
|
||||||
|
|
||||||
return (
|
|
||||||
escapedDollarSigns + (!isComplete && !toolCallGenerating ? " [*]() " : "")
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const finalContentProcessed = processContent(finalContent as string);
|
const finalContentProcessed = processContent(finalContent as string);
|
||||||
|
@ -5,7 +5,7 @@ import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"
|
|||||||
|
|
||||||
import { destructureValue, structureValue } from "@/lib/llm/utils";
|
import { destructureValue, structureValue } from "@/lib/llm/utils";
|
||||||
import { setUserDefaultModel } from "@/lib/users/UserSettings";
|
import { setUserDefaultModel } from "@/lib/users/UserSettings";
|
||||||
import { useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
||||||
import { useUser } from "@/components/user/UserProvider";
|
import { useUser } from "@/components/user/UserProvider";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
@ -207,6 +207,8 @@ export function UserSettingsModal({
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
const showPasswordSection = user?.password_configured;
|
const showPasswordSection = user?.password_configured;
|
||||||
|
|
||||||
const handleDeleteAllChats = async () => {
|
const handleDeleteAllChats = async () => {
|
||||||
@ -219,7 +221,9 @@ export function UserSettingsModal({
|
|||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
refreshChatSessions();
|
refreshChatSessions();
|
||||||
router.push("/chat");
|
if (pathname.includes("/chat")) {
|
||||||
|
router.push("/chat");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Failed to delete all chat sessions");
|
throw new Error("Failed to delete all chat sessions");
|
||||||
}
|
}
|
||||||
@ -382,7 +386,7 @@ export function UserSettingsModal({
|
|||||||
<div className="pt-4 border-t border-border">
|
<div className="pt-4 border-t border-border">
|
||||||
{!showDeleteConfirmation ? (
|
{!showDeleteConfirmation ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<p className="text-sm text-neutral-600 ">
|
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||||
This will permanently delete all your chat sessions and
|
This will permanently delete all your chat sessions and
|
||||||
cannot be undone.
|
cannot be undone.
|
||||||
</p>
|
</p>
|
||||||
@ -397,7 +401,7 @@ export function UserSettingsModal({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<p className="text-sm text-neutral-600 ">
|
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||||
Are you sure you want to delete all your chat sessions?
|
Are you sure you want to delete all your chat sessions?
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
|
@ -160,6 +160,7 @@ export const DocumentsProvider: React.FC<DocumentsProviderProps> = ({
|
|||||||
|
|
||||||
const refreshFolders = async () => {
|
const refreshFolders = async () => {
|
||||||
try {
|
try {
|
||||||
|
console.log("fetching folders");
|
||||||
const data = await documentsService.fetchFolders();
|
const data = await documentsService.fetchFolders();
|
||||||
setFolders(data);
|
setFolders(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useMemo, useState, useTransition } from "react";
|
import React, { useEffect, useMemo, useState, useTransition } from "react";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
@ -68,11 +68,23 @@ export default function MyDocuments() {
|
|||||||
const [sortDirection, setSortDirection] = useState<SortDirection>(
|
const [sortDirection, setSortDirection] = useState<SortDirection>(
|
||||||
SortDirection.Descending
|
SortDirection.Descending
|
||||||
);
|
);
|
||||||
const pageLimit = 10;
|
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { popup, setPopup } = usePopup();
|
const { popup, setPopup } = usePopup();
|
||||||
const [isCreateFolderOpen, setIsCreateFolderOpen] = useState(false);
|
const [isCreateFolderOpen, setIsCreateFolderOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const createFolder = searchParams.get("createFolder");
|
||||||
|
if (createFolder) {
|
||||||
|
setIsCreateFolderOpen(true);
|
||||||
|
const newSearchParams = new URLSearchParams(searchParams);
|
||||||
|
newSearchParams.delete("createFolder");
|
||||||
|
router.replace(`?${newSearchParams.toString()}`);
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
const [hoveredColumn, setHoveredColumn] = useState<SortType | null>(null);
|
const [hoveredColumn, setHoveredColumn] = useState<SortType | null>(null);
|
||||||
|
|
||||||
@ -118,120 +130,24 @@ export default function MyDocuments() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteItem = async (itemId: number, isFolder: boolean) => {
|
const handleDeleteItem = async (itemId: number, isFolder: boolean) => {
|
||||||
if (!isFolder) {
|
|
||||||
// For files, keep the old confirmation
|
|
||||||
const confirmDelete = window.confirm(
|
|
||||||
`Are you sure you want to delete this file?`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (confirmDelete) {
|
|
||||||
try {
|
|
||||||
await deleteItem(itemId, isFolder);
|
|
||||||
setPopup({
|
|
||||||
message: `File deleted successfully`,
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
await refreshFolders();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error deleting item:", error);
|
|
||||||
setPopup({
|
|
||||||
message: `Failed to delete file`,
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's a folder, the SharedFolderItem component will handle it
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMoveItem = async (
|
|
||||||
itemId: number,
|
|
||||||
currentFolderId: number | null,
|
|
||||||
isFolder: boolean
|
|
||||||
) => {
|
|
||||||
const availableFolders = folders
|
|
||||||
.filter((folder) => folder.id !== itemId)
|
|
||||||
.map((folder) => `${folder.id}: ${folder.name}`)
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
const promptMessage = `Enter the ID of the destination folder:\n\nAvailable folders:\n${availableFolders}\n\nEnter 0 to move to the root folder.`;
|
|
||||||
const destinationFolderId = prompt(promptMessage);
|
|
||||||
|
|
||||||
if (destinationFolderId !== null) {
|
|
||||||
const newFolderId = parseInt(destinationFolderId, 10);
|
|
||||||
if (isNaN(newFolderId)) {
|
|
||||||
setPopup({
|
|
||||||
message: "Invalid folder ID",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await moveItem(
|
|
||||||
itemId,
|
|
||||||
newFolderId === 0 ? null : newFolderId,
|
|
||||||
isFolder
|
|
||||||
);
|
|
||||||
setPopup({
|
|
||||||
message: `${
|
|
||||||
isFolder ? "Knowledge Group" : "File"
|
|
||||||
} moved successfully`,
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
await refreshFolders();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error moving item:", error);
|
|
||||||
setPopup({
|
|
||||||
message: "Failed to move item",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDownloadItem = async (documentId: string) => {
|
|
||||||
try {
|
try {
|
||||||
await downloadItem(documentId);
|
await deleteItem(itemId, isFolder);
|
||||||
} catch (error) {
|
|
||||||
console.error("Error downloading file:", error);
|
|
||||||
setPopup({
|
setPopup({
|
||||||
message: "Failed to download file",
|
message: isFolder
|
||||||
|
? `Folder deleted successfully`
|
||||||
|
: `File deleted successfully`,
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
await refreshFolders();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting item:", error);
|
||||||
|
setPopup({
|
||||||
|
message: `Failed to delete ${isFolder ? "folder" : "file"}`,
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRenameItem = async (
|
|
||||||
itemId: number,
|
|
||||||
currentName: string,
|
|
||||||
isFolder: boolean
|
|
||||||
) => {
|
|
||||||
const newName = prompt(
|
|
||||||
`Enter new name for ${isFolder ? "Knowledge Group" : "File"}:`,
|
|
||||||
currentName
|
|
||||||
);
|
|
||||||
if (newName && newName !== currentName) {
|
|
||||||
try {
|
|
||||||
await renameItem(itemId, newName, isFolder);
|
|
||||||
setPopup({
|
|
||||||
message: `${
|
|
||||||
isFolder ? "Knowledge Group" : "File"
|
|
||||||
} renamed successfully`,
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
await refreshFolders();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error renaming item:", error);
|
|
||||||
setPopup({
|
|
||||||
message: `Failed to rename ${isFolder ? "Knowledge Group" : "File"}`,
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredFolders = useMemo(() => {
|
const filteredFolders = useMemo(() => {
|
||||||
return folders
|
return folders
|
||||||
.filter(
|
.filter(
|
||||||
@ -438,11 +354,7 @@ export default function MyDocuments() {
|
|||||||
onClick={handleFolderClick}
|
onClick={handleFolderClick}
|
||||||
description={folder.description}
|
description={folder.description}
|
||||||
lastUpdated={folder.created_at}
|
lastUpdated={folder.created_at}
|
||||||
onRename={() => onRenameItem(folder.id, folder.name, true)}
|
|
||||||
onDelete={() => handleDeleteItem(folder.id, true)}
|
onDelete={() => handleDeleteItem(folder.id, true)}
|
||||||
onMove={() =>
|
|
||||||
handleMoveItem(folder.id, currentFolder, true)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -588,29 +588,6 @@ export default function UserFolderContent({ folderId }: { folderId: number }) {
|
|||||||
|
|
||||||
{/* Invalid file message */}
|
{/* Invalid file message */}
|
||||||
|
|
||||||
{/* Add a visual overlay when dragging files */}
|
|
||||||
{isDraggingOver && (
|
|
||||||
<div className="fixed inset-0 bg-neutral-950/10 backdrop-blur-sm z-50 pointer-events-none flex items-center justify-center transition-all duration-200 ease-in-out">
|
|
||||||
<div className="bg-white dark:bg-neutral-900 rounded-lg p-8 shadow-lg text-center border border-neutral-200 dark:border-neutral-800 max-w-md mx-auto">
|
|
||||||
<div className="bg-neutral-100 dark:bg-neutral-800 p-4 rounded-full w-20 h-20 mx-auto mb-5 flex items-center justify-center">
|
|
||||||
<Upload
|
|
||||||
className="w-10 h-10 text-neutral-600 dark:text-neutral-300"
|
|
||||||
strokeWidth={1.5}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<h3 className="text-xl font-medium mb-2 text-neutral-900 dark:text-neutral-50">
|
|
||||||
Drop files to upload
|
|
||||||
</h3>
|
|
||||||
<p className="text-neutral-500 dark:text-neutral-400 text-sm">
|
|
||||||
Files will be uploaded to{" "}
|
|
||||||
<span className="font-medium text-neutral-900 dark:text-neutral-200">
|
|
||||||
{folderDetails?.name || "this folder"}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<DeleteEntityModal
|
<DeleteEntityModal
|
||||||
isOpen={isDeleteModalOpen}
|
isOpen={isDeleteModalOpen}
|
||||||
onClose={() => setIsDeleteModalOpen(false)}
|
onClose={() => setIsDeleteModalOpen(false)}
|
||||||
@ -634,9 +611,9 @@ export default function UserFolderContent({ folderId }: { folderId: number }) {
|
|||||||
|
|
||||||
<div className="flex -mt-[1px] flex-col w-full">
|
<div className="flex -mt-[1px] flex-col w-full">
|
||||||
<div className="flex items-center mb-3">
|
<div className="flex items-center mb-3">
|
||||||
<nav className="flex text-lg gap-x-1 items-center">
|
<nav className="flex text-base md:text-lg gap-x-1 items-center">
|
||||||
<span
|
<span
|
||||||
className="font-medium leading-tight tracking-tight text-lg text-neutral-800 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-neutral-100 cursor-pointer flex items-center text-base"
|
className="font-medium leading-tight tracking-tight text-neutral-800 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-neutral-100 cursor-pointer flex items-center"
|
||||||
onClick={handleBack}
|
onClick={handleBack}
|
||||||
>
|
>
|
||||||
My Documents
|
My Documents
|
||||||
|
@ -255,7 +255,7 @@ export const FileListItem: React.FC<FileListItemProps> = ({
|
|||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="group-hover:visible invisible h-8 w-8 p-0"
|
className="group-hover:visible mobile:visible invisible h-8 w-8 p-0"
|
||||||
>
|
>
|
||||||
<MoreHorizontal className="h-4 w-4" />
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -104,7 +104,7 @@ const DraggableItem: React.FC<{
|
|||||||
<div className="w-6 flex items-center justify-center shrink-0">
|
<div className="w-6 flex items-center justify-center shrink-0">
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
isSelected ? "" : "opacity-0 group-hover:opacity-100"
|
isSelected ? "" : "desktop:opacity-0 group-hover:opacity-100"
|
||||||
} transition-opacity duration-150`}
|
} transition-opacity duration-150`}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -199,7 +199,7 @@ const FilePickerFolderItem: React.FC<{
|
|||||||
className={`transition-opacity duration-150 ${
|
className={`transition-opacity duration-150 ${
|
||||||
isSelected || allFilesSelected
|
isSelected || allFilesSelected
|
||||||
? "opacity-100"
|
? "opacity-100"
|
||||||
: "opacity-0 group-hover:opacity-100"
|
: "desktop:opacity-0 group-hover:opacity-100"
|
||||||
}`}
|
}`}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -276,7 +276,10 @@ const FilePickerFolderItem: React.FC<{
|
|||||||
export interface FilePickerModalProps {
|
export interface FilePickerModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSave: () => void;
|
onSave: (
|
||||||
|
selectedFiles: FileResponse[],
|
||||||
|
selectedFolders: FolderResponse[]
|
||||||
|
) => void;
|
||||||
buttonContent: string;
|
buttonContent: string;
|
||||||
setPresentingDocument: (onyxDocument: MinimalOnyxDocument) => void;
|
setPresentingDocument: (onyxDocument: MinimalOnyxDocument) => void;
|
||||||
}
|
}
|
||||||
@ -323,8 +326,6 @@ export const FilePickerModal: React.FC<FilePickerModalProps> = ({
|
|||||||
createFileFromLink,
|
createFileFromLink,
|
||||||
} = useDocumentsContext();
|
} = useDocumentsContext();
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const [linkUrl, setLinkUrl] = useState("");
|
|
||||||
const [isCreatingFileFromLink, setIsCreatingFileFromLink] = useState(false);
|
const [isCreatingFileFromLink, setIsCreatingFileFromLink] = useState(false);
|
||||||
const [isUploadingFile, setIsUploadingFile] = useState(false);
|
const [isUploadingFile, setIsUploadingFile] = useState(false);
|
||||||
|
|
||||||
@ -395,12 +396,6 @@ export const FilePickerModal: React.FC<FilePickerModalProps> = ({
|
|||||||
}
|
}
|
||||||
}, [isOpen, selectedFiles, selectedFolders]);
|
}, [isOpen, selectedFiles, selectedFolders]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isOpen) {
|
|
||||||
refreshFolders();
|
|
||||||
}
|
|
||||||
}, [isOpen, refreshFolders]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentFolder) {
|
if (currentFolder) {
|
||||||
if (currentFolder === -1) {
|
if (currentFolder === -1) {
|
||||||
@ -1087,7 +1082,7 @@ export const FilePickerModal: React.FC<FilePickerModalProps> = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="h-[calc(70vh-5rem)] flex overflow-visible flex-col">
|
<div className="h-[calc(70vh-5rem)] flex overflow-visible flex-col">
|
||||||
<div className="grid overflow-x-visible h-full overflow-y-hidden flex-1 w-full divide-x divide-neutral-200 dark:divide-neutral-700 grid-cols-2">
|
<div className="grid overflow-x-visible h-full overflow-y-hidden flex-1 w-full divide-x divide-neutral-200 dark:divide-neutral-700 desktop:grid-cols-2">
|
||||||
<div className="w-full h-full pb-4 overflow-hidden ">
|
<div className="w-full h-full pb-4 overflow-hidden ">
|
||||||
<div className="px-6 sticky flex flex-col gap-y-2 z-[1000] top-0 mb-2 flex gap-x-2 w-full pr-4">
|
<div className="px-6 sticky flex flex-col gap-y-2 z-[1000] top-0 mb-2 flex gap-x-2 w-full pr-4">
|
||||||
<div className="w-full relative">
|
<div className="w-full relative">
|
||||||
@ -1251,16 +1246,16 @@ export const FilePickerModal: React.FC<FilePickerModalProps> = ({
|
|||||||
) : folders.length > 0 ? (
|
) : folders.length > 0 ? (
|
||||||
<div className="flex-grow overflow-y-auto px-4">
|
<div className="flex-grow overflow-y-auto px-4">
|
||||||
<p className="text-text-subtle dark:text-neutral-400">
|
<p className="text-text-subtle dark:text-neutral-400">
|
||||||
No groups found
|
No folders found
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex-grow flex-col overflow-y-auto px-4 flex items-start justify-start gap-y-2">
|
<div className="flex-grow flex-col overflow-y-auto px-4 flex items-start justify-start gap-y-2">
|
||||||
<p className="text-sm text-muted-foreground dark:text-neutral-400">
|
<p className="text-sm text-muted-foreground dark:text-neutral-400">
|
||||||
No groups found
|
No folders found
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href="/chat/my-documents"
|
href="/chat/my-documents?createFolder=true"
|
||||||
className="inline-flex items-center text-sm justify-center text-neutral-600 dark:text-neutral-400 hover:underline"
|
className="inline-flex items-center text-sm justify-center text-neutral-600 dark:text-neutral-400 hover:underline"
|
||||||
>
|
>
|
||||||
<FolderIcon className="mr-2 h-4 w-4" />
|
<FolderIcon className="mr-2 h-4 w-4" />
|
||||||
@ -1270,14 +1265,20 @@ export const FilePickerModal: React.FC<FilePickerModalProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`w-full h-full flex flex-col ${
|
className={`mobile:hidden overflow-y-auto w-full h-full flex flex-col ${
|
||||||
isHoveringRight ? "bg-neutral-100 dark:bg-neutral-800/30" : ""
|
isHoveringRight ? "bg-neutral-100 dark:bg-neutral-800/30" : ""
|
||||||
}`}
|
}`}
|
||||||
onDragEnter={() => setIsHoveringRight(true)}
|
onDragEnter={() => setIsHoveringRight(true)}
|
||||||
onDragLeave={() => setIsHoveringRight(false)}
|
onDragLeave={() => setIsHoveringRight(false)}
|
||||||
>
|
>
|
||||||
<div className="px-5 pb-5 flex-1 flex flex-col">
|
<div className="px-5 h-full flex flex-col">
|
||||||
<div className="shrink default-scrollbar flex h-full overflow-y-auto mb-3">
|
{/* Top section: scrollable, takes remaining space */}
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<h3 className="text-sm font-semibold text-neutral-800 dark:text-neutral-100">
|
||||||
|
Selected Items
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-h-0 overflow-y-auto">
|
||||||
<SelectedItemsList
|
<SelectedItemsList
|
||||||
uploadingFiles={uploadingFiles}
|
uploadingFiles={uploadingFiles}
|
||||||
setPresentingDocument={setPresentingDocument}
|
setPresentingDocument={setPresentingDocument}
|
||||||
@ -1288,69 +1289,68 @@ export const FilePickerModal: React.FC<FilePickerModalProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col space-y-3">
|
{/* Bottom section: fixed height, doesn't flex */}
|
||||||
<div className="flex flex-col space-y-2">
|
<div className="flex-none py-2">
|
||||||
<FileUploadSection
|
<FileUploadSection
|
||||||
disabled={isUploadingFile || isCreatingFileFromLink}
|
disabled={isUploadingFile || isCreatingFileFromLink}
|
||||||
onUpload={(files: File[]) => {
|
onUpload={(files: File[]) => {
|
||||||
setIsUploadingFile(true);
|
setIsUploadingFile(true);
|
||||||
setUploadStartTime(Date.now()); // Record start time
|
setUploadStartTime(Date.now()); // Record start time
|
||||||
|
|
||||||
// Add files to uploading files state
|
// Start the refresh interval to simulate progress
|
||||||
|
startRefreshInterval();
|
||||||
|
|
||||||
// Start the refresh interval to simulate progress
|
// Convert File[] to FileList for addUploadedFileToContext
|
||||||
startRefreshInterval();
|
const fileListArray = Array.from(files);
|
||||||
|
const fileList = new DataTransfer();
|
||||||
|
fileListArray.forEach((file) => fileList.items.add(file));
|
||||||
|
|
||||||
// Convert File[] to FileList for addUploadedFileToContext
|
addUploadedFileToContext(fileList.files)
|
||||||
const fileListArray = Array.from(files);
|
.then(() => refreshFolders())
|
||||||
const fileList = new DataTransfer();
|
.finally(() => {
|
||||||
fileListArray.forEach((file) => fileList.items.add(file));
|
setIsUploadingFile(false);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onUrlUpload={async (url: string) => {
|
||||||
|
setIsCreatingFileFromLink(true);
|
||||||
|
setUploadStartTime(Date.now()); // Record start time
|
||||||
|
|
||||||
addUploadedFileToContext(fileList.files)
|
// Add URL to uploading files
|
||||||
.then(() => refreshFolders())
|
setUploadingFiles((prev) => [
|
||||||
.finally(() => {
|
...prev,
|
||||||
setIsUploadingFile(false);
|
{ name: url, progress: 0 },
|
||||||
});
|
]);
|
||||||
}}
|
|
||||||
onUrlUpload={async (url: string) => {
|
|
||||||
setIsCreatingFileFromLink(true);
|
|
||||||
setUploadStartTime(Date.now()); // Record start time
|
|
||||||
|
|
||||||
// Add URL to uploading files
|
// Start the refresh interval to simulate progress
|
||||||
setUploadingFiles((prev) => [
|
startRefreshInterval();
|
||||||
...prev,
|
|
||||||
{ name: url, progress: 0 },
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Start the refresh interval to simulate progress
|
try {
|
||||||
startRefreshInterval();
|
const response: FileResponse[] = await createFileFromLink(
|
||||||
|
url,
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
if (response.length > 0) {
|
||||||
const response: FileResponse[] =
|
// Extract domain from URL to help with detection
|
||||||
await createFileFromLink(url, -1);
|
const urlObj = new URL(url);
|
||||||
|
|
||||||
if (response.length > 0) {
|
const createdFile: FileResponse = response[0];
|
||||||
// Extract domain from URL to help with detection
|
addSelectedFile(createdFile);
|
||||||
const urlObj = new URL(url);
|
// Make sure to remove the uploading file indicator when done
|
||||||
|
|
||||||
const createdFile: FileResponse = response[0];
|
|
||||||
addSelectedFile(createdFile);
|
|
||||||
// Make sure to remove the uploading file indicator when done
|
|
||||||
markFileComplete(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
await refreshFolders();
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error creating file from link:", e);
|
|
||||||
// Also remove the uploading indicator on error
|
|
||||||
markFileComplete(url);
|
markFileComplete(url);
|
||||||
} finally {
|
|
||||||
setIsCreatingFileFromLink(false);
|
|
||||||
}
|
}
|
||||||
}}
|
|
||||||
isUploading={isUploadingFile || isCreatingFileFromLink}
|
await refreshFolders();
|
||||||
/>
|
} catch (e) {
|
||||||
</div>
|
console.error("Error creating file from link:", e);
|
||||||
|
// Also remove the uploading indicator on error
|
||||||
|
markFileComplete(url);
|
||||||
|
} finally {
|
||||||
|
setIsCreatingFileFromLink(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
isUploading={isUploadingFile || isCreatingFileFromLink}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1375,7 +1375,10 @@ export const FilePickerModal: React.FC<FilePickerModalProps> = ({
|
|||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
onClick={onSave}
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
onSave(selectedItems.files, selectedItems.folders)
|
||||||
|
}
|
||||||
className="px-8 py-2 w-48"
|
className="px-8 py-2 w-48"
|
||||||
disabled={
|
disabled={
|
||||||
isUploadingFile ||
|
isUploadingFile ||
|
||||||
|
@ -26,7 +26,8 @@ export const SelectedItemsList: React.FC<SelectedItemsListProps> = ({
|
|||||||
onRemoveFolder,
|
onRemoveFolder,
|
||||||
setPresentingDocument,
|
setPresentingDocument,
|
||||||
}) => {
|
}) => {
|
||||||
const hasItems = folders.length > 0 || files.length > 0;
|
const hasItems =
|
||||||
|
folders.length > 0 || files.length > 0 || uploadingFiles.length > 0;
|
||||||
const openFile = (file: FileResponse) => {
|
const openFile = (file: FileResponse) => {
|
||||||
if (file.link_url) {
|
if (file.link_url) {
|
||||||
window.open(file.link_url, "_blank");
|
window.open(file.link_url, "_blank");
|
||||||
@ -40,89 +41,143 @@ export const SelectedItemsList: React.FC<SelectedItemsListProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full flex flex-col">
|
<div className="h-full w-full flex flex-col">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="space-y-2.5 pb-2">
|
||||||
<h3 className="text-sm font-semibold text-neutral-800 dark:text-neutral-100">
|
{folders.length > 0 && (
|
||||||
Selected Items
|
<div className="space-y-2.5">
|
||||||
</h3>
|
{folders.map((folder: FolderResponse) => (
|
||||||
</div>
|
<div key={folder.id} className="group flex items-center gap-2">
|
||||||
|
|
||||||
<ScrollArea className="h-[200px] flex-grow pr-1">
|
|
||||||
<div className="space-y-2.5">
|
|
||||||
{folders.length > 0 && (
|
|
||||||
<div className="space-y-2.5">
|
|
||||||
{folders.map((folder: FolderResponse) => (
|
|
||||||
<div key={folder.id} className="group flex items-center gap-2">
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"group flex-1 flex items-center rounded-md border p-2.5",
|
|
||||||
"bg-neutral-100/80 border-neutral-200 hover:bg-neutral-200/60",
|
|
||||||
"dark:bg-neutral-800/80 dark:border-neutral-700 dark:hover:bg-neutral-750",
|
|
||||||
"dark:focus:ring-1 dark:focus:ring-neutral-500 dark:focus:border-neutral-600",
|
|
||||||
"dark:active:bg-neutral-700 dark:active:border-neutral-600",
|
|
||||||
"transition-colors duration-150"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="flex items-center min-w-0 flex-1">
|
|
||||||
<FolderIcon className="h-5 w-5 mr-2 text-black dark:text-black shrink-0 fill-black dark:fill-black" />
|
|
||||||
|
|
||||||
<span className="text-sm font-medium truncate text-neutral-800 dark:text-neutral-100">
|
|
||||||
{truncateString(folder.name, 34)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => onRemoveFolder(folder)}
|
|
||||||
className={cn(
|
|
||||||
"bg-transparent hover:bg-transparent opacity-0 group-hover:opacity-100",
|
|
||||||
"h-6 w-6 p-0 rounded-full shrink-0",
|
|
||||||
"hover:text-neutral-700",
|
|
||||||
"dark:text-neutral-300 dark:hover:text-neutral-100",
|
|
||||||
"dark:focus:ring-1 dark:focus:ring-neutral-500",
|
|
||||||
"dark:active:bg-neutral-500 dark:active:text-white",
|
|
||||||
"transition-all duration-150 ease-in-out"
|
|
||||||
)}
|
|
||||||
aria-label={`Remove folder ${folder.name}`}
|
|
||||||
>
|
|
||||||
<X className="h-3 w-3 dark:text-neutral-200" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{files.length > 0 && (
|
|
||||||
<div className="space-y-2.5 ">
|
|
||||||
{files.map((file: FileResponse) => (
|
|
||||||
<div
|
<div
|
||||||
key={file.id}
|
className={cn(
|
||||||
className="group w-full flex items-center gap-2"
|
"group flex-1 flex items-center rounded-md border p-2.5",
|
||||||
|
"bg-neutral-100/80 border-neutral-200 hover:bg-neutral-200/60",
|
||||||
|
"dark:bg-neutral-800/80 dark:border-neutral-700 dark:hover:bg-neutral-750",
|
||||||
|
"dark:focus:ring-1 dark:focus:ring-neutral-500 dark:focus:border-neutral-600",
|
||||||
|
"dark:active:bg-neutral-700 dark:active:border-neutral-600",
|
||||||
|
"transition-colors duration-150"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div className="flex items-center min-w-0 flex-1">
|
||||||
className={cn(
|
<FolderIcon className="h-5 w-5 mr-2 text-black dark:text-black shrink-0 fill-black dark:fill-black" />
|
||||||
"group flex-1 flex items-center rounded-md border p-2.5",
|
|
||||||
"bg-neutral-50 border-neutral-200 hover:bg-neutral-100",
|
<span className="text-sm font-medium truncate text-neutral-800 dark:text-neutral-100">
|
||||||
"dark:bg-neutral-800/70 dark:border-neutral-700 dark:hover:bg-neutral-750",
|
{truncateString(folder.name, 34)}
|
||||||
"dark:focus:ring-1 dark:focus:ring-neutral-500 dark:focus:border-neutral-600",
|
</span>
|
||||||
"dark:active:bg-neutral-700 dark:active:border-neutral-600",
|
</div>
|
||||||
"transition-colors duration-150",
|
</div>
|
||||||
"cursor-pointer"
|
|
||||||
)}
|
<Button
|
||||||
onClick={() => openFile(file)}
|
variant="ghost"
|
||||||
>
|
size="sm"
|
||||||
<div className="flex items-center min-w-0 flex-1">
|
onClick={() => onRemoveFolder(folder)}
|
||||||
{getFileIconFromFileNameAndLink(file.name, file.link_url)}
|
className={cn(
|
||||||
<span className="text-sm truncate text-neutral-700 dark:text-neutral-200 ml-2.5">
|
"bg-transparent hover:bg-transparent opacity-0 group-hover:opacity-100",
|
||||||
{truncateString(file.name, 34)}
|
"h-6 w-6 p-0 rounded-full shrink-0",
|
||||||
|
"hover:text-neutral-700",
|
||||||
|
"dark:text-neutral-300 dark:hover:text-neutral-100",
|
||||||
|
"dark:focus:ring-1 dark:focus:ring-neutral-500",
|
||||||
|
"dark:active:bg-neutral-500 dark:active:text-white",
|
||||||
|
"transition-all duration-150 ease-in-out"
|
||||||
|
)}
|
||||||
|
aria-label={`Remove folder ${folder.name}`}
|
||||||
|
>
|
||||||
|
<X className="h-3 w-3 dark:text-neutral-200" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{files.length > 0 && (
|
||||||
|
<div className="space-y-2.5 ">
|
||||||
|
{files.map((file: FileResponse) => (
|
||||||
|
<div
|
||||||
|
key={file.id}
|
||||||
|
className="group w-full flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"group flex-1 flex items-center rounded-md border p-2.5",
|
||||||
|
"bg-neutral-50 border-neutral-200 hover:bg-neutral-100",
|
||||||
|
"dark:bg-neutral-800/70 dark:border-neutral-700 dark:hover:bg-neutral-750",
|
||||||
|
"dark:focus:ring-1 dark:focus:ring-neutral-500 dark:focus:border-neutral-600",
|
||||||
|
"dark:active:bg-neutral-700 dark:active:border-neutral-600",
|
||||||
|
"transition-colors duration-150",
|
||||||
|
"cursor-pointer"
|
||||||
|
)}
|
||||||
|
onClick={() => openFile(file)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center min-w-0 flex-1">
|
||||||
|
{getFileIconFromFileNameAndLink(file.name, file.link_url)}
|
||||||
|
<span className="text-sm truncate text-neutral-700 dark:text-neutral-200 ml-2.5">
|
||||||
|
{truncateString(file.name, 34)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => onRemoveFile(file)}
|
||||||
|
className={cn(
|
||||||
|
"bg-transparent hover:bg-transparent opacity-0 group-hover:opacity-100",
|
||||||
|
"h-6 w-6 p-0 rounded-full shrink-0",
|
||||||
|
"hover:text-neutral-700",
|
||||||
|
"dark:text-neutral-300 dark:hover:text-neutral-100",
|
||||||
|
"dark:focus:ring-1 dark:focus:ring-neutral-500",
|
||||||
|
"dark:active:bg-neutral-500 dark:active:text-white",
|
||||||
|
"transition-all duration-150 ease-in-out"
|
||||||
|
)}
|
||||||
|
aria-label={`Remove file ${file.name}`}
|
||||||
|
>
|
||||||
|
<X className="h-3 w-3 dark:text-neutral-200" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="max-w-full space-y-2.5">
|
||||||
|
{uploadingFiles
|
||||||
|
.filter(
|
||||||
|
(uploadingFile) =>
|
||||||
|
!files.map((file) => file.name).includes(uploadingFile.name)
|
||||||
|
)
|
||||||
|
.map((uploadingFile, index) => (
|
||||||
|
<div key={index} className="mr-8 flex items-center gap-2">
|
||||||
|
<div
|
||||||
|
key={`uploading-${index}`}
|
||||||
|
className={cn(
|
||||||
|
"group flex-1 flex items-center rounded-md border p-2.5",
|
||||||
|
"bg-neutral-50 border-neutral-200 hover:bg-neutral-100",
|
||||||
|
"dark:bg-neutral-800/70 dark:border-neutral-700 dark:hover:bg-neutral-750",
|
||||||
|
"dark:focus:ring-1 dark:focus:ring-neutral-500 dark:focus:border-neutral-600",
|
||||||
|
"dark:active:bg-neutral-700 dark:active:border-neutral-600",
|
||||||
|
"transition-colors duration-150",
|
||||||
|
"cursor-pointer"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center min-w-0 flex-1">
|
||||||
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
|
{uploadingFile.name.startsWith("http") ? (
|
||||||
|
<Loader2 className="w-4 h-4 animate-spin text-blue-500" />
|
||||||
|
) : (
|
||||||
|
<CircularProgress
|
||||||
|
progress={uploadingFile.progress}
|
||||||
|
size={18}
|
||||||
|
showPercentage={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span className="truncate text-sm text-text-dark dark:text-text-dark">
|
||||||
|
{uploadingFile.name.startsWith("http")
|
||||||
|
? `${uploadingFile.name.substring(0, 30)}${
|
||||||
|
uploadingFile.name.length > 30 ? "..." : ""
|
||||||
|
}`
|
||||||
|
: truncateString(uploadingFile.name, 34)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => onRemoveFile(file)}
|
// onClick={() => onRemoveFile(file)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-transparent hover:bg-transparent opacity-0 group-hover:opacity-100",
|
"bg-transparent hover:bg-transparent opacity-0 group-hover:opacity-100",
|
||||||
"h-6 w-6 p-0 rounded-full shrink-0",
|
"h-6 w-6 p-0 rounded-full shrink-0",
|
||||||
@ -132,82 +187,20 @@ export const SelectedItemsList: React.FC<SelectedItemsListProps> = ({
|
|||||||
"dark:active:bg-neutral-500 dark:active:text-white",
|
"dark:active:bg-neutral-500 dark:active:text-white",
|
||||||
"transition-all duration-150 ease-in-out"
|
"transition-all duration-150 ease-in-out"
|
||||||
)}
|
)}
|
||||||
aria-label={`Remove file ${file.name}`}
|
// aria-label={`Remove file ${file.name}`}
|
||||||
>
|
>
|
||||||
<X className="h-3 w-3 dark:text-neutral-200" />
|
<X className="h-3 w-3 dark:text-neutral-200" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
</div>
|
||||||
</div>
|
))}
|
||||||
)}
|
|
||||||
<div className="max-w-full space-y-2.5">
|
|
||||||
{uploadingFiles
|
|
||||||
.filter(
|
|
||||||
(uploadingFile) =>
|
|
||||||
!files.map((file) => file.name).includes(uploadingFile.name)
|
|
||||||
)
|
|
||||||
.map((uploadingFile, index) => (
|
|
||||||
<div key={index} className="mr-8 flex items-center gap-2">
|
|
||||||
<div
|
|
||||||
key={`uploading-${index}`}
|
|
||||||
className={cn(
|
|
||||||
"group flex-1 flex items-center rounded-md border p-2.5",
|
|
||||||
"bg-neutral-50 border-neutral-200 hover:bg-neutral-100",
|
|
||||||
"dark:bg-neutral-800/70 dark:border-neutral-700 dark:hover:bg-neutral-750",
|
|
||||||
"dark:focus:ring-1 dark:focus:ring-neutral-500 dark:focus:border-neutral-600",
|
|
||||||
"dark:active:bg-neutral-700 dark:active:border-neutral-600",
|
|
||||||
"transition-colors duration-150",
|
|
||||||
"cursor-pointer"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="flex items-center min-w-0 flex-1">
|
|
||||||
<div className="flex items-center gap-2 min-w-0">
|
|
||||||
{uploadingFile.name.startsWith("http") ? (
|
|
||||||
<Loader2 className="w-4 h-4 animate-spin text-blue-500" />
|
|
||||||
) : (
|
|
||||||
<CircularProgress
|
|
||||||
progress={uploadingFile.progress}
|
|
||||||
size={18}
|
|
||||||
showPercentage={false}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<span className="truncate text-sm text-text-dark dark:text-text-dark">
|
|
||||||
{uploadingFile.name.startsWith("http")
|
|
||||||
? `${uploadingFile.name.substring(0, 30)}${
|
|
||||||
uploadingFile.name.length > 30 ? "..." : ""
|
|
||||||
}`
|
|
||||||
: truncateString(uploadingFile.name, 34)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
// onClick={() => onRemoveFile(file)}
|
|
||||||
className={cn(
|
|
||||||
"bg-transparent hover:bg-transparent opacity-0 group-hover:opacity-100",
|
|
||||||
"h-6 w-6 p-0 rounded-full shrink-0",
|
|
||||||
"hover:text-neutral-700",
|
|
||||||
"dark:text-neutral-300 dark:hover:text-neutral-100",
|
|
||||||
"dark:focus:ring-1 dark:focus:ring-neutral-500",
|
|
||||||
"dark:active:bg-neutral-500 dark:active:text-white",
|
|
||||||
"transition-all duration-150 ease-in-out"
|
|
||||||
)}
|
|
||||||
// aria-label={`Remove file ${file.name}`}
|
|
||||||
>
|
|
||||||
<X className="h-3 w-3 dark:text-neutral-200" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{!hasItems && (
|
|
||||||
<div className="flex items-center justify-center h-24 text-sm text-neutral-500 dark:text-neutral-400 italic bg-neutral-50/50 dark:bg-neutral-800/30 rounded-md border border-neutral-200/50 dark:border-neutral-700/50">
|
|
||||||
No items selected
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
{!hasItems && (
|
||||||
|
<div className="flex items-center justify-center h-24 text-sm text-neutral-500 dark:text-neutral-400 italic bg-neutral-50/50 dark:bg-neutral-800/30 rounded-md border border-neutral-200/50 dark:border-neutral-700/50">
|
||||||
|
No items selected
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -26,9 +26,7 @@ interface SharedFolderItemProps {
|
|||||||
onClick: (folderId: number) => void;
|
onClick: (folderId: number) => void;
|
||||||
description?: string;
|
description?: string;
|
||||||
lastUpdated?: string;
|
lastUpdated?: string;
|
||||||
onRename: () => void;
|
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
onMove: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SharedFolderItem: React.FC<SharedFolderItemProps> = ({
|
export const SharedFolderItem: React.FC<SharedFolderItemProps> = ({
|
||||||
@ -36,9 +34,7 @@ export const SharedFolderItem: React.FC<SharedFolderItemProps> = ({
|
|||||||
onClick,
|
onClick,
|
||||||
description,
|
description,
|
||||||
lastUpdated,
|
lastUpdated,
|
||||||
onRename,
|
|
||||||
onDelete,
|
onDelete,
|
||||||
onMove,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
|
|
||||||
@ -99,7 +95,7 @@ export const SharedFolderItem: React.FC<SharedFolderItemProps> = ({
|
|||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className={`group-hover:visible invisible h-8 w-8 p-0 ${
|
className={`group-hover:visible mobile:visible invisible h-8 w-8 p-0 ${
|
||||||
folder.id === -1 ? "!invisible pointer-events-none" : ""
|
folder.id === -1 ? "!invisible pointer-events-none" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -108,14 +104,6 @@ export const SharedFolderItem: React.FC<SharedFolderItemProps> = ({
|
|||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="!p-0 w-40">
|
<PopoverContent className="!p-0 w-40">
|
||||||
<div className="space-y-0">
|
<div className="space-y-0">
|
||||||
{/* <Button variant="menu" onClick={onMove}>
|
|
||||||
<FiArrowDown className="h-4 w-4" />
|
|
||||||
Move
|
|
||||||
</Button>
|
|
||||||
<Button variant="menu" onClick={onRename}>
|
|
||||||
<FiEdit className="h-4 w-4" />
|
|
||||||
Rename
|
|
||||||
</Button> */}
|
|
||||||
<Button variant="menu" onClick={handleDeleteClick}>
|
<Button variant="menu" onClick={handleDeleteClick}>
|
||||||
<FiTrash className="h-4 w-4" />
|
<FiTrash className="h-4 w-4" />
|
||||||
Delete
|
Delete
|
||||||
|
@ -23,21 +23,21 @@ import { Modal } from "@/components/Modal";
|
|||||||
import FunctionalHeader from "@/components/chat/Header";
|
import FunctionalHeader from "@/components/chat/Header";
|
||||||
import FixedLogo from "@/components/logo/FixedLogo";
|
import FixedLogo from "@/components/logo/FixedLogo";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
function BackToOnyxButton({
|
function BackToOnyxButton({
|
||||||
documentSidebarVisible,
|
documentSidebarVisible,
|
||||||
}: {
|
}: {
|
||||||
documentSidebarVisible: boolean;
|
documentSidebarVisible: boolean;
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
|
||||||
const enterpriseSettings = useContext(SettingsContext)?.enterpriseSettings;
|
const enterpriseSettings = useContext(SettingsContext)?.enterpriseSettings;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute bottom-0 bg-background w-full flex border-t border-border py-4">
|
<div className="absolute bottom-0 bg-background w-full flex border-t border-border py-4">
|
||||||
<div className="mx-auto">
|
<div className="mx-auto">
|
||||||
<Button onClick={() => router.push("/chat")}>
|
<Link href="/chat">
|
||||||
Back to {enterpriseSettings?.application_name || "Onyx Chat"}
|
Back to {enterpriseSettings?.application_name || "Onyx Chat"}
|
||||||
</Button>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{ transition: "width 0.30s ease-out" }}
|
style={{ transition: "width 0.30s ease-out" }}
|
||||||
|
@ -124,10 +124,16 @@ const StandardAnswersTableRow = ({
|
|||||||
? `\`${standardAnswer.keyword}\``
|
? `\`${standardAnswer.keyword}\``
|
||||||
: standardAnswer.keyword}
|
: standardAnswer.keyword}
|
||||||
</ReactMarkdown>,
|
</ReactMarkdown>,
|
||||||
<CustomCheckbox
|
<div
|
||||||
key={`match_regex-${standardAnswer.id}`}
|
key={`match_regex-${standardAnswer.id}`}
|
||||||
checked={standardAnswer.match_regex}
|
className="flex items-center"
|
||||||
/>,
|
>
|
||||||
|
{standardAnswer.match_regex ? (
|
||||||
|
<span className="text-green-500 font-medium">Yes</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-gray-500">No</span>
|
||||||
|
)}
|
||||||
|
</div>,
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
key={`answer-${standardAnswer.id}`}
|
key={`answer-${standardAnswer.id}`}
|
||||||
className="prose dark:prose-invert"
|
className="prose dark:prose-invert"
|
||||||
@ -290,8 +296,8 @@ const StandardAnswersTable = ({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-auto">
|
<div className="flex flex-col w-full mx-auto">
|
||||||
<Table className="w-full flex items-stretch">
|
<Table className="w-full">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{columns.map((column) => (
|
{columns.map((column) => (
|
||||||
@ -314,11 +320,13 @@ const StandardAnswersTable = ({
|
|||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
{paginatedStandardAnswers.length === 0 && (
|
<div>
|
||||||
<div className="flex justify-center">
|
{paginatedStandardAnswers.length === 0 && (
|
||||||
<Text>No matching standard answers found...</Text>
|
<div className="flex justify-center">
|
||||||
</div>
|
<Text>No matching standard answers found...</Text>
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{paginatedStandardAnswers.length > 0 && (
|
{paginatedStandardAnswers.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
|
@ -144,51 +144,52 @@ export function WhitelabelingForm() {
|
|||||||
placeholder="Custom name which will replace 'Onyx'"
|
placeholder="Custom name which will replace 'Onyx'"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
|
<Label className="mt-4">Custom Logo</Label>
|
||||||
|
|
||||||
<Label className="mt-4">Custom Logo</Label>
|
{values.use_custom_logo ? (
|
||||||
|
<div className="mt-3">
|
||||||
|
<SubLabel>Current Custom Logo: </SubLabel>
|
||||||
|
<img
|
||||||
|
src={"/api/enterprise-settings/logo?u=" + Date.now()}
|
||||||
|
alt="logo"
|
||||||
|
style={{ objectFit: "contain" }}
|
||||||
|
className="w-32 h-32 mb-10 mt-4"
|
||||||
|
/>
|
||||||
|
|
||||||
{values.use_custom_logo ? (
|
<Button
|
||||||
<div className="mt-3">
|
variant="destructive"
|
||||||
<SubLabel>Current Custom Logo: </SubLabel>
|
size="sm"
|
||||||
<img
|
type="button"
|
||||||
src={"/api/enterprise-settings/logo?u=" + Date.now()}
|
className="mb-8"
|
||||||
alt="logo"
|
onClick={async () => {
|
||||||
style={{ objectFit: "contain" }}
|
const valuesWithoutLogo = {
|
||||||
className="w-32 h-32 mb-10 mt-4"
|
...values,
|
||||||
/>
|
use_custom_logo: false,
|
||||||
|
};
|
||||||
<Button
|
await updateEnterpriseSettings(valuesWithoutLogo);
|
||||||
variant="destructive"
|
setValues(valuesWithoutLogo);
|
||||||
size="sm"
|
}}
|
||||||
type="button"
|
>
|
||||||
className="mb-8"
|
Delete
|
||||||
onClick={async () => {
|
</Button>
|
||||||
const valuesWithoutLogo = {
|
|
||||||
...values,
|
|
||||||
use_custom_logo: false,
|
|
||||||
};
|
|
||||||
await updateEnterpriseSettings(valuesWithoutLogo);
|
|
||||||
setValues(valuesWithoutLogo);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
|
<SubLabel>
|
||||||
|
Override the current custom logo by uploading a new image
|
||||||
|
below and clicking the Update button.
|
||||||
|
</SubLabel>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<SubLabel>
|
<SubLabel>
|
||||||
Override the current custom logo by uploading a new image
|
Specify your own logo to replace the standard Onyx logo.
|
||||||
below and clicking the Update button.
|
|
||||||
</SubLabel>
|
</SubLabel>
|
||||||
</div>
|
)}
|
||||||
) : (
|
|
||||||
<SubLabel>
|
|
||||||
Specify your own logo to replace the standard Onyx logo.
|
|
||||||
</SubLabel>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ImageUpload
|
<ImageUpload
|
||||||
selectedFile={selectedLogo}
|
selectedFile={selectedLogo}
|
||||||
setSelectedFile={setSelectedLogo}
|
setSelectedFile={setSelectedLogo}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
|
@ -674,3 +674,7 @@ ul > li > p {
|
|||||||
.animate-fadeIn {
|
.animate-fadeIn {
|
||||||
animation: fadeIn 0.2s ease-out forwards;
|
animation: fadeIn 0.2s ease-out forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
@ -115,17 +115,17 @@ export const ConnectorMultiSelect = ({
|
|||||||
<div className="flex flex-col w-full space-y-2 mb-4">
|
<div className="flex flex-col w-full space-y-2 mb-4">
|
||||||
{label && <Label className="text-base font-medium">{label}</Label>}
|
{label && <Label className="text-base font-medium">{label}</Label>}
|
||||||
|
|
||||||
<p className="text-xs text-neutral-500 ">
|
<p className="text-xs text-neutral-500 dark:text-neutral-400">
|
||||||
All documents indexed by the selected connectors will be part of this
|
All documents indexed by the selected connectors will be part of this
|
||||||
document set.
|
document set.
|
||||||
</p>
|
</p>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div
|
<div
|
||||||
className={`flex items-center border border-input rounded-md border border-neutral-200 ${
|
className={`flex items-center border border-input rounded-md border-neutral-200 dark:border-neutral-700 ${
|
||||||
allConnectorsSelected ? "bg-neutral-50" : ""
|
allConnectorsSelected ? "bg-neutral-50 dark:bg-neutral-800" : ""
|
||||||
} focus-within:ring-1 focus-within:ring-ring focus-within:border-neutral-400 transition-colors`}
|
} focus-within:ring-1 focus-within:ring-ring focus-within:border-neutral-400 dark:focus-within:border-neutral-500 transition-colors`}
|
||||||
>
|
>
|
||||||
<Search className="absolute left-3 h-4 w-4 text-neutral-500" />
|
<Search className="absolute left-3 h-4 w-4 text-neutral-500 dark:text-neutral-400" />
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
@ -141,8 +141,10 @@ export const ConnectorMultiSelect = ({
|
|||||||
}}
|
}}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
placeholder={effectivePlaceholder}
|
placeholder={effectivePlaceholder}
|
||||||
className={`h-9 w-full pl-9 pr-10 py-2 bg-transparent text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50 ${
|
className={`h-9 w-full pl-9 pr-10 py-2 bg-transparent dark:bg-transparent text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50 ${
|
||||||
allConnectorsSelected ? "text-neutral-500" : ""
|
allConnectorsSelected
|
||||||
|
? "text-neutral-500 dark:text-neutral-400"
|
||||||
|
: ""
|
||||||
}`}
|
}`}
|
||||||
disabled={isInputDisabled}
|
disabled={isInputDisabled}
|
||||||
/>
|
/>
|
||||||
@ -151,10 +153,10 @@ export const ConnectorMultiSelect = ({
|
|||||||
{open && !allConnectorsSelected && (
|
{open && !allConnectorsSelected && (
|
||||||
<div
|
<div
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
className="absolute z-50 w-full mt-1 rounded-md border border-neutral-200 bg-white shadow-md default-scrollbar max-h-[300px] overflow-auto"
|
className="absolute z-50 w-full mt-1 rounded-md border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-900 shadow-md default-scrollbar max-h-[300px] overflow-auto"
|
||||||
>
|
>
|
||||||
{filteredUnselectedConnectors.length === 0 ? (
|
{filteredUnselectedConnectors.length === 0 ? (
|
||||||
<div className="py-4 text-center text-xs text-neutral-500">
|
<div className="py-4 text-center text-xs text-neutral-500 dark:text-neutral-400">
|
||||||
{searchQuery
|
{searchQuery
|
||||||
? "No matching connectors found"
|
? "No matching connectors found"
|
||||||
: "No more connectors available"}
|
: "No more connectors available"}
|
||||||
@ -164,7 +166,7 @@ export const ConnectorMultiSelect = ({
|
|||||||
{filteredUnselectedConnectors.map((connector) => (
|
{filteredUnselectedConnectors.map((connector) => (
|
||||||
<div
|
<div
|
||||||
key={connector.cc_pair_id}
|
key={connector.cc_pair_id}
|
||||||
className="flex items-center justify-between py-2 px-3 cursor-pointer hover:bg-neutral-50 text-xs"
|
className="flex items-center justify-between py-2 px-3 cursor-pointer hover:bg-neutral-50 dark:hover:bg-neutral-800 text-xs"
|
||||||
onClick={() => selectConnector(connector.cc_pair_id)}
|
onClick={() => selectConnector(connector.cc_pair_id)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center truncate mr-2">
|
<div className="flex items-center truncate mr-2">
|
||||||
@ -185,12 +187,12 @@ export const ConnectorMultiSelect = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{selectedConnectors.length > 0 ? (
|
{selectedConnectors.length > 0 ? (
|
||||||
<div className="mt-3 ">
|
<div className="mt-3">
|
||||||
<div className="flex flex-wrap gap-1.5">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
{selectedConnectors.map((connector) => (
|
{selectedConnectors.map((connector) => (
|
||||||
<div
|
<div
|
||||||
key={connector.cc_pair_id}
|
key={connector.cc_pair_id}
|
||||||
className="flex items-center bg-white rounded-md border border-neutral-300 transition-all px-2 py-1 max-w-full group text-xs"
|
className="flex items-center bg-white dark:bg-neutral-800 rounded-md border border-neutral-300 dark:border-neutral-700 transition-all px-2 py-1 max-w-full group text-xs"
|
||||||
>
|
>
|
||||||
<div className="flex items-center overflow-hidden">
|
<div className="flex items-center overflow-hidden">
|
||||||
<div className="flex-shrink-0 text-xs">
|
<div className="flex-shrink-0 text-xs">
|
||||||
@ -204,7 +206,7 @@ export const ConnectorMultiSelect = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="ml-1 flex-shrink-0 rounded-full w-4 h-4 flex items-center justify-center bg-neutral-100 text-neutral-500 hover:bg-neutral-200 hover:text-neutral-700 transition-colors group-hover:bg-neutral-200"
|
className="ml-1 flex-shrink-0 rounded-full w-4 h-4 flex items-center justify-center bg-neutral-100 dark:bg-neutral-700 text-neutral-500 dark:text-neutral-400 hover:bg-neutral-200 dark:hover:bg-neutral-600 hover:text-neutral-700 dark:hover:text-neutral-300 transition-colors group-hover:bg-neutral-200 dark:group-hover:bg-neutral-600"
|
||||||
onClick={() => removeConnector(connector.cc_pair_id)}
|
onClick={() => removeConnector(connector.cc_pair_id)}
|
||||||
aria-label="Remove connector"
|
aria-label="Remove connector"
|
||||||
>
|
>
|
||||||
@ -215,7 +217,7 @@ export const ConnectorMultiSelect = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="mt-3 p-3 border border-dashed border-neutral-300 rounded-md bg-neutral-50 text-neutral-500 text-xs">
|
<div className="mt-3 p-3 border border-dashed border-neutral-300 dark:border-neutral-700 rounded-md bg-neutral-50 dark:bg-neutral-800 text-neutral-500 dark:text-neutral-400 text-xs">
|
||||||
No connectors selected. Search and select connectors above.
|
No connectors selected. Search and select connectors above.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -224,7 +226,7 @@ export const ConnectorMultiSelect = ({
|
|||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
name={name}
|
name={name}
|
||||||
component="div"
|
component="div"
|
||||||
className="text-red-500 text-xs mt-1"
|
className="text-red-500 dark:text-red-400 text-xs mt-1"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -92,8 +92,9 @@ export function Modal({
|
|||||||
${className || ""}
|
${className || ""}
|
||||||
flex
|
flex
|
||||||
flex-col
|
flex-col
|
||||||
|
|
||||||
${heightOverride ? `h-${heightOverride}` : "max-h-[90vh]"}
|
${heightOverride ? `h-${heightOverride}` : "max-h-[90vh]"}
|
||||||
${hideOverflow ? "overflow-hidden" : "overflow-auto"}
|
${hideOverflow ? "overflow-hidden" : "overflow-visible"}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{onOutsideClick && !hideCloseButton && (
|
{onOutsideClick && !hideCloseButton && (
|
||||||
|
@ -69,7 +69,7 @@ const MultiSelectDropdown = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col space-y-4 mb-4">
|
<div className="flex flex-col text-white space-y-4 mb-4">
|
||||||
<Label>{label}</Label>
|
<Label>{label}</Label>
|
||||||
{creatable ? (
|
{creatable ? (
|
||||||
<CreatableSelect
|
<CreatableSelect
|
||||||
|
@ -27,9 +27,9 @@ export function TokenDisplay({
|
|||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div className="flex items-center space-x-3 bg-neutral-100 dark:bg-neutral-800 rounded-full px-4 py-1.5">
|
<div className="flex items-center space-x-3 bg-neutral-100 dark:bg-neutral-800 rounded-full px-4 py-1.5">
|
||||||
<div className="relative w-36 h-2 bg-neutral-200 dark:bg-neutral-700 rounded-full overflow-hidden">
|
<div className="hidden sm:block relative w-24 h-2 bg-neutral-200 dark:bg-neutral-700 rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className={`absolute top-0 left-0 h-full rounded-full ${
|
className={` absolute top-0 left-0 h-full rounded-full ${
|
||||||
tokenPercentage >= 100
|
tokenPercentage >= 100
|
||||||
? "bg-yellow-500 dark:bg-yellow-600"
|
? "bg-yellow-500 dark:bg-yellow-600"
|
||||||
: "bg-green-500 dark:bg-green-600"
|
: "bg-green-500 dark:bg-green-600"
|
||||||
|
@ -379,18 +379,24 @@ export function ClientLayout({
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
{
|
...(!enableCloud
|
||||||
name: (
|
? [
|
||||||
<div className="flex">
|
{
|
||||||
<FiBarChart2
|
name: (
|
||||||
className="text-text-700"
|
<div className="flex">
|
||||||
size={18}
|
<FiBarChart2
|
||||||
/>
|
className="text-text-700"
|
||||||
<div className="ml-1">Custom Analytics</div>
|
size={18}
|
||||||
</div>
|
/>
|
||||||
),
|
<div className="ml-1">
|
||||||
link: "/admin/performance/custom-analytics",
|
Custom Analytics
|
||||||
},
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
link: "/admin/performance/custom-analytics",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -453,7 +459,7 @@ export function ClientLayout({
|
|||||||
<div className="fixed left-0 gap-x-4 px-4 top-4 h-8 px-0 mb-auto w-full items-start flex justify-end">
|
<div className="fixed left-0 gap-x-4 px-4 top-4 h-8 px-0 mb-auto w-full items-start flex justify-end">
|
||||||
<UserDropdown toggleUserSettings={toggleUserSettings} />
|
<UserDropdown toggleUserSettings={toggleUserSettings} />
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-20 flex w-full overflow-y-auto overflow-x-hidden h-full px-4 md:px-12">
|
<div className="pt-20 pb-4 flex w-full overflow-y-auto overflow-x-hidden h-full px-4 md:px-12">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -201,7 +201,6 @@ export function TextFormField({
|
|||||||
maxWidth,
|
maxWidth,
|
||||||
removeLabel,
|
removeLabel,
|
||||||
min,
|
min,
|
||||||
includeForgotPassword,
|
|
||||||
onChange,
|
onChange,
|
||||||
width,
|
width,
|
||||||
vertical,
|
vertical,
|
||||||
@ -229,7 +228,6 @@ export function TextFormField({
|
|||||||
explanationLink?: string;
|
explanationLink?: string;
|
||||||
small?: boolean;
|
small?: boolean;
|
||||||
min?: number;
|
min?: number;
|
||||||
includeForgotPassword?: boolean;
|
|
||||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
width?: string;
|
width?: string;
|
||||||
vertical?: boolean;
|
vertical?: boolean;
|
||||||
@ -339,14 +337,6 @@ export function TextFormField({
|
|||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
autoComplete={autoCompleteDisabled ? "off" : undefined}
|
autoComplete={autoCompleteDisabled ? "off" : undefined}
|
||||||
/>
|
/>
|
||||||
{includeForgotPassword && (
|
|
||||||
<Link
|
|
||||||
href="/auth/forgot-password"
|
|
||||||
className="absolute right-3 top-1/2 mt-[3px] transform -translate-y-1/2 text-xs text-blue-500 cursor-pointer"
|
|
||||||
>
|
|
||||||
Forgot password?
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{explanationText && (
|
{explanationText && (
|
||||||
|
@ -16,7 +16,6 @@ interface TextViewProps {
|
|||||||
presentingDocument: MinimalOnyxDocument;
|
presentingDocument: MinimalOnyxDocument;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TextView({
|
export default function TextView({
|
||||||
presentingDocument,
|
presentingDocument,
|
||||||
onClose,
|
onClose,
|
||||||
@ -27,6 +26,13 @@ export default function TextView({
|
|||||||
const [fileName, setFileName] = useState("");
|
const [fileName, setFileName] = useState("");
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [fileType, setFileType] = useState("application/octet-stream");
|
const [fileType, setFileType] = useState("application/octet-stream");
|
||||||
|
const [renderCount, setRenderCount] = useState(0);
|
||||||
|
|
||||||
|
// Log render count on each render
|
||||||
|
useEffect(() => {
|
||||||
|
setRenderCount((prevCount) => prevCount + 1);
|
||||||
|
console.log(`TextView component rendered ${renderCount + 1} times`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Detect if a given MIME type is one of the recognized markdown formats
|
// Detect if a given MIME type is one of the recognized markdown formats
|
||||||
const isMarkdownFormat = (mimeType: string): boolean => {
|
const isMarkdownFormat = (mimeType: string): boolean => {
|
||||||
@ -63,6 +69,7 @@ export default function TextView({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchFile = useCallback(async () => {
|
const fetchFile = useCallback(async () => {
|
||||||
|
console.log("fetching file");
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const fileId =
|
const fileId =
|
||||||
presentingDocument.document_id.split("__")[1] ||
|
presentingDocument.document_id.split("__")[1] ||
|
||||||
@ -107,13 +114,14 @@ export default function TextView({
|
|||||||
// Keep the slight delay for a smoother loading experience
|
// Keep the slight delay for a smoother loading experience
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
console.log("finished loading");
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}, [presentingDocument]);
|
}, [presentingDocument]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchFile();
|
fetchFile();
|
||||||
}, [fetchFile]);
|
}, []);
|
||||||
|
|
||||||
const handleDownload = () => {
|
const handleDownload = () => {
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
|
@ -327,7 +327,7 @@ export function HorizontalFilters({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="b">
|
||||||
<div className="flex gap-x-3">
|
<div className="flex gap-x-3">
|
||||||
<div className="w-52">
|
<div className="w-52">
|
||||||
<DateRangeSelector value={timeRange} onValueChange={setTimeRange} />
|
<DateRangeSelector value={timeRange} onValueChange={setTimeRange} />
|
||||||
@ -387,7 +387,7 @@ export function HorizontalFilters({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex pb-4 mt-2 h-12">
|
<div className="flex mt-2">
|
||||||
<div className="flex flex-wrap gap-x-2">
|
<div className="flex flex-wrap gap-x-2">
|
||||||
{timeRange && timeRange.selectValue && (
|
{timeRange && timeRange.selectValue && (
|
||||||
<SelectedBubble onClick={() => setTimeRange(null)}>
|
<SelectedBubble onClick={() => setTimeRange(null)}>
|
||||||
|
@ -1303,7 +1303,7 @@ export const ProductboardIcon = ({
|
|||||||
size = 16,
|
size = 16,
|
||||||
className = defaultTailwindCSS,
|
className = defaultTailwindCSS,
|
||||||
}: IconProps) => (
|
}: IconProps) => (
|
||||||
<LogoIcon size={size} className={className} src="/Productboard.webp" />
|
<LogoIcon size={size} className={className} src="/Productboard.png" />
|
||||||
);
|
);
|
||||||
|
|
||||||
export const AzureIcon = ({
|
export const AzureIcon = ({
|
||||||
|
@ -45,7 +45,7 @@ export default function CreateEntityModal({
|
|||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[425px]">
|
<DialogContent className="max-w-[95%] sm:max-w-[425px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{title}</DialogTitle>
|
<DialogTitle>{title}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
@ -437,19 +437,20 @@ export function CompactDocumentCard({
|
|||||||
url?: string;
|
url?: string;
|
||||||
updatePresentingDocument: (document: OnyxDocument) => void;
|
updatePresentingDocument: (document: OnyxDocument) => void;
|
||||||
}) {
|
}) {
|
||||||
console.log("document", document);
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
openDocument(document, updatePresentingDocument);
|
openDocument(document, updatePresentingDocument);
|
||||||
}}
|
}}
|
||||||
className="max-w-[200px] gap-y-0 cursor-pointer pb-0 pt-0 mt-0 flex gap-y-0 flex-col content-start items-start gap-0 "
|
className="max-w-[250px] gap-y-1 cursor-pointer pb-0 pt-0 mt-0 flex gap-y-0 flex-col content-start items-start gap-0 "
|
||||||
>
|
>
|
||||||
<div className="text-sm !pb-0 !mb-0 font-semibold flex items-center gap-x-1 text-text-900 pt-0 mt-0 truncate w-full">
|
<div className="text-sm flex gap-x-2 !pb-0 !mb-0 font-semibold flex items-center gap-x-1 text-text-900 pt-0 mt-0 w-full">
|
||||||
{icon}
|
{icon}
|
||||||
{(document.semantic_identifier || document.document_id).slice(0, 40)}
|
<p className="gap-0 p-0 m-0 line-clamp-2">
|
||||||
{(document.semantic_identifier || document.document_id).length > 40 &&
|
{(document.semantic_identifier || document.document_id).slice(0, 40)}
|
||||||
"..."}
|
{(document.semantic_identifier || document.document_id).length > 40 &&
|
||||||
|
"..."}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{document.blurb && (
|
{document.blurb && (
|
||||||
<div className="text-xs mb-0 text-neutral-600 dark:text-neutral-300 line-clamp-2">
|
<div className="text-xs mb-0 text-neutral-600 dark:text-neutral-300 line-clamp-2">
|
||||||
@ -479,7 +480,7 @@ export function CompactQuestionCard({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={() => openQuestion(question)}
|
onClick={() => openQuestion(question)}
|
||||||
className="max-w-[250px] gap-y-0 cursor-pointer pb-0 pt-0 mt-0 flex gap-y-0 flex-col content-start items-start gap-0"
|
className="max-w-[350px] gap-y-1 cursor-pointer pb-0 pt-0 mt-0 flex gap-y-0 flex-col content-start items-start gap-0"
|
||||||
>
|
>
|
||||||
<div className="text-sm !pb-0 !mb-0 font-semibold flex items-center gap-x-1 text-text-900 pt-0 mt-0 truncate w-full">
|
<div className="text-sm !pb-0 !mb-0 font-semibold flex items-center gap-x-1 text-text-900 pt-0 mt-0 truncate w-full">
|
||||||
Question
|
Question
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { openDocument } from "@/lib/search/utils";
|
import { openDocument } from "@/lib/search/utils";
|
||||||
import { SubQuestionDetail } from "@/app/chat/interfaces";
|
import { SubQuestionDetail } from "@/app/chat/interfaces";
|
||||||
|
import { getFileIconFromFileNameAndLink } from "@/lib/assistantIconUtils";
|
||||||
|
|
||||||
export interface DocumentCardProps {
|
export interface DocumentCardProps {
|
||||||
document: LoadedOnyxDocument;
|
document: LoadedOnyxDocument;
|
||||||
@ -39,6 +40,13 @@ export function Citation({
|
|||||||
if (!document_info && !question_info) {
|
if (!document_info && !question_info) {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
const icon = document_info?.document
|
||||||
|
? getFileIconFromFileNameAndLink(
|
||||||
|
document_info.document.semantic_identifier || "",
|
||||||
|
document_info.document.link || ""
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TooltipProvider delayDuration={0}>
|
<TooltipProvider delayDuration={0}>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@ -72,7 +80,7 @@ export function Citation({
|
|||||||
<CompactDocumentCard
|
<CompactDocumentCard
|
||||||
updatePresentingDocument={document_info.updatePresentingDocument}
|
updatePresentingDocument={document_info.updatePresentingDocument}
|
||||||
url={document_info.url}
|
url={document_info.url}
|
||||||
icon={document_info.icon}
|
icon={icon}
|
||||||
document={document_info.document}
|
document={document_info.document}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -112,7 +112,24 @@ export async function fetchChatData(searchParams: {
|
|||||||
? `${fullUrl}?${searchParamsString}`
|
? `${fullUrl}?${searchParamsString}`
|
||||||
: fullUrl;
|
: fullUrl;
|
||||||
|
|
||||||
if (!NEXT_PUBLIC_ENABLE_CHROME_EXTENSION) {
|
// Check the referrer to prevent redirect loops
|
||||||
|
const referrer = headersList.get("referer") || "";
|
||||||
|
const isComingFromLogin = referrer.includes("/auth/login");
|
||||||
|
|
||||||
|
// Also check for the from=login query parameter
|
||||||
|
const isRedirectedFromLogin = searchParams["from"] === "login";
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Auth check: authDisabled=${authDisabled}, user=${!!user}, referrer=${referrer}, fromLogin=${isRedirectedFromLogin}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Only redirect if we're not already coming from the login page
|
||||||
|
if (
|
||||||
|
!NEXT_PUBLIC_ENABLE_CHROME_EXTENSION &&
|
||||||
|
!isComingFromLogin &&
|
||||||
|
!isRedirectedFromLogin
|
||||||
|
) {
|
||||||
|
console.log("Redirecting to login from chat page");
|
||||||
return {
|
return {
|
||||||
redirect: `/auth/login?next=${encodeURIComponent(redirectUrl)}`,
|
redirect: `/auth/login?next=${encodeURIComponent(redirectUrl)}`,
|
||||||
};
|
};
|
||||||
|
@ -77,7 +77,8 @@ export const SERVER_SIDE_ONLY__CLOUD_ENABLED =
|
|||||||
process.env.NEXT_PUBLIC_CLOUD_ENABLED?.toLowerCase() === "true";
|
process.env.NEXT_PUBLIC_CLOUD_ENABLED?.toLowerCase() === "true";
|
||||||
|
|
||||||
export const NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED =
|
export const NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED =
|
||||||
process.env.NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED?.toLowerCase() === "true";
|
process.env.NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED?.toLowerCase() === "true" &&
|
||||||
|
!NEXT_PUBLIC_CLOUD_ENABLED;
|
||||||
|
|
||||||
export const NEXT_PUBLIC_TEST_ENV =
|
export const NEXT_PUBLIC_TEST_ENV =
|
||||||
process.env.NEXT_PUBLIC_TEST_ENV?.toLowerCase() === "true";
|
process.env.NEXT_PUBLIC_TEST_ENV?.toLowerCase() === "true";
|
||||||
|
@ -130,12 +130,10 @@ export async function renameItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function downloadItem(documentId: string): Promise<Blob> {
|
export async function downloadItem(documentId: string): Promise<Blob> {
|
||||||
const response = await fetch(
|
const fileId = documentId.split("__")[1] || documentId;
|
||||||
`/api/chat/file/${encodeURIComponent(documentId)}`,
|
const response = await fetch(`/api/chat/file/${encodeURIComponent(fileId)}`, {
|
||||||
{
|
method: "GET",
|
||||||
method: "GET",
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Failed to fetch file");
|
throw new Error("Failed to fetch file");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user