feat(chat): add compact message display mode and goal-review/compression headers
- 审核(goal_review) user 消息气泡显示「审核 + circle-alert 图标」 - compact 消息新增「简略信息」显示形式(个人空间可切换),渲染为横线包裹的一行概要 - 压缩续接消息单独标记为 compression_handoff,visibility 改为 compact Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
32865f6d6e
commit
f5e4541d83
@ -152,6 +152,7 @@ _VALID_USER_MESSAGE_SOURCES = {
|
|||||||
"goal_prompt",
|
"goal_prompt",
|
||||||
"goal_review",
|
"goal_review",
|
||||||
"compression",
|
"compression",
|
||||||
|
"compression_handoff",
|
||||||
"permission",
|
"permission",
|
||||||
"sandbox",
|
"sandbox",
|
||||||
"skill",
|
"skill",
|
||||||
@ -164,7 +165,7 @@ def _user_message_ui_defaults(source: str, *, auto_user_message_event: bool = Fa
|
|||||||
return {"visibility": "hidden", "starts_work": False}
|
return {"visibility": "hidden", "starts_work": False}
|
||||||
if normalized in {"guidance", "notify"}:
|
if normalized in {"guidance", "notify"}:
|
||||||
return {"visibility": "compact", "starts_work": False}
|
return {"visibility": "compact", "starts_work": False}
|
||||||
if normalized in {"goal_review", "compression", "sub_agent", "background_command"}:
|
if normalized in {"goal_review", "compression", "compression_handoff", "sub_agent", "background_command"}:
|
||||||
return {"visibility": "compact", "starts_work": True}
|
return {"visibility": "compact", "starts_work": True}
|
||||||
if normalized == "presend":
|
if normalized == "presend":
|
||||||
return {"visibility": "chat", "starts_work": True}
|
return {"visibility": "chat", "starts_work": True}
|
||||||
@ -1085,16 +1086,22 @@ async def handle_task_with_sender(
|
|||||||
if guide_message:
|
if guide_message:
|
||||||
finalize_user_work_timer()
|
finalize_user_work_timer()
|
||||||
finalize_run_versioning_checkpoint("auto_deep_compress_handoff")
|
finalize_run_versioning_checkpoint("auto_deep_compress_handoff")
|
||||||
await handle_task_with_sender(
|
# 压缩后的续接引导语单独标记来源,使其以 compact 形式展示,
|
||||||
web_terminal,
|
# 而不是混入普通用户消息流。
|
||||||
workspace,
|
setattr(web_terminal, "_current_user_message_source", "compression_handoff")
|
||||||
guide_message,
|
try:
|
||||||
[],
|
await handle_task_with_sender(
|
||||||
sender,
|
web_terminal,
|
||||||
client_sid,
|
workspace,
|
||||||
username,
|
guide_message,
|
||||||
[]
|
[],
|
||||||
)
|
sender,
|
||||||
|
client_sid,
|
||||||
|
username,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
setattr(web_terminal, "_current_user_message_source", "user")
|
||||||
return
|
return
|
||||||
finalize_user_work_timer()
|
finalize_user_work_timer()
|
||||||
finalize_run_versioning_checkpoint("auto_deep_compress_end")
|
finalize_run_versioning_checkpoint("auto_deep_compress_end")
|
||||||
@ -1570,16 +1577,22 @@ async def handle_task_with_sender(
|
|||||||
if deep_result.get("success") and guide_message:
|
if deep_result.get("success") and guide_message:
|
||||||
finalize_user_work_timer()
|
finalize_user_work_timer()
|
||||||
finalize_run_versioning_checkpoint("deep_compress_handoff")
|
finalize_run_versioning_checkpoint("deep_compress_handoff")
|
||||||
await handle_task_with_sender(
|
# 压缩后的续接引导语单独标记来源,使其以 compact 形式展示,
|
||||||
web_terminal,
|
# 而不是混入普通用户消息流。
|
||||||
workspace,
|
setattr(web_terminal, "_current_user_message_source", "compression_handoff")
|
||||||
guide_message,
|
try:
|
||||||
[],
|
await handle_task_with_sender(
|
||||||
sender,
|
web_terminal,
|
||||||
client_sid,
|
workspace,
|
||||||
username,
|
guide_message,
|
||||||
[]
|
[],
|
||||||
)
|
sender,
|
||||||
|
client_sid,
|
||||||
|
username,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
setattr(web_terminal, "_current_user_message_source", "user")
|
||||||
return
|
return
|
||||||
finalize_user_work_timer()
|
finalize_user_work_timer()
|
||||||
finalize_run_versioning_checkpoint("deep_compress_end")
|
finalize_run_versioning_checkpoint("deep_compress_end")
|
||||||
|
|||||||
@ -19,6 +19,7 @@ _VALID_SOURCES = {
|
|||||||
"goal_prompt",
|
"goal_prompt",
|
||||||
"goal_review",
|
"goal_review",
|
||||||
"compression",
|
"compression",
|
||||||
|
"compression_handoff",
|
||||||
"permission",
|
"permission",
|
||||||
"sandbox",
|
"sandbox",
|
||||||
"skill",
|
"skill",
|
||||||
@ -32,7 +33,7 @@ def _runtime_message_ui_defaults(src: str, *, inline: bool = False) -> Dict[str,
|
|||||||
return {"visibility": "hidden", "starts_work": False}
|
return {"visibility": "hidden", "starts_work": False}
|
||||||
if normalized in {"guidance", "notify"}:
|
if normalized in {"guidance", "notify"}:
|
||||||
return {"visibility": "compact", "starts_work": False}
|
return {"visibility": "compact", "starts_work": False}
|
||||||
if normalized in {"goal_review", "compression", "sub_agent", "background_command", "presend"}:
|
if normalized in {"goal_review", "compression", "compression_handoff", "sub_agent", "background_command", "presend"}:
|
||||||
return {"visibility": "compact", "starts_work": True}
|
return {"visibility": "compact", "starts_work": True}
|
||||||
if normalized == "goal":
|
if normalized == "goal":
|
||||||
# Backward-compatible fallback for old callers. Inline goal injections are
|
# Backward-compatible fallback for old callers. Inline goal injections are
|
||||||
|
|||||||
@ -1207,6 +1207,7 @@ def compress_conversation(conversation_id, terminal: WebTerminal, workspace: Use
|
|||||||
images=[],
|
images=[],
|
||||||
conversation_id=new_conversation_id,
|
conversation_id=new_conversation_id,
|
||||||
videos=[],
|
videos=[],
|
||||||
|
message_source="compression_handoff",
|
||||||
)
|
)
|
||||||
response_payload["auto_task_started"] = True
|
response_payload["auto_task_started"] = True
|
||||||
response_payload["auto_task_id"] = task_rec.task_id
|
response_payload["auto_task_id"] = task_rec.task_id
|
||||||
|
|||||||
@ -11,44 +11,49 @@
|
|||||||
'message-block--before-sub-agent-notice': isFollowedBySubAgentNotice(index)
|
'message-block--before-sub-agent-notice': isFollowedBySubAgentNotice(index)
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div v-if="msg.role === 'user'" class="user-message" :class="{ 'user-message--compact': getMessageVisibility(msg) === 'compact' }">
|
<div v-if="msg.role === 'user'" class="user-message" :class="{ 'user-message--compact': getMessageVisibility(msg) === 'compact', 'user-message--brief': isBriefCompactMessage(msg) }">
|
||||||
<div class="message-header icon-label">
|
<div v-if="isBriefCompactMessage(msg)" class="compact-brief-line" role="separator">
|
||||||
<span class="icon icon-sm" :style="iconStyleSafe(userHeaderIconKey(msg))" aria-hidden="true"></span>
|
<span class="compact-brief-text">{{ compactBriefLabel(msg) }}</span>
|
||||||
<span>{{ userHeaderLabel(msg) }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="message-text user-bubble-text">
|
<template v-else>
|
||||||
<div
|
<div class="message-header icon-label">
|
||||||
v-if="msg.content"
|
<span class="icon icon-sm" :style="iconStyleSafe(userHeaderIconKey(msg))" aria-hidden="true"></span>
|
||||||
class="bubble-text"
|
<span>{{ userHeaderLabel(msg) }}</span>
|
||||||
v-html="renderUserMessageContent(msg.content)"
|
</div>
|
||||||
></div>
|
<div class="message-text user-bubble-text">
|
||||||
<div v-if="msg.images && msg.images.length" class="image-inline-row">
|
|
||||||
<div
|
<div
|
||||||
class="image-thumbnail-wrapper"
|
v-if="msg.content"
|
||||||
v-for="(img, imgIndex) in msg.images"
|
class="bubble-text"
|
||||||
:key="mediaPreviewKey(msg, img, imgIndex)"
|
v-html="renderUserMessageContent(msg.content)"
|
||||||
>
|
></div>
|
||||||
<img
|
<div v-if="msg.images && msg.images.length" class="image-inline-row">
|
||||||
:src="getPreviewUrl(msg, img, 'image')"
|
<div
|
||||||
:alt="formatImageName(img)"
|
class="image-thumbnail-wrapper"
|
||||||
class="image-thumbnail"
|
v-for="(img, imgIndex) in msg.images"
|
||||||
/>
|
:key="mediaPreviewKey(msg, img, imgIndex)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="getPreviewUrl(msg, img, 'image')"
|
||||||
|
:alt="formatImageName(img)"
|
||||||
|
class="image-thumbnail"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="msg.videos && msg.videos.length" class="image-inline-row video-inline-row">
|
||||||
|
<div
|
||||||
|
class="image-thumbnail-wrapper"
|
||||||
|
v-for="(video, videoIndex) in msg.videos"
|
||||||
|
:key="mediaPreviewKey(msg, video, videoIndex)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="getPreviewUrl(msg, video, 'video')"
|
||||||
|
:alt="formatImageName(video)"
|
||||||
|
class="image-thumbnail"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="msg.videos && msg.videos.length" class="image-inline-row video-inline-row">
|
</template>
|
||||||
<div
|
|
||||||
class="image-thumbnail-wrapper"
|
|
||||||
v-for="(video, videoIndex) in msg.videos"
|
|
||||||
:key="mediaPreviewKey(msg, video, videoIndex)"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="getPreviewUrl(msg, video, 'video')"
|
|
||||||
:alt="formatImageName(video)"
|
|
||||||
class="image-thumbnail"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="msg.role === 'assistant'" class="assistant-message">
|
<div v-else-if="msg.role === 'assistant'" class="assistant-message">
|
||||||
<!-- 在 assistant 消息前显示 AI Assistant 头部 -->
|
<!-- 在 assistant 消息前显示 AI Assistant 头部 -->
|
||||||
@ -627,6 +632,9 @@ const personalization = usePersonalizationStore();
|
|||||||
const blockDisplayMode = computed(() => {
|
const blockDisplayMode = computed(() => {
|
||||||
return personalization.experiments.blockDisplayMode || 'stacked';
|
return personalization.experiments.blockDisplayMode || 'stacked';
|
||||||
});
|
});
|
||||||
|
const compactMessageDisplay = computed(() => {
|
||||||
|
return personalization.experiments.compactMessageDisplay || 'full';
|
||||||
|
});
|
||||||
const stackedBlocksEnabled = computed(() => {
|
const stackedBlocksEnabled = computed(() => {
|
||||||
// 堆叠模式和极简模式都使用堆叠布局
|
// 堆叠模式和极简模式都使用堆叠布局
|
||||||
return blockDisplayMode.value === 'stacked' || blockDisplayMode.value === 'minimal';
|
return blockDisplayMode.value === 'stacked' || blockDisplayMode.value === 'minimal';
|
||||||
@ -1380,6 +1388,12 @@ function userHeaderLabel(msg: any): string {
|
|||||||
if (source === 'goal') {
|
if (source === 'goal') {
|
||||||
return '目标';
|
return '目标';
|
||||||
}
|
}
|
||||||
|
if (source === 'goal_review') {
|
||||||
|
return '审核';
|
||||||
|
}
|
||||||
|
if (source === 'compression' || source === 'compression_handoff') {
|
||||||
|
return '压缩';
|
||||||
|
}
|
||||||
if (source === 'notify' || source === 'sub_agent' || source === 'background_command') {
|
if (source === 'notify' || source === 'sub_agent' || source === 'background_command') {
|
||||||
return '通知';
|
return '通知';
|
||||||
}
|
}
|
||||||
@ -1394,12 +1408,54 @@ function userHeaderIconKey(msg: any): string {
|
|||||||
if (source === 'goal') {
|
if (source === 'goal') {
|
||||||
return 'flag';
|
return 'flag';
|
||||||
}
|
}
|
||||||
|
if (source === 'goal_review') {
|
||||||
|
return 'circleAlert';
|
||||||
|
}
|
||||||
|
if (source === 'compression' || source === 'compression_handoff') {
|
||||||
|
return 'layers';
|
||||||
|
}
|
||||||
if (source === 'notify' || source === 'sub_agent' || source === 'background_command') {
|
if (source === 'notify' || source === 'sub_agent' || source === 'background_command') {
|
||||||
return 'bell';
|
return 'bell';
|
||||||
}
|
}
|
||||||
return 'user';
|
return 'user';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- 简略信息渲染(compact 消息可选显示形式)----
|
||||||
|
// 命中条件:当前消息为 compact 可见性,且用户在个人空间选择了“简略信息”。
|
||||||
|
function isBriefCompactMessage(msg: any): boolean {
|
||||||
|
if (!msg || msg.role !== 'user') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (compactMessageDisplay.value !== 'brief') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return getMessageVisibility(msg) === 'compact';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简略信息只展示一行概要标签(不显示原始内容),按来源给出固定文案。
|
||||||
|
function compactBriefLabel(msg: any): string {
|
||||||
|
const source = userHeaderSource(msg);
|
||||||
|
switch (source) {
|
||||||
|
case 'goal_review':
|
||||||
|
return '目标审核完成';
|
||||||
|
case 'sub_agent':
|
||||||
|
return '后台子智能体完成';
|
||||||
|
case 'background_command':
|
||||||
|
return '后台指令完成';
|
||||||
|
case 'compression':
|
||||||
|
case 'compression_handoff':
|
||||||
|
return '对话压缩完成';
|
||||||
|
case 'goal':
|
||||||
|
return '目标进行中';
|
||||||
|
case 'guidance':
|
||||||
|
return '运行中引导';
|
||||||
|
case 'notify':
|
||||||
|
return '系统通知';
|
||||||
|
default:
|
||||||
|
return '系统消息';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
attachBounceListener();
|
attachBounceListener();
|
||||||
console.warn('[SCROLL_BOUNCE_TRACE]', 'mounted', {
|
console.warn('[SCROLL_BOUNCE_TRACE]', 'mounted', {
|
||||||
|
|||||||
@ -615,6 +615,45 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settings-select-row">
|
||||||
|
<span class="settings-row-copy"
|
||||||
|
><span class="settings-row-title">简略消息显示</span
|
||||||
|
><span class="settings-row-desc"
|
||||||
|
>审核、子智能体等紧凑系统消息的显示形式。</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="settings-select-wrap"
|
||||||
|
:class="{ open: activeDropdown === 'compact-message' }"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="settings-select-button"
|
||||||
|
@click="toggleDropdown('compact-message')"
|
||||||
|
>
|
||||||
|
{{ compactMessageDisplayLabel }}
|
||||||
|
<span class="select-chevron" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
:class="['settings-floating-menu', { dark: activeTheme === 'dark' }]"
|
||||||
|
:style="activeDropdown ? floatingMenuStyle : undefined"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="option in compactMessageDisplayOptions"
|
||||||
|
:key="option.id"
|
||||||
|
type="button"
|
||||||
|
class="settings-menu-option"
|
||||||
|
:class="{ selected: currentCompactMessageDisplay === option.value }"
|
||||||
|
@click="selectCompactMessageDisplay(option.value)"
|
||||||
|
>
|
||||||
|
<strong>{{ option.label }}</strong
|
||||||
|
><span>{{ option.desc }}</span
|
||||||
|
><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
@ -1656,6 +1695,16 @@ const blockDisplayLabel = computed(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const currentCompactMessageDisplay = computed(() => experiments.value.compactMessageDisplay);
|
||||||
|
|
||||||
|
const compactMessageDisplayLabel = computed(() => {
|
||||||
|
return (
|
||||||
|
compactMessageDisplayOptions.find(
|
||||||
|
(option) => option.value === currentCompactMessageDisplay.value
|
||||||
|
)?.label || '完整信息'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const usageSummary = ref({
|
const usageSummary = ref({
|
||||||
total_input_tokens: 0,
|
total_input_tokens: 0,
|
||||||
total_output_tokens: 0,
|
total_output_tokens: 0,
|
||||||
@ -2084,6 +2133,26 @@ const handleBlockDisplayModeChange = (mode: 'traditional' | 'stacked' | 'minimal
|
|||||||
personalization.setBlockDisplayMode(mode);
|
personalization.setBlockDisplayMode(mode);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const compactMessageDisplayOptions = [
|
||||||
|
{
|
||||||
|
id: 'full',
|
||||||
|
label: '完整信息',
|
||||||
|
desc: '审核、子智能体等系统消息显示完整原始内容',
|
||||||
|
value: 'full' as const
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'brief',
|
||||||
|
label: '简略信息',
|
||||||
|
desc: '仅以一行横线概要替代(如「目标审核完成」)',
|
||||||
|
value: 'brief' as const
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectCompactMessageDisplay = (mode: 'full' | 'brief') => {
|
||||||
|
personalization.setCompactMessageDisplay(mode);
|
||||||
|
closeDropdown();
|
||||||
|
};
|
||||||
|
|
||||||
const openAdminPanel = () => {
|
const openAdminPanel = () => {
|
||||||
window.open('/admin/monitor', '_blank', 'noopener');
|
window.open('/admin/monitor', '_blank', 'noopener');
|
||||||
personalization.closeDrawer();
|
personalization.closeDrawer();
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { defineStore } from 'pinia';
|
|||||||
import { useModelStore } from './model';
|
import { useModelStore } from './model';
|
||||||
|
|
||||||
export type BlockDisplayMode = 'traditional' | 'stacked' | 'minimal';
|
export type BlockDisplayMode = 'traditional' | 'stacked' | 'minimal';
|
||||||
|
export type CompactMessageDisplay = 'full' | 'brief';
|
||||||
type RunMode = 'fast' | 'thinking' | 'deep';
|
type RunMode = 'fast' | 'thinking' | 'deep';
|
||||||
type PermissionMode = 'readonly' | 'approval' | 'auto_approval' | 'unrestricted';
|
type PermissionMode = 'readonly' | 'approval' | 'auto_approval' | 'unrestricted';
|
||||||
|
|
||||||
@ -53,6 +54,7 @@ interface PersonalForm {
|
|||||||
|
|
||||||
interface ExperimentState {
|
interface ExperimentState {
|
||||||
blockDisplayMode: BlockDisplayMode;
|
blockDisplayMode: BlockDisplayMode;
|
||||||
|
compactMessageDisplay: CompactMessageDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PersonalizationState {
|
interface PersonalizationState {
|
||||||
@ -142,7 +144,8 @@ const defaultForm = (): PersonalForm => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const defaultExperimentState = (): ExperimentState => ({
|
const defaultExperimentState = (): ExperimentState => ({
|
||||||
blockDisplayMode: 'stacked'
|
blockDisplayMode: 'stacked',
|
||||||
|
compactMessageDisplay: 'full'
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadExperimentState = (): ExperimentState => {
|
const loadExperimentState = (): ExperimentState => {
|
||||||
@ -168,8 +171,18 @@ const loadExperimentState = (): ExperimentState => {
|
|||||||
blockDisplayMode = parsed.stackedBlocksEnabled ? 'stacked' : 'traditional';
|
blockDisplayMode = parsed.stackedBlocksEnabled ? 'stacked' : 'traditional';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let compactMessageDisplay: CompactMessageDisplay =
|
||||||
|
defaultExperimentState().compactMessageDisplay;
|
||||||
|
if (
|
||||||
|
typeof parsed?.compactMessageDisplay === 'string' &&
|
||||||
|
['full', 'brief'].includes(parsed.compactMessageDisplay)
|
||||||
|
) {
|
||||||
|
compactMessageDisplay = parsed.compactMessageDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
blockDisplayMode
|
blockDisplayMode,
|
||||||
|
compactMessageDisplay
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('无法读取实验功能设置:', error);
|
console.warn('无法读取实验功能设置:', error);
|
||||||
@ -760,6 +773,13 @@ export const usePersonalizationStore = defineStore('personalization', {
|
|||||||
};
|
};
|
||||||
this.persistExperiments();
|
this.persistExperiments();
|
||||||
},
|
},
|
||||||
|
setCompactMessageDisplay(mode: CompactMessageDisplay) {
|
||||||
|
this.experiments = {
|
||||||
|
...this.experiments,
|
||||||
|
compactMessageDisplay: mode
|
||||||
|
};
|
||||||
|
this.persistExperiments();
|
||||||
|
},
|
||||||
setCommunicationStyle(style: 'default' | 'human_like') {
|
setCommunicationStyle(style: 'default' | 'human_like') {
|
||||||
this.form = {
|
this.form = {
|
||||||
...this.form,
|
...this.form,
|
||||||
|
|||||||
@ -511,6 +511,38 @@
|
|||||||
border: 1px solid var(--theme-control-border);
|
border: 1px solid var(--theme-control-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 简略信息:以横跨显示区域的横线包裹一行概要文字(类似 markdown 的 --- 分隔线)。 */
|
||||||
|
.user-message.user-message--brief {
|
||||||
|
align-items: stretch;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message.user-message--brief .compact-brief-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
width: min(760px, 92%);
|
||||||
|
margin: 2px auto;
|
||||||
|
color: var(--claude-text-secondary);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message.user-message--brief .compact-brief-line::before,
|
||||||
|
.user-message.user-message--brief .compact-brief-line::after {
|
||||||
|
content: '';
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: 0;
|
||||||
|
border-top: 1px solid var(--theme-control-border, var(--claude-border));
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message.user-message--brief .compact-brief-text {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
font-size: 12.5px;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
.user-message .message-text .user-skill-link {
|
.user-message .message-text .user-skill-link {
|
||||||
color: #2563eb;
|
color: #2563eb;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@ -6,6 +6,7 @@ const COMPACT_FALLBACK_SOURCES = new Set([
|
|||||||
'guidance',
|
'guidance',
|
||||||
'goal_review',
|
'goal_review',
|
||||||
'compression',
|
'compression',
|
||||||
|
'compression_handoff',
|
||||||
'sub_agent',
|
'sub_agent',
|
||||||
'background_command'
|
'background_command'
|
||||||
]);
|
]);
|
||||||
@ -72,7 +73,7 @@ export function messageStartsWork(message: any): boolean {
|
|||||||
if (source === 'guidance' || source === 'goal_prompt' || source === 'skill') {
|
if (source === 'guidance' || source === 'goal_prompt' || source === 'skill') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (source === 'goal_review' || source === 'compression' || isLegacyGoalReview(message)) {
|
if (source === 'goal_review' || source === 'compression' || source === 'compression_handoff' || isLegacyGoalReview(message)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return LEGACY_STARTS_WORK_SOURCES.has(source || 'user');
|
return LEGACY_STARTS_WORK_SOURCES.has(source || 'user');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user