From e2fe9f6dbd7e7706c139d544ca37782ca11d59aa Mon Sep 17 00:00:00 2001 From: wsp1911 Date: Wed, 25 Mar 2026 21:38:00 +0800 Subject: [PATCH 1/2] fix(log): reduce log files limit --- src/apps/desktop/src/lib.rs | 2 +- src/apps/desktop/src/logging.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apps/desktop/src/lib.rs b/src/apps/desktop/src/lib.rs index 0368bf7f..d49f8cc5 100644 --- a/src/apps/desktop/src/lib.rs +++ b/src/apps/desktop/src/lib.rs @@ -144,7 +144,7 @@ pub async fn run() { .level_for("portable_pty", log::LevelFilter::Info) .level_for("russh", log::LevelFilter::Info) .targets(log_targets) - .rotation_strategy(RotationStrategy::KeepSome(30)) + .rotation_strategy(RotationStrategy::KeepSome(3)) .max_file_size(10 * 1024 * 1024) .timezone_strategy(TimezoneStrategy::UseLocal) .clear_format() diff --git a/src/apps/desktop/src/logging.rs b/src/apps/desktop/src/logging.rs index 5a501208..a9ee2136 100644 --- a/src/apps/desktop/src/logging.rs +++ b/src/apps/desktop/src/logging.rs @@ -12,7 +12,7 @@ use std::thread; use tauri_plugin_log::{fern, Target, TargetKind}; const SESSION_DIR_PATTERN: &str = r"^\d{8}T\d{6}$"; -const MAX_LOG_SESSIONS: usize = 50; +const MAX_LOG_SESSIONS: usize = 10; static SESSION_LOG_DIR: OnceLock = OnceLock::new(); // Default to Debug in early development for easier diagnostics static CURRENT_LOG_LEVEL: AtomicU8 = AtomicU8::new(level_filter_to_u8(log::LevelFilter::Debug)); From 7c220ec766b29d1de38e86bada577b3a35e48a7e Mon Sep 17 00:00:00 2001 From: wsp1911 Date: Wed, 25 Mar 2026 21:38:00 +0800 Subject: [PATCH 2/2] fix(web-ui): refine explore group streaming fade behavior - only show streaming fades after the explore group reaches its max scroll height - make top and bottom fades depend on actual scroll position - keep fade visibility symmetric for non-top and non-bottom states --- .../modern/ExploreGroupRenderer.tsx | 52 +++++++++++++++++-- .../components/modern/ExploreRegion.scss | 26 +++++++--- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/web-ui/src/flow_chat/components/modern/ExploreGroupRenderer.tsx b/src/web-ui/src/flow_chat/components/modern/ExploreGroupRenderer.tsx index a9d683df..d1bdd6f7 100644 --- a/src/web-ui/src/flow_chat/components/modern/ExploreGroupRenderer.tsx +++ b/src/web-ui/src/flow_chat/components/modern/ExploreGroupRenderer.tsx @@ -3,7 +3,7 @@ * Renders merged explore-only rounds as a collapsible region. */ -import React, { useRef, useMemo, useCallback, useEffect } from 'react'; +import React, { useRef, useMemo, useCallback, useEffect, useState } from 'react'; import { ChevronRight } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import type { FlowItem, FlowToolItem, FlowTextItem, FlowThinkingItem } from '../../types/flow-chat'; @@ -26,6 +26,7 @@ export const ExploreGroupRenderer: React.FC = ({ }) => { const { t } = useTranslation('flow-chat'); const containerRef = useRef(null); + const [scrollState, setScrollState] = useState({ hasScroll: false, atTop: true, atBottom: true }); const { exploreGroupStates, @@ -61,6 +62,19 @@ export const ExploreGroupRenderer: React.FC = ({ const isCollapsed = !isExpanded; const allowManualToggle = !isGroupStreaming; + const checkScrollState = useCallback(() => { + const el = containerRef.current; + if (!el) { + return; + } + + setScrollState({ + hasScroll: el.scrollHeight > el.clientHeight + 1, + atTop: el.scrollTop <= 5, + atBottom: el.scrollTop + el.clientHeight >= el.scrollHeight - 5, + }); + }, []); + useEffect(() => { if (isGroupStreaming && !hasExplicitState) { applyExpandedState(false, true, () => { @@ -95,10 +109,39 @@ export const ExploreGroupRenderer: React.FC = ({ requestAnimationFrame(() => { if (containerRef.current) { containerRef.current.scrollTop = containerRef.current.scrollHeight; + checkScrollState(); } }); } - }, [allItems, isCollapsed, isGroupStreaming]); + }, [allItems, checkScrollState, isCollapsed, isGroupStreaming]); + + useEffect(() => { + if (!isExpanded) { + setScrollState({ hasScroll: false, atTop: true, atBottom: true }); + return; + } + + const el = containerRef.current; + if (!el) { + return; + } + + const frameId = requestAnimationFrame(checkScrollState); + + if (typeof ResizeObserver === 'undefined') { + return () => cancelAnimationFrame(frameId); + } + + const observer = new ResizeObserver(() => { + checkScrollState(); + }); + observer.observe(el); + + return () => { + cancelAnimationFrame(frameId); + observer.disconnect(); + }; + }, [allItems, checkScrollState, isExpanded]); // Build summary text with i18n. const displaySummary = useMemo(() => { @@ -141,6 +184,9 @@ export const ExploreGroupRenderer: React.FC = ({ allowManualToggle ? 'explore-region--collapsible' : null, isCollapsed ? 'explore-region--collapsed' : 'explore-region--expanded', isGroupStreaming ? 'explore-region--streaming' : null, + scrollState.hasScroll ? 'explore-region--has-scroll' : null, + scrollState.atTop ? 'explore-region--at-top' : null, + scrollState.atBottom ? 'explore-region--at-bottom' : null, ].filter(Boolean).join(' '); return (
= ({ )}
-
+
{allItems.map((item, idx) => (