fix(ui): smooth transitions and align settings layout #3596
ci.yml
on: pull_request
Frontend Build
2m 48s
CLI Tests (ubuntu-latest)
4m 22s
Matrix: rust-build-check
Annotations
2 errors
|
Frontend Build
Process completed with exit code 1.
|
|
src/app/startup/startupPerformanceContract.test.ts > startup performance contract > keeps settings config panels lazy by active tab:
src/web-ui/src/app/startup/startupPerformanceContract.test.ts#L397
AssertionError: expected '/**\n * SettingsScene — content-only …' to contain 'lazy(() => import(\'../../../infrastr…'
- Expected
+ Received
- lazy(() => import('../../../infrastructure/config/components/AIModelConfig'))
+ /**
+ * SettingsScene — content-only renderer for the Settings scene.
+ *
+ * The left-side navigation lives in SettingsNav (rendered by NavPanel via
+ * nav-registry). This component only renders the active config content panel
+ * driven by settingsStore.activeTab.
+ */
+
+ import React, {
+ Suspense,
+ useEffect,
+ useLayoutEffect,
+ useRef,
+ useState,
+ } from 'react';
+ import { useSettingsStore } from './settingsStore';
+ import type { ConfigTab } from './settingsConfig';
+ import {
+ AcpAgentsConfig,
+ AIModelConfig,
+ AppearanceConfig,
+ ArchivedSessionsConfig,
+ BasicsConfig,
+ EditorConfig,
+ ExternalSourcesConfig,
+ KeyboardShortcutsTab,
+ McpToolsConfig,
+ MemoriesConfig,
+ QuickActionsConfig,
+ ReviewConfig,
+ SessionPermissionsConfig,
+ SessionPersonalizationConfig,
+ } from './settingsContentRegistry';
+ import './SettingsScene.scss';
+
+ // Keep in sync with settings-content-exit in SettingsScene.scss.
+ const SETTINGS_CONTENT_EXIT_DURATION_MS = 180;
+
+ function SettingsSceneLoading() {
+ return (
+ <div className="bitfun-settings-scene__loading" aria-busy="true" aria-hidden="true">
+ <div className="bitfun-settings-scene__loading-line bitfun-settings-scene__loading-line--title" />
+ <div className="bitfun-settings-scene__loading-line" />
+ <div className="bitfun-settings-scene__loading-line" />
+ <div className="bitfun-settings-scene__loading-block" />
+ </div>
+ );
+ }
+
+ function resolveSettingsContent(tab: ConfigTab): React.ComponentType | null {
+ switch (tab) {
+ case 'basics': return BasicsConfig;
+ case 'appearance': return AppearanceConfig;
+ case 'models': return AIModelConfig;
+ case 'archived-sessions': return ArchivedSessionsConfig;
+ case 'session-personalization': return SessionPersonalizationConfig;
+ case 'session-permissions': return SessionPermissionsConfig;
+ case 'quick-actions': return QuickActionsConfig;
+ case 'review': return ReviewConfig;
+ case 'memories': return MemoriesConfig;
+ case 'mcp-tools': return McpToolsConfig;
+ case 'external-sources': return ExternalSourcesConfig;
+ case 'acp-agents': return AcpAgentsConfig;
+ case 'editor': return EditorConfig;
+ case 'keyboard': return KeyboardShortcutsTab;
+ default: return null;
+ }
+ }
+
+ const SettingsScene: React.FC = () => {
+ const activeTab = useSettingsStore(s => s.activeTab);
+ const setActiveTab = useSettingsStore(s => s.setActiveTab);
+
+ const resolvedTab: ConfigTab =
+ (activeTab as string) === 'session-config' ? 'session-personalization' : activeTab;
+ const [outgoingTab, setOutgoingTab] = useState<ConfigTab | null>(null);
+ const previousTabRef = useRef<ConfigTab>(resolvedTab);
+
+ useEffect(() => {
+ /** Legacy merged session settings tab removed in favor of two panels. */
+ if ((activeTab as string) === 'session-config') {
+ setActiveTab('session-personalization');
+ }
+ }, [activeTab, setActiveTab]);
+
+ // Derive the previous tab during render so React keeps its keyed subtree
+ // mounted in the same commit that introduces the incoming page.
+ const renderedOutgoingTab = previousTabRef.current !== resolvedTab
+ ? previousTabRef.current
+ : outgoingTab;
+
+ useLayoutEffect(() => {
+ const previousTab = previousTabRef.current;
+ previousTabRef.current = resolvedTab;
+ if (previousTab === resolvedTab) return;
+
+ setOutgoingTab(previousTab);
+ const exitTimer = window.setTimeout(() => {
+ setOutgoingTab(current => current === previousTab ? null : current);
+ }, SETTINGS_CONTEN
|