799 lines
27 KiB
Python
799 lines
27 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
会话页面
|
||
"""
|
||
import reflex as rx
|
||
|
||
from application.models import Conversation, Dialog, ThoughtNode
|
||
from application.states import (
|
||
ConversationHistoryState,
|
||
ConversationState,
|
||
CreateConversationState,
|
||
)
|
||
|
||
|
||
def render_conversation_history_item(
|
||
conversation_id: str, conversation: Conversation
|
||
) -> rx.Component:
|
||
"""
|
||
渲染会话历史条目
|
||
:param conversation_id: 会话唯一标识
|
||
:param conversation: 会话实例
|
||
:return: Component
|
||
"""
|
||
|
||
# 会话激活状态
|
||
is_actived = conversation_id == ConversationState.conversation_id
|
||
|
||
return rx.box(
|
||
rx.hstack(
|
||
# 渲染对话描述
|
||
rx.text(
|
||
conversation.description,
|
||
flex=1,
|
||
height="22px",
|
||
line_height="22px",
|
||
font_size="var(--devui-font-size)",
|
||
padding_right="4px",
|
||
overflow="hidden",
|
||
text_overflow="ellipsis",
|
||
white_space="nowrap",
|
||
),
|
||
# 渲染更多按钮
|
||
rx.box(
|
||
rx.popover.root(
|
||
# 触发事件:点击更多按钮
|
||
rx.popover.trigger(
|
||
rx.box(
|
||
rx.icon(
|
||
"ellipsis", size=14, color="var(--devui-text-weak)"
|
||
),
|
||
cursor="pointer",
|
||
)
|
||
),
|
||
# 渲染气泡卡片
|
||
rx.popover.content(
|
||
rx.vstack(
|
||
rx.box(
|
||
position="absolute",
|
||
top="-12px",
|
||
left="50%",
|
||
transform="translateX(-50%) rotate(45deg)",
|
||
width="8px",
|
||
height="8px",
|
||
background_color="#ffffff",
|
||
box_shadow="-2px -2px 4px rgba(0,0,0,0.05)",
|
||
),
|
||
rx.popover.close(
|
||
rx.box(
|
||
"删除",
|
||
width="100%",
|
||
height="24px",
|
||
padding="4px",
|
||
border_radius="4px",
|
||
line_height="16px",
|
||
font_family="HuaweiFont,Helvetica,Arial,PingFangSC-Regular,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Microsoft JhengHei",
|
||
font_size="12px",
|
||
color="#252b3a",
|
||
style={
|
||
"_hover": {
|
||
"background_color": "#f2f2f3",
|
||
}
|
||
},
|
||
# 点击事件:删除对话
|
||
on_click=lambda: ConversationState.delete_conversation(
|
||
conversation_id
|
||
),
|
||
)
|
||
),
|
||
position="relative",
|
||
width="100%",
|
||
),
|
||
position="relative",
|
||
align="center",
|
||
padding="8px",
|
||
border_radius="4px",
|
||
background_color="#ffffff",
|
||
box_shadow="0 2px 12px rgba(0,0,0,0.1)",
|
||
overflow="visible",
|
||
side="bottom",
|
||
side_offset=9,
|
||
),
|
||
open_delay=0,
|
||
),
|
||
min_width="14px",
|
||
cursor="pointer",
|
||
style={
|
||
"opacity": rx.cond(
|
||
is_actived, "1", "0"
|
||
), # 若当前会话已激活则不透明,否则透明
|
||
"pointer_events": rx.cond(
|
||
is_actived, "auto", "none"
|
||
), # 若当前会话已激活则可点击,否则不可点击
|
||
"transition": "opacity 0.18s ease",
|
||
},
|
||
),
|
||
display="flex",
|
||
align_items="center",
|
||
width="100%",
|
||
margin_bottom="8px",
|
||
),
|
||
width="100%",
|
||
padding="16px",
|
||
margin_bottom="8px",
|
||
border_radius="8px",
|
||
line_height="1.5",
|
||
color="var(--devui-text-weak)",
|
||
cursor="pointer",
|
||
style={
|
||
"background": rx.cond(
|
||
is_actived,
|
||
"linear-gradient(to right, #f3efff, #f3efff33, #e2f1fd33, #e2f1fd)",
|
||
"var(--devui-base-bg)",
|
||
),
|
||
"box_shadow": rx.cond(is_actived, "2px 2px 8px #e9e9e9", "none"),
|
||
"&:hover": {
|
||
"background": "linear-gradient(to right, #f3efff, #f3efff33, #e2f1fd33, #e2f1fd)",
|
||
"box_shadow": "2px 2px 8px #e9e9e9",
|
||
}, # 鼠标悬停渲染背景颜色和阴影
|
||
"&:hover > div > div:last-child": {
|
||
"opacity": "1 !important",
|
||
"pointer_events": "auto !important",
|
||
}, # 鼠标悬停强制显示右侧三点按钮
|
||
},
|
||
# 点击事件:将指定对话唯一标识设置为当前对话唯一标识
|
||
on_click=ConversationState.switch_conversation(conversation_id),
|
||
)
|
||
|
||
|
||
def render_conversation_history_collapse(is_collapse_expanded: bool) -> rx.Component:
|
||
"""
|
||
渲染对话历史折叠面板
|
||
:param is_collapse_expanded: 对话历史折叠面板展开状态
|
||
:return: Component
|
||
"""
|
||
return rx.box(
|
||
rx.vstack(
|
||
# 渲染标题
|
||
rx.hstack(
|
||
rx.text(
|
||
"会话历史",
|
||
margin_bottom="8px",
|
||
font_size="var(--devui-font-size-lg)",
|
||
font_weight="700",
|
||
white_space="nowrap",
|
||
),
|
||
display="flex",
|
||
align_items="center",
|
||
justify_content="space-between",
|
||
),
|
||
# 渲染列表
|
||
rx.auto_scroll(
|
||
rx.foreach(
|
||
ConversationState.conversation_history,
|
||
lambda item, _: render_conversation_history_item(item[0], item[1]),
|
||
),
|
||
flex="1",
|
||
margin_top="8px",
|
||
width="100%",
|
||
align_items="stretch",
|
||
overflow="auto",
|
||
style={
|
||
"&::-webkit-scrollbar": {
|
||
"display": "none",
|
||
},
|
||
},
|
||
),
|
||
display="flex",
|
||
flex_direction="column",
|
||
height="100%",
|
||
padding="12px",
|
||
background_color="#f9f9f9cc",
|
||
backdrop_filter="blur(50px)",
|
||
gap="12px",
|
||
color="var(--prismui-color-text)",
|
||
),
|
||
width=rx.cond(is_collapse_expanded, "25%", "0px"),
|
||
min_width=rx.cond(is_collapse_expanded, "240px", "0px"),
|
||
max_width=rx.cond(is_collapse_expanded, "380px", "0px"),
|
||
height="100%",
|
||
overflow="hidden",
|
||
transition="all 0.3s ease-in-out",
|
||
)
|
||
|
||
|
||
def render_greeting() -> rx.Component:
|
||
"""
|
||
渲染问候
|
||
:return: Component
|
||
"""
|
||
return rx.vstack(
|
||
rx.vstack(
|
||
# 品牌图标、名称和介绍
|
||
rx.vstack(
|
||
rx.hstack(
|
||
rx.image(
|
||
src="/logo.png",
|
||
width="64px",
|
||
height="64px",
|
||
object_fit="contain",
|
||
),
|
||
rx.box(
|
||
"棱镜球",
|
||
font_size="32px",
|
||
font_weight="700",
|
||
letter_spacing="1px",
|
||
),
|
||
display="flex",
|
||
align_items="center",
|
||
gap="8px",
|
||
),
|
||
rx.vstack(
|
||
rx.text(
|
||
"产品汪的棱镜球,精准捕捉各类业务需求",
|
||
width="100%",
|
||
text_align="center",
|
||
),
|
||
rx.text(
|
||
"可帮助检索业务资料,协助生成产品方案、输出 PRD 、流程图和原型图等",
|
||
width="100%",
|
||
text_align="center",
|
||
),
|
||
width="100%",
|
||
line_height="1.5",
|
||
font_size="var(--devui-font-size)",
|
||
),
|
||
display="flex",
|
||
flex_direction="column",
|
||
width="100%",
|
||
align_items="center",
|
||
gap="12px",
|
||
color="var(--prismui-color-text)",
|
||
),
|
||
# 猜你想问
|
||
rx.vstack(
|
||
rx.hstack(
|
||
rx.text(
|
||
"猜你想问",
|
||
line_height="24px",
|
||
font_size="16px",
|
||
font_weight="700",
|
||
),
|
||
display="flex",
|
||
width="100%",
|
||
justify_content="space-between",
|
||
align_items="center",
|
||
margin_bottom="16px",
|
||
),
|
||
rx.hstack(
|
||
rx.box(
|
||
"需求梳理",
|
||
padding="10px 16px",
|
||
background_color="var(--devui-dividing-line)",
|
||
border_radius="var(--devui-border-radius-full)",
|
||
font_size="var(--devui-font-size)",
|
||
color="var(--devui-aide-text)",
|
||
cursor="pointer",
|
||
),
|
||
display="flex",
|
||
flex_wrap="wrap",
|
||
align_items="center",
|
||
gap="12px",
|
||
),
|
||
width="100%",
|
||
padding="24px",
|
||
background_color="var(--devui-base-bg)",
|
||
border_radius="24px",
|
||
),
|
||
display="flex",
|
||
flex_direction="column",
|
||
width="100%",
|
||
min_height="0",
|
||
margin="auto 0",
|
||
align_items="center",
|
||
gap="24px",
|
||
color="var(--prismui-color-text)",
|
||
),
|
||
display="flex",
|
||
flex="1",
|
||
flex_direction="column",
|
||
width="100%",
|
||
max_width="1200px",
|
||
padding="0 12px",
|
||
justify_content="flex-start",
|
||
gap="24px",
|
||
overflow="auto",
|
||
style={
|
||
"&::-webkit-scrollbar": {
|
||
"display": "none",
|
||
},
|
||
},
|
||
)
|
||
|
||
|
||
def render_thought_node(thought_node_id: int, thought_node: ThoughtNode):
|
||
"""
|
||
渲染思考节点
|
||
:param thought_node_id: 思考节点唯一标识
|
||
:param thought_node: 思考节点实例
|
||
:return: Component
|
||
"""
|
||
return rx.vstack(
|
||
rx.match(
|
||
thought_node.kind,
|
||
(
|
||
"thinking",
|
||
rx.markdown(
|
||
thought_node.content,
|
||
component_map={
|
||
"p": lambda text: rx.text(
|
||
text,
|
||
color="var(--prismui-color-text-light)",
|
||
margin="0 0 8px",
|
||
font_size="var(--prismui-font-size-sm)",
|
||
)
|
||
},
|
||
),
|
||
),
|
||
rx.fragment(),
|
||
),
|
||
align_items="flex-start",
|
||
width="100%",
|
||
key=thought_node_id,
|
||
)
|
||
|
||
|
||
def render_dialog_history_item(dialog_id: str, dialog: Dialog) -> rx.Component:
|
||
"""
|
||
渲染对话历史条目
|
||
:param dialog_id: 对话唯一标识
|
||
:param dialog: 对话实例
|
||
:return: Component
|
||
"""
|
||
# 推理状态
|
||
is_thinking = dialog.is_thinking
|
||
# 思考折叠面板展开状态
|
||
is_collapse_expanded = dialog.is_collapse_expanded
|
||
# 思考节点
|
||
thought_nodes = dialog.thought_nodes
|
||
|
||
return rx.vstack(
|
||
# 渲染问题和操作栏
|
||
rx.hstack(
|
||
rx.spacer(),
|
||
rx.vstack(
|
||
# 渲染问题
|
||
rx.text(
|
||
dialog.question,
|
||
max_width="600px",
|
||
padding="12px 16px",
|
||
background_color="var(--prismui-background-color-3)",
|
||
border_radius="12px",
|
||
word_wrap="break-word",
|
||
word_break="break-all",
|
||
white_space="pre-line",
|
||
color="var(--prismui-color-text)",
|
||
),
|
||
# 操作栏
|
||
rx.hstack(
|
||
rx.box(
|
||
rx.icon(
|
||
"rotate-ccw",
|
||
width="14px",
|
||
height="14px",
|
||
color="var(--prismui-color-2)",
|
||
),
|
||
padding="4px",
|
||
border_radius="4px",
|
||
cursor="pointer",
|
||
style={
|
||
"_hover": {
|
||
"background_color": "var(--prismui-background-color-2)",
|
||
}
|
||
},
|
||
),
|
||
width="100%",
|
||
margin_top="8px",
|
||
),
|
||
),
|
||
width="100%",
|
||
margin_top="8px",
|
||
align_items="flex-start",
|
||
gap="4px",
|
||
font_size="var(--devui-font-size)",
|
||
),
|
||
# 渲染思考折叠面板
|
||
rx.vstack(
|
||
# 标题栏
|
||
rx.box(
|
||
rx.hstack(
|
||
rx.text(
|
||
rx.cond(is_thinking, "思考中", "思考完成"),
|
||
color="var(--prismui-color-3)",
|
||
font_size="var(--prismui-font-size-2)",
|
||
),
|
||
rx.icon(
|
||
"chevron-right",
|
||
width="14px",
|
||
height="14px",
|
||
color="var(--prismui-color-3)",
|
||
style={
|
||
"transition": "transform 0.2s ease-in-out",
|
||
"transform": rx.cond(
|
||
is_collapse_expanded, "rotate(90deg)", "rotate(0deg)"
|
||
),
|
||
},
|
||
),
|
||
align_items="center",
|
||
gap="4px",
|
||
margin_bottom="8px",
|
||
line_height="22px",
|
||
),
|
||
cursor="pointer",
|
||
# 点击事件,展开/折叠思考折叠面板
|
||
on_click=lambda: ConversationState.toggle_collapse(dialog_id),
|
||
),
|
||
rx.cond(
|
||
is_collapse_expanded,
|
||
rx.auto_scroll(
|
||
rx.foreach(
|
||
thought_nodes,
|
||
lambda i, _: render_thought_node(i[0], i[1]),
|
||
),
|
||
width="100%",
|
||
),
|
||
rx.fragment(),
|
||
),
|
||
text_align="left",
|
||
width="100%",
|
||
margin_top="8px",
|
||
),
|
||
# 渲染回答
|
||
rx.markdown(
|
||
dialog.answer,
|
||
component_map={
|
||
"p": lambda text: rx.text(
|
||
text,
|
||
word_wrap="break-word",
|
||
word_break="break-all",
|
||
white_space="pre-line",
|
||
line_height="24px",
|
||
font_size="var(--prismui-font-size-1)",
|
||
color="var(--prismui-color-1)",
|
||
),
|
||
"strong": lambda text: rx.text(
|
||
text,
|
||
display="inline",
|
||
font_weight="var(--prismui-font-weight-1)",
|
||
),
|
||
},
|
||
width="100%",
|
||
margin_bottom="8px",
|
||
),
|
||
width="100%",
|
||
key=dialog_id,
|
||
)
|
||
|
||
|
||
def render_dialog_history() -> rx.Component:
|
||
"""
|
||
渲染对话历史
|
||
:return: Component
|
||
"""
|
||
|
||
# 获取当前会话的对话历史
|
||
dialog_history = ConversationState.dialog_history
|
||
|
||
# 若运行字典为空则渲染品牌图标、名称和介绍、预设用户提示词等,否则遍历渲染运行项
|
||
return rx.cond(
|
||
dialog_history.length() == 0,
|
||
# 渲染问候
|
||
render_greeting(),
|
||
# 遍历渲染对话项
|
||
rx.vstack(
|
||
rx.auto_scroll(
|
||
rx.foreach(
|
||
dialog_history,
|
||
lambda i, _: render_dialog_history_item(i[0], i[1]),
|
||
),
|
||
width="100%",
|
||
padding="0 12px",
|
||
),
|
||
flex="1",
|
||
width="100%",
|
||
max_width="1200px",
|
||
margin_x="auto",
|
||
padding_top="20px",
|
||
justify_content="flex-start",
|
||
overflow_x="hidden",
|
||
overflow_y="auto",
|
||
),
|
||
)
|
||
|
||
|
||
def render_create_conversation_button() -> rx.Component:
|
||
"""
|
||
渲染新建会话按钮
|
||
:return: Component
|
||
"""
|
||
return rx.hstack(
|
||
rx.el.style(
|
||
"""
|
||
.rt-TooltipArrow polygon {
|
||
fill: #ffffff !important;
|
||
}
|
||
.rt-TooltipText {
|
||
color: var(--prismui-color-text) !important;
|
||
opacity: 1 !important;
|
||
}
|
||
"""
|
||
),
|
||
rx.spacer(),
|
||
rx.dialog.root(
|
||
# 触发事件:点击图标
|
||
rx.dialog.trigger(
|
||
rx.box(
|
||
rx.tooltip(
|
||
rx.icon(
|
||
"plus",
|
||
size=14,
|
||
color="var(--prismui-color-text)",
|
||
style={
|
||
"_hover": {
|
||
"color": "var(--devui-brand)",
|
||
}
|
||
},
|
||
),
|
||
content="新建会话",
|
||
background_color="#ffffff",
|
||
box_shadow="0 2px 12px 0 rgba(37, 43, 58, .24)",
|
||
color="#252b3a",
|
||
side="top",
|
||
side_offset=9,
|
||
),
|
||
display="flex",
|
||
width="24px",
|
||
height="24px",
|
||
background_color="var(--devui-base-bg)",
|
||
box_shadow="0 1px 8px #1919190f",
|
||
border_radius="var(--devui-border-radius-full)",
|
||
justify_content="center",
|
||
align_items="center",
|
||
cursor="pointer",
|
||
)
|
||
),
|
||
rx.dialog.content(
|
||
rx.form(
|
||
rx.hstack(
|
||
rx.input(
|
||
name="description",
|
||
placeholder="请输入会话描述",
|
||
flex="auto",
|
||
min_width="20ch",
|
||
),
|
||
rx.button("新建"),
|
||
spacing="2",
|
||
wrap="wrap",
|
||
width="100%",
|
||
),
|
||
# 提交事件:新建对话
|
||
on_submit=ConversationState.create_conversation,
|
||
),
|
||
background_color=rx.color("mauve", 1),
|
||
),
|
||
open=CreateConversationState.is_modal_open,
|
||
on_open_change=CreateConversationState.toggle_modal,
|
||
),
|
||
display="flex",
|
||
width="100%",
|
||
max_width="1200px",
|
||
height="39px",
|
||
padding="0 12px",
|
||
justify_content="flex-end",
|
||
align_items="center",
|
||
gap="4px",
|
||
)
|
||
|
||
|
||
def render_input() -> rx.Component:
|
||
"""
|
||
渲染输入框
|
||
:return: Component
|
||
"""
|
||
return rx.vstack(
|
||
# 渲染自定义输入组件
|
||
rx.form(
|
||
rx.box(
|
||
rx.vstack(
|
||
rx.hstack(
|
||
rx.text_area(
|
||
name="question",
|
||
placeholder="请输入您的问题,按Enter换行",
|
||
vertical_align="middle",
|
||
width="100%",
|
||
height="64px",
|
||
padding="4px 0",
|
||
background_color="var(--devui-base-bg)",
|
||
font_size="var(--devui-font-size)",
|
||
color="var(--prismui-color-text)",
|
||
style={
|
||
"border": "none !important",
|
||
"outline": "none !important",
|
||
"boxShadow": "none !important",
|
||
},
|
||
),
|
||
width="100%",
|
||
padding="0 16px",
|
||
),
|
||
rx.hstack(
|
||
rx.spacer(),
|
||
rx.button(
|
||
rx.icon(
|
||
"send",
|
||
margin_right="4px",
|
||
width="12px",
|
||
height="12px",
|
||
),
|
||
rx.text("发送"),
|
||
position="relative",
|
||
display="inline-flex",
|
||
padding="0 12px",
|
||
background_color="var(--devui-primary)",
|
||
border="none",
|
||
border_radius="20px",
|
||
align_items="center",
|
||
justify_content="center",
|
||
white_space="nowrap",
|
||
inline_height="1.5",
|
||
font_size="var(--devui-font-size)",
|
||
color="var(--devui-light-text)",
|
||
overflow="hidden",
|
||
cursor="pointer",
|
||
type="submit",
|
||
loading=ConversationState.running_status,
|
||
disabled=ConversationState.running_status,
|
||
),
|
||
width="100%",
|
||
justify_content="flex-end",
|
||
align_items="center",
|
||
height="32px",
|
||
padding="0 16px",
|
||
),
|
||
),
|
||
style={
|
||
"* textarea::placeholder": {
|
||
"fontFamily": "var(--font-family)",
|
||
"fontSize": "var(--devui-font-size)",
|
||
"color": "var(--placeholder)",
|
||
"opacity": 1,
|
||
},
|
||
},
|
||
),
|
||
display="flex",
|
||
flex_direction="column",
|
||
width="100%",
|
||
padding="12px 0",
|
||
background_color="var(--devui-base-bg)",
|
||
border="none",
|
||
border_radius="16px",
|
||
box_shadow="0 1px 8px 0 var(--mc-box-shadow)",
|
||
on_submit=ConversationState.run,
|
||
reset_on_submit=True,
|
||
),
|
||
# 渲染底部文案
|
||
rx.text(
|
||
"内容由大模型生成,无法确保准确性和完整性,仅供参考",
|
||
margin_top="8px",
|
||
text_align="center",
|
||
font_size="12px",
|
||
color="var(--devui-aide-text)",
|
||
),
|
||
width="100%",
|
||
max_width="1200px",
|
||
padding="0 12px 12px",
|
||
align_items="center",
|
||
)
|
||
|
||
|
||
def render_conversation_workplace() -> rx.Component:
|
||
"""
|
||
渲染会话工作区
|
||
:return: Component
|
||
"""
|
||
return rx.box(
|
||
rx.vstack(
|
||
# 渲染对话历史
|
||
render_dialog_history(),
|
||
# 渲染新建对话按钮
|
||
render_create_conversation_button(),
|
||
# 渲染输入框
|
||
render_input(),
|
||
display="flex",
|
||
flex_flow="column",
|
||
width="100%",
|
||
height="100%",
|
||
gap="8px",
|
||
align_items="center",
|
||
),
|
||
position="relative",
|
||
display="flex",
|
||
flex="1",
|
||
width="0",
|
||
height="100%",
|
||
background="linear-gradient(180deg, #fffffff2, #f8fafff2 99%)",
|
||
)
|
||
|
||
|
||
def render_conversation_history_collapse_button(
|
||
is_collapse_expanded: bool,
|
||
) -> rx.Component:
|
||
"""
|
||
渲染会话历史折叠面板的打开/关闭按钮
|
||
:param is_collapse_expanded: 会话历史折叠面板展开状态
|
||
:return: Component
|
||
"""
|
||
return rx.button(
|
||
rx.cond(
|
||
is_collapse_expanded,
|
||
rx.icon(
|
||
"chevron-left",
|
||
width="16px",
|
||
height="16px",
|
||
color="var(--devui-icon-fill-weak)",
|
||
),
|
||
rx.icon(
|
||
"chevron-right",
|
||
width="16px",
|
||
height="16px",
|
||
color="var(--devui-icon-fill-weak)",
|
||
),
|
||
),
|
||
position="absolute",
|
||
top="50%",
|
||
left=rx.cond(is_collapse_expanded, "calc(clamp(240px, 25%, 380px) - 8px)", "0"),
|
||
z_index=99,
|
||
width="16px",
|
||
height="40px",
|
||
background="var(--prismui-background-color-1)",
|
||
box_shadow="2px 0 4px 0 var(--mc-float-block-shadow)",
|
||
border_radius=rx.cond(is_collapse_expanded, "6px", "0 6px 6px 0"),
|
||
transition="all 0.3s ease-in-out",
|
||
cursor="pointer",
|
||
# 点击事件:打开/关闭对话历史折叠面板
|
||
on_click=ConversationHistoryState.toggle_collapse,
|
||
)
|
||
|
||
|
||
def render_conversation() -> rx.Component:
|
||
"""
|
||
渲染会话页面:布局参考 MetaChat,左侧为会话历史,右侧为对话历史和输入框
|
||
"""
|
||
# 会话历史折叠面板展开状态
|
||
is_collapse_expanded = ConversationHistoryState.is_collapse_expanded
|
||
|
||
return rx.box(
|
||
# 渲染对话历史折叠面板
|
||
rx.hstack(
|
||
# 渲染对话历史折叠面板
|
||
render_conversation_history_collapse(is_collapse_expanded),
|
||
# 渲染会话工作区
|
||
render_conversation_workplace(),
|
||
position="relative",
|
||
display="flex",
|
||
flex="1",
|
||
height="100%",
|
||
min_height="0",
|
||
overflow="hidden",
|
||
transition="all 0.3s ease-in-out",
|
||
),
|
||
# 渲染对话历史折叠面板打开/关闭按钮
|
||
render_conversation_history_collapse_button(is_collapse_expanded),
|
||
position="relative",
|
||
width="100%",
|
||
height="100%",
|
||
border_radius="12px",
|
||
overflow="hidden",
|
||
# 挂载事件:初始化会话历史
|
||
on_mount=ConversationState.on_mount,
|
||
)
|