Compare commits
4 Commits
89bcca744d
...
fe061e6168
| Author | SHA1 | Date | |
|---|---|---|---|
| fe061e6168 | |||
| d08cc693b3 | |||
| a30b03fb93 | |||
| 4c3d8cedc7 |
@ -37,6 +37,10 @@ MESSAGES = {
|
||||
"zh-CN": "未登录",
|
||||
"en-US": "Not logged in",
|
||||
},
|
||||
"auth.session_expired": {
|
||||
"zh-CN": "登录状态已过期,请重新登录",
|
||||
"en-US": "Your login session has expired. Please log in again.",
|
||||
},
|
||||
"auth.host_mode_tutorial_not_applicable": {
|
||||
"zh-CN": "宿主机模式无需设置新手教程状态",
|
||||
"en-US": "Tutorial status does not apply in host mode",
|
||||
|
||||
@ -821,7 +821,7 @@ def api_login_required(view_func):
|
||||
@wraps(view_func)
|
||||
def wrapped(*args, **kwargs):
|
||||
if not is_logged_in():
|
||||
return jsonify({"error": "Unauthorized"}), 401
|
||||
return jsonify({"error": tr("auth.session_expired")}), 401
|
||||
return view_func(*args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
|
||||
@ -34,7 +34,7 @@ def api_login_required(view_func):
|
||||
@wraps(view_func)
|
||||
def wrapped(*args, **kwargs):
|
||||
if not is_logged_in():
|
||||
return jsonify({"error": "Unauthorized"}), 401
|
||||
return jsonify({"error": tr("auth.session_expired")}), 401
|
||||
return view_func(*args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
||||
@ -128,6 +128,7 @@
|
||||
/>
|
||||
|
||||
<div v-if="titleRibbonVisible" class="conversation-ribbon" ref="titleRibbon">
|
||||
<div class="conversation-ribbon__blur" aria-hidden="true"></div>
|
||||
<button
|
||||
type="button"
|
||||
class="conversation-ribbon__selector"
|
||||
@ -302,6 +303,8 @@
|
||||
:project-git-summary="projectGitSummary"
|
||||
:user-question-minimized="userQuestionMinimized"
|
||||
:pending-user-question-count="pendingUserQuestions.length"
|
||||
:plan-approval-minimized="planApprovalMinimized"
|
||||
:pending-plan-approval-count="pendingPlanApprovals.length"
|
||||
:goal-mode-armed="goalModeArmed"
|
||||
:goal-running="goalRunning"
|
||||
:goal-progress="goalProgress"
|
||||
@ -319,6 +322,7 @@
|
||||
:active-sub-agent-count="activeSubAgentCount"
|
||||
:avatar-status="showStatusAvatar && messages.length > 0 ? avatarStatus : null"
|
||||
@restore-user-question="restoreUserQuestionDialog"
|
||||
@restore-plan-approval="restorePlanApprovalDialog"
|
||||
@stop-all-sub-agents="handleStopAllSubAgents"
|
||||
@update:input-message="inputSetMessage"
|
||||
@input-change="handleInputChange"
|
||||
@ -544,10 +548,11 @@
|
||||
@dismiss="dismissUserQuestions"
|
||||
/>
|
||||
<PlanApprovalDialog
|
||||
:visible="pendingPlanApprovals.length > 0"
|
||||
:visible="pendingPlanApprovals.length > 0 && !planApprovalMinimized"
|
||||
:approvals="pendingPlanApprovals"
|
||||
:submitting-ids="answeringPlanApprovalIds"
|
||||
@submit="submitPlanApproval"
|
||||
@minimize="minimizePlanApprovalDialog"
|
||||
/>
|
||||
<transition name="overlay-fade">
|
||||
<VersioningDialog
|
||||
@ -588,6 +593,7 @@
|
||||
ref="mobilePanelTrigger"
|
||||
>
|
||||
<div class="mobile-panel-topbar">
|
||||
<div class="mobile-panel-topbar__blur" aria-hidden="true"></div>
|
||||
<button
|
||||
type="button"
|
||||
class="mobile-panel-fab"
|
||||
|
||||
@ -446,6 +446,10 @@ export const toolMethods = {
|
||||
this.pendingPlanApprovals = this.pendingPlanApprovals.filter(
|
||||
(item: any) => item && String(item.approval_id || '') !== id
|
||||
);
|
||||
// 弹窗被关闭(最小化)期间批准已解决:无待批事项时复位,避免状态栏按钮残留
|
||||
if (!this.pendingPlanApprovals.length) {
|
||||
this.planApprovalMinimized = false;
|
||||
}
|
||||
// 批准后后端已切换运行模式/恢复权限与执行环境,刷新显示(多标签页同步场景)
|
||||
if (String(data?.decision || '') === 'approved') {
|
||||
this.fetchWorkMode();
|
||||
|
||||
@ -95,6 +95,18 @@ export const dialogMethods = {
|
||||
// ignore
|
||||
}
|
||||
},
|
||||
minimizePlanApprovalDialog() {
|
||||
if (!Array.isArray(this.pendingPlanApprovals) || !this.pendingPlanApprovals.length) {
|
||||
return;
|
||||
}
|
||||
this.planApprovalMinimized = true;
|
||||
},
|
||||
restorePlanApprovalDialog() {
|
||||
if (!Array.isArray(this.pendingPlanApprovals) || !this.pendingPlanApprovals.length) {
|
||||
return;
|
||||
}
|
||||
this.planApprovalMinimized = false;
|
||||
},
|
||||
async submitPlanApproval(payload) {
|
||||
const approvalId = String(payload?.approval_id || '').trim();
|
||||
if (!approvalId) {
|
||||
|
||||
@ -292,6 +292,7 @@ export function dataState() {
|
||||
pendingUserQuestions: [],
|
||||
pendingPlanApprovals: [],
|
||||
answeringPlanApprovalIds: [],
|
||||
planApprovalMinimized: false,
|
||||
userQuestionDialogVisible: false,
|
||||
userQuestionMinimized: false,
|
||||
userQuestionActiveIndex: 0,
|
||||
|
||||
@ -107,4 +107,32 @@ watch(() => props.content, renderMath, { immediate: true });
|
||||
.markdown-text-segment > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 列表基础样式(渲染器自有,不依赖外部容器类名):
|
||||
全局 reset 清掉了 ul/ol 默认 padding,此处置回标准缩进,
|
||||
保证 MarkdownRenderer 在任何容器内(聊天/批准弹窗/预览等)表现一致:
|
||||
标记在左、文字缩进靠右 */
|
||||
.markdown-text-segment :deep(ul),
|
||||
.markdown-text-segment :deep(ol) {
|
||||
padding-left: 24px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.markdown-text-segment :deep(ul) {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.markdown-text-segment :deep(ul ul) {
|
||||
list-style-type: circle;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.markdown-text-segment :deep(ol) {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
.markdown-text-segment :deep(li) {
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -179,6 +179,14 @@
|
||||
>
|
||||
{{ $t('input.waitingForQuestion') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="planApprovalMinimized && (pendingPlanApprovalCount || 0) > 0"
|
||||
type="button"
|
||||
class="floating-project-status__notice floating-project-status__notice--button"
|
||||
@click.stop="$emit('restore-plan-approval')"
|
||||
>
|
||||
{{ $t('input.planApprovalPending') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="(activeSubAgentCount || 0) > 0"
|
||||
type="button"
|
||||
@ -698,6 +706,7 @@ const emit = defineEmits([
|
||||
'delete-runtime-message',
|
||||
'composer-height-change',
|
||||
'restore-user-question',
|
||||
'restore-plan-approval',
|
||||
'toggle-goal-mode',
|
||||
'open-goal-dialog',
|
||||
'workflow-activated',
|
||||
@ -776,6 +785,8 @@ const props = defineProps<{
|
||||
} | null;
|
||||
userQuestionMinimized?: boolean;
|
||||
pendingUserQuestionCount?: number;
|
||||
planApprovalMinimized?: boolean;
|
||||
pendingPlanApprovalCount?: number;
|
||||
goalModeArmed?: boolean;
|
||||
goalRunning?: boolean;
|
||||
goalProgress?: Record<string, any> | null;
|
||||
@ -1066,6 +1077,7 @@ const floatingStatusVisible = computed(() => {
|
||||
!!props.goalRunning ||
|
||||
!!props.goalModeArmed ||
|
||||
(Number(props.pendingUserQuestionCount || 0) > 0) ||
|
||||
(!!props.planApprovalMinimized && Number(props.pendingPlanApprovalCount || 0) > 0) ||
|
||||
(Number(props.activeSubAgentCount || 0) > 0);
|
||||
return visible;
|
||||
});
|
||||
|
||||
@ -4,6 +4,17 @@
|
||||
<section class="plan-approval-card" role="dialog" aria-modal="true" :aria-label="$t('overlay.planApprovalAriaLabel')">
|
||||
<header class="plan-approval-windowbar">
|
||||
<div class="plan-approval-window-title">{{ $t('overlay.planApprovalTitle') }}</div>
|
||||
<button
|
||||
type="button"
|
||||
class="plan-approval-close"
|
||||
:title="$t('overlay.planApprovalMinimize')"
|
||||
:aria-label="$t('overlay.planApprovalMinimize')"
|
||||
@click="emit('minimize')"
|
||||
>
|
||||
<svg viewBox="0 0 20 20" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M5 5l10 10M15 5L5 15" />
|
||||
</svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<header class="plan-approval-header">
|
||||
@ -75,6 +86,7 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'submit', payload: { approval_id: string; approved: boolean; comment: string }): void;
|
||||
(event: 'minimize'): void;
|
||||
}>();
|
||||
|
||||
// 计划批准一次只处理一份(plan 模式下模型阻塞等待,天然串行);
|
||||
@ -139,6 +151,7 @@ function reject() {
|
||||
}
|
||||
|
||||
.plan-approval-windowbar {
|
||||
position: relative;
|
||||
height: 40px;
|
||||
flex: 0 0 40px;
|
||||
display: flex;
|
||||
@ -147,6 +160,29 @@ function reject() {
|
||||
border-bottom: 1px solid var(--border-default);
|
||||
}
|
||||
|
||||
.plan-approval-close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 7px;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.plan-approval-close:hover {
|
||||
background: var(--hover-bg);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.plan-approval-window-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
|
||||
@ -9,6 +9,7 @@ export default {
|
||||
goalModeRunning: 'Goal mode running',
|
||||
goalModeReady: 'Goal mode ready',
|
||||
waitingForQuestion: 'Waiting for your answer',
|
||||
planApprovalPending: 'Plan awaiting approval',
|
||||
subAgentsRunning: '{n} sub-agents running',
|
||||
terminalsRunning: '{n} terminals',
|
||||
quickMenuAriaLabel: 'Quick menu',
|
||||
|
||||
@ -88,6 +88,7 @@ export default {
|
||||
// ── PlanApprovalDialog ──
|
||||
planApprovalAriaLabel: 'Plan awaiting approval',
|
||||
planApprovalTitle: 'Plan awaiting approval',
|
||||
planApprovalMinimize: 'Close the dialog; restore it later from the status bar',
|
||||
planTruncatedNote: '(Content too long; preview truncated. See the file for the full content)',
|
||||
planCommentPlaceholder:
|
||||
'Feedback (optional): added as extra requirements when approving, or describe what to adjust when rejecting...',
|
||||
|
||||
@ -9,6 +9,7 @@ export default {
|
||||
goalModeRunning: '目标模式运行中',
|
||||
goalModeReady: '目标模式就绪',
|
||||
waitingForQuestion: '等待回答问题',
|
||||
planApprovalPending: '有待批准的计划',
|
||||
subAgentsRunning: '{n} 个子智能体运行中',
|
||||
terminalsRunning: '{n} 个终端',
|
||||
quickMenuAriaLabel: '快捷菜单',
|
||||
|
||||
@ -87,6 +87,7 @@ export default {
|
||||
// ── PlanApprovalDialog:计划待批准 ──
|
||||
planApprovalAriaLabel: '计划待批准',
|
||||
planApprovalTitle: '计划待批准',
|
||||
planApprovalMinimize: '关闭弹窗,稍后从状态栏恢复',
|
||||
planTruncatedNote: '(内容过长,已截断预览,完整内容见文件)',
|
||||
planCommentPlaceholder: '意见(可选):批准时作为补充要求,拒绝时说明需要调整的方向…',
|
||||
planRejectTitle: '拒绝这份计划,AI 将根据你的意见修订后重新提交',
|
||||
|
||||
@ -44,6 +44,46 @@
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* 顶栏下方渐进模糊带:mask-image 与 backdrop-filter 写在同一元素上时,
|
||||
新版浏览器内核中模糊会静默失效(2026-08 实测),单元素 mask 渐隐模糊不可用;
|
||||
改用三级强度递减的条带(元素本体 + ::before + ::after)模拟渐进模糊,
|
||||
上方再由 ::after 颜色渐隐层溶掉阶梯感 */
|
||||
.conversation-ribbon__blur {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 2px; /* 带 1(最强):ribbon 底上 8px ~ 底上 2px */
|
||||
height: 6px;
|
||||
backdrop-filter: blur(14px);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.conversation-ribbon__blur::before,
|
||||
.conversation-ribbon__blur::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 带 2(中等):与带 1 重叠 1px 避免接缝 */
|
||||
.conversation-ribbon__blur::before {
|
||||
top: 5px;
|
||||
height: 7px;
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
/* 带 3(极轻):溶掉模糊下边界 */
|
||||
.conversation-ribbon__blur::after {
|
||||
top: 11px;
|
||||
height: 6px;
|
||||
backdrop-filter: blur(1.2px);
|
||||
-webkit-backdrop-filter: blur(1.2px);
|
||||
}
|
||||
|
||||
.conversation-ribbon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
@ -1440,6 +1440,46 @@
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
/* 顶栏下方渐进模糊带:mask-image 与 backdrop-filter 写在同一元素上时,
|
||||
新版浏览器内核中模糊会静默失效(2026-08 实测),单元素 mask 渐隐模糊不可用;
|
||||
改用三级强度递减的条带(元素本体 + ::before + ::after)模拟渐进模糊,
|
||||
上方再由 ::after 颜色渐隐层溶掉阶梯感 */
|
||||
.mobile-panel-topbar__blur {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 2px; /* 带 1(最强):顶栏底上 8px ~ 底上 2px */
|
||||
height: 6px;
|
||||
backdrop-filter: blur(14px);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mobile-panel-topbar__blur::before,
|
||||
.mobile-panel-topbar__blur::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 带 2(中等):与带 1 重叠 1px 避免接缝 */
|
||||
.mobile-panel-topbar__blur::before {
|
||||
top: 5px;
|
||||
height: 7px;
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
/* 带 3(极轻):溶掉模糊下边界 */
|
||||
.mobile-panel-topbar__blur::after {
|
||||
top: 11px;
|
||||
height: 6px;
|
||||
backdrop-filter: blur(1.2px);
|
||||
-webkit-backdrop-filter: blur(1.2px);
|
||||
}
|
||||
|
||||
.mobile-panel-topbar::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
@ -549,32 +549,6 @@ body[data-theme='dark'] {
|
||||
}
|
||||
}
|
||||
|
||||
/* Markdown列表样式 - 修复偏左问题 */
|
||||
.text-content ul,
|
||||
.text-content ol {
|
||||
margin-left: 24px; /* 增加左边距 */
|
||||
padding-left: 0;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.text-content ul {
|
||||
list-style-type: disc; /* 实心圆点 */
|
||||
}
|
||||
|
||||
.text-content ul ul {
|
||||
list-style-type: circle; /* 空心圆点 */
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.text-content ol {
|
||||
list-style-type: decimal; /* 数字列表 */
|
||||
}
|
||||
|
||||
.text-content li {
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 搜索结果展示 */
|
||||
.search-meta {
|
||||
font-size: 14px;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user