Python/agent/application/pages/workbench.py

428 lines
14 KiB
Python

# -*- coding: utf-8 -*-
"""
工作台
"""
import reflex as rx
from application.pages import create_task_page, knowledge_base_page, login_page
from application.states import AuthState, TaskState
from application.states.models import TaskHistoryItem
# 导航按钮字典
NAV_BUTTONS: dict = {
"create_task": {"src": "/create_task.svg", "text": "创建任务"},
}
"""
约定:
1 若组件仅用于当前页面则无需抽象为通用组件(application.components.*)
"""
def nav_button(
nav_button: str,
) -> rx.Component:
"""
导航按钮
:param nav_button: 导航按钮
:return : Component
"""
# 导航按钮激活状态
is_actived = nav_button == AuthState.activated_nav_button
return rx.hstack(
# 导航按钮的图标
rx.image(
NAV_BUTTONS[nav_button]["src"],
width="16px",
height="16px",
object_fit="contain",
),
# 导航按钮的文本
rx.text(
NAV_BUTTONS[nav_button]["text"],
line_height="20px",
font_size="var(--prismui-font-size-2)",
font_weight=rx.cond(
is_actived, "--prismui-font-weight-3", None
), # 通过加粗字体区分是否激活
color="var(--prismui-color-1)",
),
align_items="center",
gap="8px",
padding="5px 12px",
background_color=rx.cond(
is_actived, "var(--prismui-background-color-2)", "transparent"
),
cursor="pointer",
# 点击事件:设置激活导航按钮
on_click=AuthState.set_activated_nav_button(nav_button),
)
def task_history_item(
item: TaskHistoryItem,
) -> rx.Component:
"""
任务历史项
:param item: 任务历史项
:return: Component
"""
# 高亮
is_highlight = (item.id == TaskState.popover_shown_task_history_item_id) | (
item.id == TaskState.task_id
)
return rx.list.item(
rx.vstack(
rx.hstack(
# 会话历史项的会话描述
rx.text(
item.description,
flex=1,
height="22px",
line_height="22px",
padding_right="18px",
color="var(--prismui-color-2)",
text_overflow="ellipsis",
white_space="nowrap",
overflow="hidden",
),
display="flex",
align_items="center",
width="100%",
margin_bottom="8px",
),
rx.hstack(
rx.spacer(),
# 会话历史项的会话创建日期时间
rx.text(
item.created_at,
line_height="20px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-8)",
),
justify_content="space-between",
align_items="center",
width="100%",
height="20px",
margin_bottom="8px",
),
# 点击事件:设置激活任务
on_click=lambda: TaskState.set_task(item.id),
width="100%",
),
# 会话历史项的更多按钮
rx.box(
rx.popover.root(
rx.popover.trigger(
rx.box(
rx.icon(
"ellipsis",
width="14px",
height="14px",
color="var(--prismui-color-2)",
),
cursor="pointer",
)
),
rx.popover.content(
rx.vstack(
rx.box(
position="absolute",
top="-6px",
left="50%",
transform="translateX(-50%) rotate(45deg)",
width="8px",
height="8px",
background_color="var(--prismui-background-color-1)",
),
rx.popover.close(
rx.box(
"删除",
# 点击事件:删除任务
on_click=lambda: TaskState.delete_task(item.id),
display="flex",
align_items="center",
justify_content="center",
width="100%",
height="24px",
border_radius="var(--prismui-border-radius-1)",
line_height="16px",
font_family="var(--prismui-font-family)",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-1)",
style={
"_hover": {
"background_color": "var(--prismui-background-color-6)",
}, # 鼠标悬停显示背景颜色
},
cursor="pointer",
),
),
position="relative",
width="100%",
),
side="bottom",
side_offset=8,
position="relative",
align="center",
padding="4px 8px",
border_radius="var(--prismui-border-radius-1)",
box_shadow="var(--prismui-box-shadow-6)",
overflow="visible",
),
# 悬停气泡显示 / 隐藏变化事件:设置任务历史项悬停气泡显示 / 隐藏
on_open_change=lambda is_shown: TaskState.set_task_history_item_popover_shown(
item.id, is_shown
),
open_delay=0,
),
position="absolute",
top="27px",
right="16px",
transform="translateY(-50%)",
z_index="99",
min_width="14px",
cursor="pointer",
opacity=rx.cond(is_highlight, "1", "0"),
transition="opacity 0.18s ease-in-out",
pointer_events=rx.cond(is_highlight, "auto", "none"),
),
display="flex",
position="relative",
align_items="flex-start",
width="100%",
padding="16px",
margin_bottom="8px",
background=rx.cond(
is_highlight,
"var(--prismui-background-3)",
"var(--prismui-background-color-1)",
),
border_radius="var(--prismui-border-radius-3)",
box_shadow=rx.cond(is_highlight, "var(--prismui-box-shadow-7)", "none"),
cursor="pointer",
style={
"&:hover": {
"background": "var(--prismui-background-3)",
"box_shadow": "var(--prismui-box-shadow-7)",
}, # 鼠标悬停背景颜色和阴影
"&:hover > div:last-child": {
"opacity": "1",
"pointer_events": "auto",
}, # 鼠标悬停显示更多按钮
},
)
def task_history() -> rx.Component:
"""
任务历史
:return: rx.Component
"""
return rx.auto_scroll(
rx.foreach(
TaskState.task_history,
task_history_item,
),
flex="1",
align_items="stretch",
width="100%",
margin_top="8px",
style={
"&::-webkit-scrollbar": {
"display": "none",
},
},
overflow="auto",
)
def settings_button():
"""
设置按钮
:return: Component
"""
return rx.box(
rx.hover_card.root(
# 触发事件:点击自定义按钮
rx.hover_card.trigger(
rx.box(
rx.icon(
"settings",
stroke_width=1.5,
width="20px",
height="20px",
color="var(--prismui-color-1)",
),
display="flex",
flex_flow="column",
justify_content="center",
align_items="center",
width="36px",
height="36px",
border_radius="var(--prismui-border-radius-3)",
style={
"&:hover": {
"background": "var(--prismui-background-color-5)",
}, # 鼠标悬停背景颜色
},
cursor="pointer",
)
),
# 悬停卡片
rx.hover_card.content(
rx.box(
rx.box(
transform="translateX(-50%) rotate(45deg)",
position="absolute",
top="calc(50% - 4px)",
left="0",
width="8px",
height="8px",
background_color="var(--prismui-background-color-1)",
),
rx.box(
"退出登录",
display="flex",
justify_content="center",
align_items="center",
width="100%",
height="24px",
line_height="16px",
border_radius="var(--prismui-border-radius-1)",
style={
"_hover": {
"background_color": "var(--prismui-background-color-6)",
},
},
# 点击事件:退出登录
on_click=lambda: AuthState.logout(),
),
cursor="pointer",
),
side="left",
side_offset=8,
position="relative",
align="center",
padding="4px 12px",
border_radius="var(--prismui-border-radius-1)",
box_shadow="var(--prismui-box-shadow-3)",
font_family="var(--prismui-font-family)",
overflow="visible",
style={
"font-size": "var(--prismui-font-size-1) !important",
"color": "var(--prismui-color-1) !important",
},
),
open=AuthState.is_settings_hover_card_open,
# 悬停卡片打开状态变化事件:设置设置悬停卡片打开状态
on_open_change=AuthState.set_settings_hover_card_open,
),
min_width="14px",
cursor="pointer",
)
def sidebar() -> rx.Component:
"""
侧边栏
:return: rx.Component
"""
return rx.vstack(
rx.vstack(
# 渲染系统图标和名称
rx.hstack(
# 系统图标
rx.image(
src="/logo.png",
width="20px",
height="20px",
object_fit="contain",
),
# 系统名称
rx.text(
"棱镜球",
line_height="20px",
font_size="var(--prismui-font-size-2)",
font_weight="var(--prismui-font-weight-4)",
),
align_items="center",
gap="4px",
height="56px",
padding="0 0 12px 12px",
),
# 渲染导航按钮
rx.vstack(
# 渲染导航按钮:创建任务
nav_button("create_task"),
gap="2px",
padding="0 0 16px 0",
),
# 渲染最近任务
rx.vstack(
rx.text(
"最近",
line_height="20px",
font_size="var(--prismui-font-size-2)",
padding="0 0 0 10px",
),
padding="0 12px",
),
align_items="flex-start",
gap="2px",
width="100%",
),
rx.vstack(
rx.vstack(
# 设置按钮
settings_button(),
),
padding_bottom="16px",
gap="8px",
),
flex_shrink="0",
justify_content="space-between",
align_items="flex-start",
width="264px",
height="100%",
padding="0 12px",
background="var(--prismui-background-1)",
)
def workbench() -> rx.Component:
"""
工作台
"""
return rx.fragment(
# 若用户未登录则渲染登录页面
rx.cond(
AuthState.user_id,
rx.fragment(),
login_page(),
),
rx.hstack(
# 侧边栏
sidebar(),
# 默认新建任务页面
rx.match(
AuthState.activated_nav_button,
# 知识库页面
("knowledge_base", knowledge_base_page()),
# 创建任务页面
("create_task", create_task_page()),
# 会话页面
create_task_page(),
),
width="100%",
height="100vh",
overflow="auto",
),
)