From 099de1e922f0ccdad7ed9a10101491bef8eab301 Mon Sep 17 00:00:00 2001
From: JOJO <1498581755@qq.com>
Date: Tue, 30 Dec 2025 10:32:09 +0800
Subject: [PATCH] feat: add blank chat hero and limit quick menu actions
---
static/src/App.vue | 87 ++++++++++---------
static/src/app.ts | 83 ++++++++++++++++--
.../styles/components/input/_composer.scss | 55 ++++++++++++
3 files changed, 176 insertions(+), 49 deletions(-)
diff --git a/static/src/App.vue b/static/src/App.vue
index 63f49f2..97c1671 100644
--- a/static/src/App.vue
+++ b/static/src/App.vue
@@ -127,6 +127,11 @@
/>
+
+
+
{{ blankWelcomeText }}
+
+
-
+
+
+
diff --git a/static/src/app.ts b/static/src/app.ts
index 670f31f..63a6fe9 100644
--- a/static/src/app.ts
+++ b/static/src/app.ts
@@ -148,6 +148,19 @@ const appOptions = {
historyLoading: false,
historyLoadingFor: null,
historyLoadSeq: 0,
+ blankHeroActive: false,
+ blankHeroExiting: false,
+ blankWelcomeText: '',
+ blankWelcomePool: [
+ '有什么可以帮忙的?',
+ '想了解些热点吗?',
+ '要我帮你完成作业吗?',
+ '整点代码?',
+ '随便聊点什么?',
+ '想让我帮你整理一下思路吗?',
+ '要不要我帮你写个小工具?',
+ '发我一句话,我来接着做。'
+ ],
mobileViewportQuery: null,
modeMenuOpen: false,
conversationListRequestSeq: 0,
@@ -318,6 +331,9 @@ const appOptions = {
composerBusy() {
const monitorLock = this.monitorIsLocked && this.chatDisplayMode === 'monitor';
return this.streamingUi || this.taskInProgress || monitorLock || this.stopRequested;
+ },
+ composerHeroActive() {
+ return this.blankHeroActive || this.blankHeroExiting;
}
},
@@ -342,6 +358,12 @@ const appOptions = {
inputMessage() {
this.autoResizeInput();
},
+ messages: {
+ deep: true,
+ handler() {
+ this.refreshBlankHeroState();
+ }
+ },
currentConversationId: {
immediate: false,
handler(newValue, oldValue) {
@@ -354,6 +376,7 @@ const appOptions = {
historyLoadingFor: this.historyLoadingFor,
historyLoadSeq: this.historyLoadSeq
});
+ this.refreshBlankHeroState();
this.logMessageState('watch:currentConversationId', { oldValue, newValue, skipConversationHistoryReload: this.skipConversationHistoryReload });
if (!newValue || typeof newValue !== 'string' || newValue.startsWith('temp_')) {
return;
@@ -1397,6 +1420,7 @@ const appOptions = {
const targetConversationId = this.currentConversationId;
if (!targetConversationId || targetConversationId.startsWith('temp_')) {
debugLog('没有当前对话ID,跳过历史加载');
+ this.refreshBlankHeroState();
return;
}
@@ -1481,16 +1505,17 @@ const appOptions = {
debugLog('尝试不显示错误弹窗,仅在控制台记录');
// 不显示alert,避免打断用户体验
this.logMessageState('fetchAndDisplayHistory:error-clear', { error: error?.message || String(error) });
- this.messages = [];
- this.logMessageState('fetchAndDisplayHistory:error-cleared');
+ this.messages = [];
+ this.logMessageState('fetchAndDisplayHistory:error-cleared');
+ }
+ } finally {
+ // 仅在本次加载仍是最新请求时清除 loading 状态
+ if (loadSeq === this.historyLoadSeq) {
+ this.historyLoading = false;
+ this.historyLoadingFor = null;
+ }
+ this.refreshBlankHeroState();
}
- } finally {
- // 仅在本次加载仍是最新请求时清除 loading 状态
- if (loadSeq === this.historyLoadSeq) {
- this.historyLoading = false;
- this.historyLoadingFor = null;
- }
- }
},
// ==========================================
@@ -2020,6 +2045,16 @@ const appOptions = {
return;
}
+ const wasBlank = this.isConversationBlank();
+ if (wasBlank) {
+ this.blankHeroExiting = true;
+ this.blankHeroActive = true;
+ setTimeout(() => {
+ this.blankHeroExiting = false;
+ this.blankHeroActive = false;
+ }, 320);
+ }
+
// 标记任务进行中,直到任务完成或用户手动停止
this.taskInProgress = true;
this.chatAddUserMessage(message);
@@ -2373,6 +2408,36 @@ const appOptions = {
this.uiSetPanelMenuOpen(false);
},
+ isConversationBlank() {
+ if (!Array.isArray(this.messages) || !this.messages.length) return true;
+ return !this.messages.some(
+ (msg) => msg && (msg.role === 'user' || msg.role === 'assistant')
+ );
+ },
+
+ pickWelcomeText() {
+ const pool = this.blankWelcomePool;
+ if (!Array.isArray(pool) || !pool.length) {
+ this.blankWelcomeText = '有什么可以帮忙的?';
+ return;
+ }
+ const idx = Math.floor(Math.random() * pool.length);
+ this.blankWelcomeText = pool[idx];
+ },
+
+ refreshBlankHeroState() {
+ const isBlank = this.isConversationBlank();
+ if (isBlank) {
+ if (!this.blankHeroExiting) {
+ this.pickWelcomeText();
+ }
+ this.blankHeroActive = true;
+ } else {
+ this.blankHeroActive = false;
+ this.blankHeroExiting = false;
+ }
+ },
+
applyToolSettingsSnapshot(categories) {
if (!Array.isArray(categories)) {
console.warn('[ToolSettings] Snapshot skipped: categories not array', categories);
diff --git a/static/src/styles/components/input/_composer.scss b/static/src/styles/components/input/_composer.scss
index aa5672f..80b1706 100644
--- a/static/src/styles/components/input/_composer.scss
+++ b/static/src/styles/components/input/_composer.scss
@@ -260,6 +260,61 @@
gap: 6px;
}
+/* Blank conversation hero */
+.chat-container {
+ position: relative;
+}
+
+.blank-hero-overlay {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ pointer-events: none;
+ z-index: 1;
+ gap: 10px;
+ padding-bottom: 140px;
+}
+
+.blank-hero-text {
+ font-size: 32px;
+ color: #2f3a4a;
+ font-weight: 600;
+}
+
+.blank-hero-overlay .icon-lg {
+ width: 48px;
+ height: 48px;
+}
+
+.composer-container {
+ position: relative;
+ transition: transform 0.3s ease;
+ z-index: 2;
+}
+
+.composer-container.blank-hero-mode {
+ transform: translateY(-38vh);
+}
+
+@media (max-width: 768px) {
+ .blank-hero-text {
+ font-size: 28px;
+ }
+ .blank-hero-overlay .icon-lg {
+ width: 44px;
+ height: 44px;
+ }
+ .composer-container.blank-hero-mode {
+ transform: translateY(-32vh);
+ }
+ .blank-hero-overlay {
+ padding-bottom: 120px;
+ }
+}
+
.quick-submenu.tool-submenu {
top: auto;
bottom: 0;