From 45397e941d2cc8e8eed78e6aacc66a9ffd56bd5b Mon Sep 17 00:00:00 2001 From: Soulter <37870767+Soulter@users.noreply.github.com> Date: Wed, 14 Jan 2026 19:15:48 +0800 Subject: [PATCH] feat: chatui project (#4477) * feat: chatui-project * fix: remove console log from getProjects function --- astrbot/core/db/__init__.py | 86 +++++- astrbot/core/db/po.py | 65 +++++ astrbot/core/db/sqlite.py | 229 +++++++++++++++- astrbot/dashboard/routes/__init__.py | 2 + astrbot/dashboard/routes/chat.py | 42 ++- astrbot/dashboard/routes/chatui_project.py | 245 ++++++++++++++++++ astrbot/dashboard/server.py | 1 + dashboard/src/components/chat/Chat.vue | 234 ++++++++++++++--- dashboard/src/components/chat/ChatInput.vue | 82 +++--- .../src/components/chat/ConfigSelector.vue | 41 +-- .../components/chat/ConversationSidebar.vue | 26 +- .../src/components/chat/ProjectDialog.vue | 114 ++++++++ dashboard/src/components/chat/ProjectList.vue | 159 ++++++++++++ dashboard/src/components/chat/ProjectView.vue | 186 +++++++++++++ .../src/components/chat/ProviderModelMenu.vue | 7 +- dashboard/src/components/chat/WelcomeView.vue | 144 ++++++++++ dashboard/src/composables/useMessages.ts | 7 + dashboard/src/composables/useProjects.ts | 120 +++++++++ .../src/i18n/locales/en-US/features/chat.json | 15 +- .../src/i18n/locales/zh-CN/features/chat.json | 13 + 20 files changed, 1708 insertions(+), 110 deletions(-) create mode 100644 astrbot/dashboard/routes/chatui_project.py create mode 100644 dashboard/src/components/chat/ProjectDialog.vue create mode 100644 dashboard/src/components/chat/ProjectList.vue create mode 100644 dashboard/src/components/chat/ProjectView.vue create mode 100644 dashboard/src/components/chat/WelcomeView.vue create mode 100644 dashboard/src/composables/useProjects.ts diff --git a/astrbot/core/db/__init__.py b/astrbot/core/db/__init__.py index 3a79e41c2..a0945fc30 100644 --- a/astrbot/core/db/__init__.py +++ b/astrbot/core/db/__init__.py @@ -9,6 +9,7 @@ from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_asyn from astrbot.core.db.po import ( Attachment, + ChatUIProject, CommandConfig, CommandConflict, ConversationV2, @@ -17,6 +18,7 @@ from astrbot.core.db.po import ( PlatformSession, PlatformStat, Preference, + SessionProjectRelation, Stats, ) @@ -446,8 +448,11 @@ class BaseDatabase(abc.ABC): platform_id: str | None = None, page: int = 1, page_size: int = 20, - ) -> list[PlatformSession]: - """Get all Platform sessions for a specific creator (username) and optionally platform.""" + ) -> list[dict]: + """Get all Platform sessions for a specific creator (username) and optionally platform. + + Returns a list of dicts containing session info and project info (if session belongs to a project). + """ ... @abc.abstractmethod @@ -463,3 +468,80 @@ class BaseDatabase(abc.ABC): async def delete_platform_session(self, session_id: str) -> None: """Delete a Platform session by its ID.""" ... + + # ==== + # ChatUI Project Management + # ==== + + @abc.abstractmethod + async def create_chatui_project( + self, + creator: str, + title: str, + emoji: str | None = "📁", + description: str | None = None, + ) -> ChatUIProject: + """Create a new ChatUI project.""" + ... + + @abc.abstractmethod + async def get_chatui_project_by_id(self, project_id: str) -> ChatUIProject | None: + """Get a ChatUI project by its ID.""" + ... + + @abc.abstractmethod + async def get_chatui_projects_by_creator( + self, + creator: str, + page: int = 1, + page_size: int = 100, + ) -> list[ChatUIProject]: + """Get all ChatUI projects for a specific creator.""" + ... + + @abc.abstractmethod + async def update_chatui_project( + self, + project_id: str, + title: str | None = None, + emoji: str | None = None, + description: str | None = None, + ) -> None: + """Update a ChatUI project.""" + ... + + @abc.abstractmethod + async def delete_chatui_project(self, project_id: str) -> None: + """Delete a ChatUI project by its ID.""" + ... + + @abc.abstractmethod + async def add_session_to_project( + self, + session_id: str, + project_id: str, + ) -> SessionProjectRelation: + """Add a session to a project.""" + ... + + @abc.abstractmethod + async def remove_session_from_project(self, session_id: str) -> None: + """Remove a session from its project.""" + ... + + @abc.abstractmethod + async def get_project_sessions( + self, + project_id: str, + page: int = 1, + page_size: int = 100, + ) -> list[PlatformSession]: + """Get all sessions in a project.""" + ... + + @abc.abstractmethod + async def get_project_by_session( + self, session_id: str, creator: str + ) -> ChatUIProject | None: + """Get the project that a session belongs to.""" + ... diff --git a/astrbot/core/db/po.py b/astrbot/core/db/po.py index fdbf4aff3..882c0b3c5 100644 --- a/astrbot/core/db/po.py +++ b/astrbot/core/db/po.py @@ -239,6 +239,71 @@ class Attachment(SQLModel, table=True): ) +class ChatUIProject(SQLModel, table=True): + """This class represents projects for organizing ChatUI conversations. + + Projects allow users to group related conversations together. + """ + + __tablename__: str = "chatui_projects" + + inner_id: int | None = Field( + primary_key=True, + sa_column_kwargs={"autoincrement": True}, + default=None, + ) + project_id: str = Field( + max_length=36, + nullable=False, + unique=True, + default_factory=lambda: str(uuid.uuid4()), + ) + creator: str = Field(nullable=False) + """Username of the project creator""" + emoji: str | None = Field(default="📁", max_length=10) + """Emoji icon for the project""" + title: str = Field(nullable=False, max_length=255) + """Title of the project""" + description: str | None = Field(default=None, max_length=1000) + """Description of the project""" + created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) + updated_at: datetime = Field( + default_factory=lambda: datetime.now(timezone.utc), + sa_column_kwargs={"onupdate": datetime.now(timezone.utc)}, + ) + + __table_args__ = ( + UniqueConstraint( + "project_id", + name="uix_chatui_project_id", + ), + ) + + +class SessionProjectRelation(SQLModel, table=True): + """This class represents the relationship between platform sessions and ChatUI projects.""" + + __tablename__: str = "session_project_relations" + + id: int | None = Field( + primary_key=True, + sa_column_kwargs={"autoincrement": True}, + default=None, + ) + session_id: str = Field(nullable=False, max_length=100) + """Session ID from PlatformSession""" + project_id: str = Field(nullable=False, max_length=36) + """Project ID from ChatUIProject""" + created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) + + __table_args__ = ( + UniqueConstraint( + "session_id", + name="uix_session_project_relation", + ), + ) + + class CommandConfig(SQLModel, table=True): """Per-command configuration overrides for dashboard management.""" diff --git a/astrbot/core/db/sqlite.py b/astrbot/core/db/sqlite.py index 7422a5cc2..b1354c012 100644 --- a/astrbot/core/db/sqlite.py +++ b/astrbot/core/db/sqlite.py @@ -11,6 +11,7 @@ from sqlmodel import col, delete, desc, func, or_, select, text, update from astrbot.core.db import BaseDatabase from astrbot.core.db.po import ( Attachment, + ChatUIProject, CommandConfig, CommandConflict, ConversationV2, @@ -19,6 +20,7 @@ from astrbot.core.db.po import ( PlatformSession, PlatformStat, Preference, + SessionProjectRelation, SQLModel, ) from astrbot.core.db.po import ( @@ -1060,12 +1062,35 @@ class SQLiteDatabase(BaseDatabase): platform_id: str | None = None, page: int = 1, page_size: int = 20, - ) -> list[PlatformSession]: - """Get all Platform sessions for a specific creator (username) and optionally platform.""" + ) -> list[dict]: + """Get all Platform sessions for a specific creator (username) and optionally platform. + + Returns a list of dicts containing session info and project info (if session belongs to a project). + """ async with self.get_db() as session: session: AsyncSession offset = (page - 1) * page_size - query = select(PlatformSession).where(PlatformSession.creator == creator) + + # LEFT JOIN with SessionProjectRelation and ChatUIProject to get project info + query = ( + select( + PlatformSession, + col(ChatUIProject.project_id), + col(ChatUIProject.title).label("project_title"), + col(ChatUIProject.emoji).label("project_emoji"), + ) + .outerjoin( + SessionProjectRelation, + col(PlatformSession.session_id) + == col(SessionProjectRelation.session_id), + ) + .outerjoin( + ChatUIProject, + col(SessionProjectRelation.project_id) + == col(ChatUIProject.project_id), + ) + .where(col(PlatformSession.creator) == creator) + ) if platform_id: query = query.where(PlatformSession.platform_id == platform_id) @@ -1076,7 +1101,24 @@ class SQLiteDatabase(BaseDatabase): .limit(page_size) ) result = await session.execute(query) - return list(result.scalars().all()) + + # Convert to list of dicts with session and project info + sessions_with_projects = [] + for row in result.all(): + platform_session = row[0] + project_id = row[1] + project_title = row[2] + project_emoji = row[3] + + session_dict = { + "session": platform_session, + "project_id": project_id, + "project_title": project_title, + "project_emoji": project_emoji, + } + sessions_with_projects.append(session_dict) + + return sessions_with_projects async def update_platform_session( self, @@ -1107,3 +1149,182 @@ class SQLiteDatabase(BaseDatabase): col(PlatformSession.session_id) == session_id, ), ) + + # ==== + # ChatUI Project Management + # ==== + + async def create_chatui_project( + self, + creator: str, + title: str, + emoji: str | None = "📁", + description: str | None = None, + ) -> ChatUIProject: + """Create a new ChatUI project.""" + async with self.get_db() as session: + session: AsyncSession + async with session.begin(): + project = ChatUIProject( + creator=creator, + title=title, + emoji=emoji, + description=description, + ) + session.add(project) + await session.flush() + await session.refresh(project) + return project + + async def get_chatui_project_by_id(self, project_id: str) -> ChatUIProject | None: + """Get a ChatUI project by its ID.""" + async with self.get_db() as session: + session: AsyncSession + result = await session.execute( + select(ChatUIProject).where( + col(ChatUIProject.project_id) == project_id, + ), + ) + return result.scalar_one_or_none() + + async def get_chatui_projects_by_creator( + self, + creator: str, + page: int = 1, + page_size: int = 100, + ) -> list[ChatUIProject]: + """Get all ChatUI projects for a specific creator.""" + async with self.get_db() as session: + session: AsyncSession + offset = (page - 1) * page_size + result = await session.execute( + select(ChatUIProject) + .where(col(ChatUIProject.creator) == creator) + .order_by(desc(ChatUIProject.updated_at)) + .limit(page_size) + .offset(offset), + ) + return list(result.scalars().all()) + + async def update_chatui_project( + self, + project_id: str, + title: str | None = None, + emoji: str | None = None, + description: str | None = None, + ) -> None: + """Update a ChatUI project.""" + async with self.get_db() as session: + session: AsyncSession + async with session.begin(): + values: dict[str, T.Any] = {"updated_at": datetime.now(timezone.utc)} + if title is not None: + values["title"] = title + if emoji is not None: + values["emoji"] = emoji + if description is not None: + values["description"] = description + + await session.execute( + update(ChatUIProject) + .where(col(ChatUIProject.project_id) == project_id) + .values(**values), + ) + + async def delete_chatui_project(self, project_id: str) -> None: + """Delete a ChatUI project by its ID.""" + async with self.get_db() as session: + session: AsyncSession + async with session.begin(): + # First remove all session relations + await session.execute( + delete(SessionProjectRelation).where( + col(SessionProjectRelation.project_id) == project_id, + ), + ) + # Then delete the project + await session.execute( + delete(ChatUIProject).where( + col(ChatUIProject.project_id) == project_id, + ), + ) + + async def add_session_to_project( + self, + session_id: str, + project_id: str, + ) -> SessionProjectRelation: + """Add a session to a project.""" + async with self.get_db() as session: + session: AsyncSession + async with session.begin(): + # First remove existing relation if any + await session.execute( + delete(SessionProjectRelation).where( + col(SessionProjectRelation.session_id) == session_id, + ), + ) + # Then create new relation + relation = SessionProjectRelation( + session_id=session_id, + project_id=project_id, + ) + session.add(relation) + await session.flush() + await session.refresh(relation) + return relation + + async def remove_session_from_project(self, session_id: str) -> None: + """Remove a session from its project.""" + async with self.get_db() as session: + session: AsyncSession + async with session.begin(): + await session.execute( + delete(SessionProjectRelation).where( + col(SessionProjectRelation.session_id) == session_id, + ), + ) + + async def get_project_sessions( + self, + project_id: str, + page: int = 1, + page_size: int = 100, + ) -> list[PlatformSession]: + """Get all sessions in a project.""" + async with self.get_db() as session: + session: AsyncSession + offset = (page - 1) * page_size + result = await session.execute( + select(PlatformSession) + .join( + SessionProjectRelation, + col(PlatformSession.session_id) + == col(SessionProjectRelation.session_id), + ) + .where(col(SessionProjectRelation.project_id) == project_id) + .order_by(desc(PlatformSession.updated_at)) + .limit(page_size) + .offset(offset), + ) + return list(result.scalars().all()) + + async def get_project_by_session( + self, session_id: str, creator: str + ) -> ChatUIProject | None: + """Get the project that a session belongs to.""" + async with self.get_db() as session: + session: AsyncSession + result = await session.execute( + select(ChatUIProject) + .join( + SessionProjectRelation, + col(ChatUIProject.project_id) + == col(SessionProjectRelation.project_id), + ) + .where( + col(SessionProjectRelation.session_id) == session_id, + col(ChatUIProject.creator) == creator, + ), + ) + return result.scalar_one_or_none() diff --git a/astrbot/dashboard/routes/__init__.py b/astrbot/dashboard/routes/__init__.py index bca1a2268..908bbfcc3 100644 --- a/astrbot/dashboard/routes/__init__.py +++ b/astrbot/dashboard/routes/__init__.py @@ -1,6 +1,7 @@ from .auth import AuthRoute from .backup import BackupRoute from .chat import ChatRoute +from .chatui_project import ChatUIProjectRoute from .command import CommandRoute from .config import ConfigRoute from .conversation import ConversationRoute @@ -20,6 +21,7 @@ __all__ = [ "AuthRoute", "BackupRoute", "ChatRoute", + "ChatUIProjectRoute", "CommandRoute", "ConfigRoute", "ConversationRoute", diff --git a/astrbot/dashboard/routes/chat.py b/astrbot/dashboard/routes/chat.py index 6ee589316..c42bc4f64 100644 --- a/astrbot/dashboard/routes/chat.py +++ b/astrbot/dashboard/routes/chat.py @@ -618,9 +618,17 @@ class ChatRoute(Route): page_size=100, # 暂时返回前100个 ) - # 转换为字典格式,并添加额外信息 + # 转换为字典格式,并添加项目信息 + # get_platform_sessions_by_creator 现在返回 list[dict] 包含 session 和项目字段 sessions_data = [] - for session in sessions: + for item in sessions: + session = item["session"] + project_id = item["project_id"] + + # 跳过属于项目的会话(在侧边栏对话列表中不显示) + if project_id is not None: + continue + sessions_data.append( { "session_id": session.session_id, @@ -645,6 +653,12 @@ class ChatRoute(Route): session = await self.db.get_platform_session_by_id(session_id) platform_id = session.platform_id if session else "webchat" + # 获取项目信息(如果会话属于某个项目) + username = g.get("username", "guest") + project_info = await self.db.get_project_by_session( + session_id=session_id, creator=username + ) + # Get platform message history using session_id history_ls = await self.platform_history_mgr.get( platform_id=platform_id, @@ -655,16 +669,20 @@ class ChatRoute(Route): history_res = [history.model_dump() for history in history_ls] - return ( - Response() - .ok( - data={ - "history": history_res, - "is_running": self.running_convs.get(session_id, False), - }, - ) - .__dict__ - ) + response_data = { + "history": history_res, + "is_running": self.running_convs.get(session_id, False), + } + + # 如果会话属于项目,添加项目信息 + if project_info: + response_data["project"] = { + "project_id": project_info.project_id, + "title": project_info.title, + "emoji": project_info.emoji, + } + + return Response().ok(data=response_data).__dict__ async def update_session_display_name(self): """Update a Platform session's display name.""" diff --git a/astrbot/dashboard/routes/chatui_project.py b/astrbot/dashboard/routes/chatui_project.py new file mode 100644 index 000000000..5a66dafd3 --- /dev/null +++ b/astrbot/dashboard/routes/chatui_project.py @@ -0,0 +1,245 @@ +from quart import g, request + +from astrbot.core.db import BaseDatabase + +from .route import Response, Route, RouteContext + + +class ChatUIProjectRoute(Route): + def __init__(self, context: RouteContext, db: BaseDatabase) -> None: + super().__init__(context) + self.routes = { + "/chatui_project/create": ("POST", self.create_project), + "/chatui_project/list": ("GET", self.list_projects), + "/chatui_project/get": ("GET", self.get_project), + "/chatui_project/update": ("POST", self.update_chatui_project), + "/chatui_project/delete": ("GET", self.delete_project), + "/chatui_project/add_session": ("POST", self.add_session_to_project), + "/chatui_project/remove_session": ( + "POST", + self.remove_session_from_project, + ), + "/chatui_project/get_sessions": ("GET", self.get_project_sessions), + } + self.db = db + self.register_routes() + + async def create_project(self): + """Create a new ChatUI project.""" + username = g.get("username", "guest") + post_data = await request.json + + title = post_data.get("title") + emoji = post_data.get("emoji", "📁") + description = post_data.get("description") + + if not title: + return Response().error("Missing key: title").__dict__ + + project = await self.db.create_chatui_project( + creator=username, + title=title, + emoji=emoji, + description=description, + ) + + return ( + Response() + .ok( + data={ + "project_id": project.project_id, + "title": project.title, + "emoji": project.emoji, + "description": project.description, + "created_at": project.created_at.astimezone().isoformat(), + "updated_at": project.updated_at.astimezone().isoformat(), + } + ) + .__dict__ + ) + + async def list_projects(self): + """Get all ChatUI projects for the current user.""" + username = g.get("username", "guest") + + projects = await self.db.get_chatui_projects_by_creator(creator=username) + + projects_data = [ + { + "project_id": project.project_id, + "title": project.title, + "emoji": project.emoji, + "description": project.description, + "created_at": project.created_at.astimezone().isoformat(), + "updated_at": project.updated_at.astimezone().isoformat(), + } + for project in projects + ] + + return Response().ok(data=projects_data).__dict__ + + async def get_project(self): + """Get a specific ChatUI project.""" + project_id = request.args.get("project_id") + if not project_id: + return Response().error("Missing key: project_id").__dict__ + + username = g.get("username", "guest") + + project = await self.db.get_chatui_project_by_id(project_id) + if not project: + return Response().error(f"Project {project_id} not found").__dict__ + + # Verify ownership + if project.creator != username: + return Response().error("Permission denied").__dict__ + + return ( + Response() + .ok( + data={ + "project_id": project.project_id, + "title": project.title, + "emoji": project.emoji, + "description": project.description, + "created_at": project.created_at.astimezone().isoformat(), + "updated_at": project.updated_at.astimezone().isoformat(), + } + ) + .__dict__ + ) + + async def update_chatui_project(self): + """Update a ChatUI project.""" + post_data = await request.json + + project_id = post_data.get("project_id") + title = post_data.get("title") + emoji = post_data.get("emoji") + description = post_data.get("description") + + if not project_id: + return Response().error("Missing key: project_id").__dict__ + + username = g.get("username", "guest") + + # Verify ownership + project = await self.db.get_chatui_project_by_id(project_id) + if not project: + return Response().error(f"Project {project_id} not found").__dict__ + if project.creator != username: + return Response().error("Permission denied").__dict__ + + await self.db.update_chatui_project( + project_id=project_id, + title=title, + emoji=emoji, + description=description, + ) + + return Response().ok().__dict__ + + async def delete_project(self): + """Delete a ChatUI project.""" + project_id = request.args.get("project_id") + if not project_id: + return Response().error("Missing key: project_id").__dict__ + + username = g.get("username", "guest") + + # Verify ownership + project = await self.db.get_chatui_project_by_id(project_id) + if not project: + return Response().error(f"Project {project_id} not found").__dict__ + if project.creator != username: + return Response().error("Permission denied").__dict__ + + await self.db.delete_chatui_project(project_id) + + return Response().ok().__dict__ + + async def add_session_to_project(self): + """Add a session to a project.""" + post_data = await request.json + + session_id = post_data.get("session_id") + project_id = post_data.get("project_id") + + if not session_id: + return Response().error("Missing key: session_id").__dict__ + if not project_id: + return Response().error("Missing key: project_id").__dict__ + + username = g.get("username", "guest") + + # Verify project ownership + project = await self.db.get_chatui_project_by_id(project_id) + if not project: + return Response().error(f"Project {project_id} not found").__dict__ + if project.creator != username: + return Response().error("Permission denied").__dict__ + + # Verify session ownership + session = await self.db.get_platform_session_by_id(session_id) + if not session: + return Response().error(f"Session {session_id} not found").__dict__ + if session.creator != username: + return Response().error("Permission denied").__dict__ + + await self.db.add_session_to_project(session_id, project_id) + + return Response().ok().__dict__ + + async def remove_session_from_project(self): + """Remove a session from its project.""" + post_data = await request.json + + session_id = post_data.get("session_id") + + if not session_id: + return Response().error("Missing key: session_id").__dict__ + + username = g.get("username", "guest") + + # Verify session ownership + session = await self.db.get_platform_session_by_id(session_id) + if not session: + return Response().error(f"Session {session_id} not found").__dict__ + if session.creator != username: + return Response().error("Permission denied").__dict__ + + await self.db.remove_session_from_project(session_id) + + return Response().ok().__dict__ + + async def get_project_sessions(self): + """Get all sessions in a project.""" + project_id = request.args.get("project_id") + if not project_id: + return Response().error("Missing key: project_id").__dict__ + + username = g.get("username", "guest") + + # Verify project ownership + project = await self.db.get_chatui_project_by_id(project_id) + if not project: + return Response().error(f"Project {project_id} not found").__dict__ + if project.creator != username: + return Response().error("Permission denied").__dict__ + + sessions = await self.db.get_project_sessions(project_id) + + sessions_data = [ + { + "session_id": session.session_id, + "platform_id": session.platform_id, + "creator": session.creator, + "display_name": session.display_name, + "is_group": session.is_group, + "created_at": session.created_at.astimezone().isoformat(), + "updated_at": session.updated_at.astimezone().isoformat(), + } + for session in sessions + ] + + return Response().ok(data=sessions_data).__dict__ diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index ad83c4886..afac7fedb 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -74,6 +74,7 @@ class AstrBotDashboard: self.sfr = StaticFileRoute(self.context) self.ar = AuthRoute(self.context) self.chat_route = ChatRoute(self.context, db, core_lifecycle) + self.chatui_project_route = ChatUIProjectRoute(self.context, db) self.tools_root = ToolsRoute(self.context, core_lifecycle) self.conversation_route = ConversationRoute(self.context, db, core_lifecycle) self.file_route = FileRoute(self.context) diff --git a/dashboard/src/components/chat/Chat.vue b/dashboard/src/components/chat/Chat.vue index ad56a17fc..c9261bf61 100644 --- a/dashboard/src/components/chat/Chat.vue +++ b/dashboard/src/components/chat/Chat.vue @@ -9,10 +9,12 @@ :sessions="sessions" :selectedSessions="selectedSessions" :currSessionId="currSessionId" + :selectedProjectId="selectedProjectId" :isDark="isDark" :chatboxMode="chatboxMode" :isMobile="isMobile" :mobileMenuOpen="mobileMenuOpen" + :projects="projects" @newChat="handleNewChat" @selectConversation="handleSelectConversation" @editTitle="showEditTitleDialog" @@ -20,6 +22,10 @@ @closeMobileSidebar="closeMobileSidebar" @toggleTheme="toggleTheme" @toggleFullscreen="toggleFullscreen" + @selectProject="handleSelectProject" + @createProject="showCreateProjectDialog" + @editProject="showEditProjectDialog" + @deleteProject="handleDeleteProject" /> @@ -32,7 +38,17 @@ -
+ + + +
-
-
- -
-
- Hello, I'm - AstrBot ⭐ -
-
+ + + + + + + + + diff --git a/dashboard/src/components/chat/ProjectList.vue b/dashboard/src/components/chat/ProjectList.vue new file mode 100644 index 000000000..71d0fd87c --- /dev/null +++ b/dashboard/src/components/chat/ProjectList.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/dashboard/src/components/chat/ProjectView.vue b/dashboard/src/components/chat/ProjectView.vue new file mode 100644 index 000000000..87a22ec9e --- /dev/null +++ b/dashboard/src/components/chat/ProjectView.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/dashboard/src/components/chat/ProviderModelMenu.vue b/dashboard/src/components/chat/ProviderModelMenu.vue index 98345d3ba..946fba3f8 100644 --- a/dashboard/src/components/chat/ProviderModelMenu.vue +++ b/dashboard/src/components/chat/ProviderModelMenu.vue @@ -1,7 +1,7 @@