From fb704828ba5e05ee401891a641c3db805834a9f5 Mon Sep 17 00:00:00 2001 From: thebeyondr <19380973+thebeyondr@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:55:12 -0500 Subject: [PATCH 01/42] =?UTF-8?q?feat(ui):=20=E2=9C=A8=20add=20collapsible?= =?UTF-8?q?=20sidebar=20navigation=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create new sidebar context with collapse/pin functionality - Add hover-to-expand and keyboard shortcuts (Cmd/Ctrl+B) - Implement smooth transitions and backdrop overlay --- src/contexts/SidebarContext.tsx | 101 ++++++++++++++++++++++++++++++++ src/layout/index.tsx | 8 ++- src/pages/_app.tsx | 5 +- 3 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 src/contexts/SidebarContext.tsx diff --git a/src/contexts/SidebarContext.tsx b/src/contexts/SidebarContext.tsx new file mode 100644 index 0000000000..b377902913 --- /dev/null +++ b/src/contexts/SidebarContext.tsx @@ -0,0 +1,101 @@ +import React, { createContext, useContext, useCallback, useEffect, useState } from 'react' + +interface SidebarContextType { + isCollapsed: boolean + isInitialized: boolean + isPinned: boolean + toggle: () => void + setCollapsed: (collapsed: boolean) => void + setPinned: (pinned: boolean) => void +} + +const SidebarContext = createContext(null) + +const STORAGE_KEY = 'defillama-sidebar-collapsed' + +export function SidebarProvider({ children }: { children: React.ReactNode }) { + const [isCollapsed, setIsCollapsed] = useState(false) + const [isInitialized, setIsInitialized] = useState(false) + const [isPinned, setIsPinned] = useState(false) + + // Initialize state from localStorage + useEffect(() => { + const stored = localStorage.getItem(STORAGE_KEY) + console.log('Initializing sidebar state from localStorage:', stored) + if (stored !== null) { + const parsedValue = JSON.parse(stored) + console.log('Setting initial collapsed state to:', parsedValue) + setIsCollapsed(parsedValue) + } + setIsInitialized(true) + }, []) + + // Persist state to localStorage + useEffect(() => { + console.log('Persisting sidebar state to localStorage:', isCollapsed) + localStorage.setItem(STORAGE_KEY, JSON.stringify(isCollapsed)) + }, [isCollapsed]) + + // Toggle function + const toggle = useCallback(() => { + setIsCollapsed((prev) => { + console.log('Toggling sidebar from', prev, 'to', !prev) + const newState = !prev + // When expanding via toggle (button or keyboard), pin it + // When collapsing, unpin it + setIsPinned(!newState) + return newState + }) + }, []) + + // Set specific state + const setCollapsed = useCallback((collapsed: boolean) => { + setIsCollapsed(collapsed) + }, []) + + // Set pinned state + const setPinned = useCallback((pinned: boolean) => { + setIsPinned(pinned) + }, []) + + // Keyboard shortcut support (Cmd/Ctrl + B) + useEffect(() => { + let lastToggleTime = 0 + const handleKeyDown = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && event.key === 'b') { + event.preventDefault() + // Debounce to prevent double-firing in React Strict Mode + const now = Date.now() + if (now - lastToggleTime < 100) return + lastToggleTime = now + toggle() + } + } + + document.addEventListener('keydown', handleKeyDown) + return () => document.removeEventListener('keydown', handleKeyDown) + }, [toggle]) + + return ( + + {children} + + ) +} + +export function useSidebarState() { + const context = useContext(SidebarContext) + if (!context) { + throw new Error('useSidebarState must be used within a SidebarProvider') + } + return context +} \ No newline at end of file diff --git a/src/layout/index.tsx b/src/layout/index.tsx index fad0d34050..e649b24ff9 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -29,13 +29,14 @@ function Layout({ ...props }: ILayoutProps) { const isClient = useIsClient() + return ( <> + ) }) diff --git a/src/components/Nav/ThemeSwitch.tsx b/src/components/Nav/ThemeSwitch.tsx index 3d37d9c203..2d2fabc350 100644 --- a/src/components/Nav/ThemeSwitch.tsx +++ b/src/components/Nav/ThemeSwitch.tsx @@ -1,12 +1,17 @@ import * as React from 'react' import { Icon } from '~/components/Icon' import { useDarkModeManager } from '~/contexts/LocalStorage' +import { useSidebarState } from '~/contexts/SidebarContext' export function ThemeSwitch() { const [darkMode, toggleDarkMode] = useDarkModeManager() + const { isCollapsed } = useSidebarState() return ( - - + From ad630c524e889d8bb68ae502cf7c10d3942c180f Mon Sep 17 00:00:00 2001 From: thebeyondr <19380973+thebeyondr@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:58:42 -0500 Subject: [PATCH 12/42] refactor(nav): improve NavGroup layout and accessibility - Update responsive class names for better consistency - Simplify label display based on expansion state - Add screen reader support for collapsed state --- src/components/Nav/NavGroup.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/Nav/NavGroup.tsx b/src/components/Nav/NavGroup.tsx index bc724eadc7..13a855a13a 100644 --- a/src/components/Nav/NavGroup.tsx +++ b/src/components/Nav/NavGroup.tsx @@ -165,20 +165,22 @@ export const NavGroup = React.memo(function NavGroup({ {group.icon ? ( ) : group.emoji ? ( {group.emoji} ) : null} -
- - {group.label} - -
+ {isExpanded ? ( +
+ + {group.label} + +
+ ) : ( + {group.label} + )} {isExpanded && ( Date: Thu, 9 Oct 2025 12:59:59 -0500 Subject: [PATCH 13/42] feat(nav): add Metrics, Account, and Custom Dashboards links - Add new links to primary navigation - Improve Careers link formatting in resources section --- src/components/Nav/navStructure.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/Nav/navStructure.ts b/src/components/Nav/navStructure.ts index 236d0e7a34..0d73022122 100644 --- a/src/components/Nav/navStructure.ts +++ b/src/components/Nav/navStructure.ts @@ -30,7 +30,10 @@ export type NavItem = NavLink | NavGroup | NavSeparator // Main navigation structure export const primaryNavigation: NavItem[] = [ - { type: 'link', label: 'Overview', route: '/', icon: 'house' } + { type: 'link', label: 'Overview', route: '/', icon: 'house' }, + { type: 'link', label: 'Metrics', route: '/metrics', icon: 'bar-chart-2' }, + { type: 'link', label: 'Account', route: '/subscription', icon: 'user' }, + { type: 'link', label: 'Custom Dashboards', route: '/pro', icon: 'blocks' } ] // Secondary navigation - reserved for pinned routes (dynamically populated) @@ -91,7 +94,12 @@ export const resourcesNavigation: NavItem[] = [ route: 'https://docs.llama.fi/list-your-project/submit-a-project', external: true }, - { type: 'link', label: 'Careers', route: 'https://github.com/DefiLlama/careers/blob/master/README.md', external: true } + { + type: 'link', + label: 'Careers', + route: 'https://github.com/DefiLlama/careers/blob/master/README.md', + external: true + } ] }, { From 6bab5ffedb4e3d83dd42ada0dd4ea9a2db8185c8 Mon Sep 17 00:00:00 2001 From: thebeyondr <19380973+thebeyondr@users.noreply.github.com> Date: Thu, 9 Oct 2025 13:00:11 -0500 Subject: [PATCH 14/42] refactor(nav): streamline DesktopNav component and enhance layout - Remove console log for cleaner code - Consolidate navigation links into a single NavItems component with attention handling - Adjust padding and layout for improved visual consistency in various sections --- src/components/Nav/Desktop.tsx | 53 +++++++++------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/src/components/Nav/Desktop.tsx b/src/components/Nav/Desktop.tsx index 077015b5be..9139cddd2e 100644 --- a/src/components/Nav/Desktop.tsx +++ b/src/components/Nav/Desktop.tsx @@ -79,8 +79,6 @@ export const DesktopNav = React.memo(function DesktopNav({ const showOverlay = !isCollapsed && !isPinned const dynamicNavWidth = isExpanded ? 'w-[244px]' : 'w-[80px]' - console.log('Desktop Nav State:', { isCollapsed, isPinned, isExpanded, showOverlay }) - return ( <> {/* Backdrop - only show when temporarily expanded (not pinned) */} @@ -137,37 +135,10 @@ export const DesktopNav = React.memo(function DesktopNav({
{/* Primary Navigation */}
- - - {/* Metrics Link */} - - - {/* Account and Custom Dashboards */} - - + item.type === 'link' && item.route === '/subscription' ? { ...item, attention: accountAttention } : item + )} isExpanded={isExpanded} />
@@ -178,7 +149,7 @@ export const DesktopNav = React.memo(function DesktopNav({
{/* Pinned header - text when expanded, icon with badge when collapsed */} {isExpanded ? ( -

Pinned

+

Pinned

) : (
@@ -224,7 +195,7 @@ export const DesktopNav = React.memo(function DesktopNav({ {/* Resources - Footer Section */}

@@ -242,11 +213,11 @@ export const DesktopNav = React.memo(function DesktopNav({ {/* Legal Footer */}

-
+
Privacy @@ -258,12 +229,14 @@ export const DesktopNav = React.memo(function DesktopNav({
-
-
+
+
}> - +
+ +
From 743a7a1bd8eb32725c11a6fba9c79c4331a024af Mon Sep 17 00:00:00 2001 From: thebeyondr <19380973+thebeyondr@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:55:12 -0500 Subject: [PATCH 15/42] =?UTF-8?q?feat(ui):=20=E2=9C=A8=20add=20collapsible?= =?UTF-8?q?=20sidebar=20navigation=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create new sidebar context with collapse/pin functionality - Add hover-to-expand and keyboard shortcuts (Cmd/Ctrl+B) - Implement smooth transitions and backdrop overlay --- src/contexts/SidebarContext.tsx | 101 ++++++++++++++++++++++++++++++++ src/layout/index.tsx | 8 ++- src/pages/_app.tsx | 5 +- 3 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 src/contexts/SidebarContext.tsx diff --git a/src/contexts/SidebarContext.tsx b/src/contexts/SidebarContext.tsx new file mode 100644 index 0000000000..b377902913 --- /dev/null +++ b/src/contexts/SidebarContext.tsx @@ -0,0 +1,101 @@ +import React, { createContext, useContext, useCallback, useEffect, useState } from 'react' + +interface SidebarContextType { + isCollapsed: boolean + isInitialized: boolean + isPinned: boolean + toggle: () => void + setCollapsed: (collapsed: boolean) => void + setPinned: (pinned: boolean) => void +} + +const SidebarContext = createContext(null) + +const STORAGE_KEY = 'defillama-sidebar-collapsed' + +export function SidebarProvider({ children }: { children: React.ReactNode }) { + const [isCollapsed, setIsCollapsed] = useState(false) + const [isInitialized, setIsInitialized] = useState(false) + const [isPinned, setIsPinned] = useState(false) + + // Initialize state from localStorage + useEffect(() => { + const stored = localStorage.getItem(STORAGE_KEY) + console.log('Initializing sidebar state from localStorage:', stored) + if (stored !== null) { + const parsedValue = JSON.parse(stored) + console.log('Setting initial collapsed state to:', parsedValue) + setIsCollapsed(parsedValue) + } + setIsInitialized(true) + }, []) + + // Persist state to localStorage + useEffect(() => { + console.log('Persisting sidebar state to localStorage:', isCollapsed) + localStorage.setItem(STORAGE_KEY, JSON.stringify(isCollapsed)) + }, [isCollapsed]) + + // Toggle function + const toggle = useCallback(() => { + setIsCollapsed((prev) => { + console.log('Toggling sidebar from', prev, 'to', !prev) + const newState = !prev + // When expanding via toggle (button or keyboard), pin it + // When collapsing, unpin it + setIsPinned(!newState) + return newState + }) + }, []) + + // Set specific state + const setCollapsed = useCallback((collapsed: boolean) => { + setIsCollapsed(collapsed) + }, []) + + // Set pinned state + const setPinned = useCallback((pinned: boolean) => { + setIsPinned(pinned) + }, []) + + // Keyboard shortcut support (Cmd/Ctrl + B) + useEffect(() => { + let lastToggleTime = 0 + const handleKeyDown = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && event.key === 'b') { + event.preventDefault() + // Debounce to prevent double-firing in React Strict Mode + const now = Date.now() + if (now - lastToggleTime < 100) return + lastToggleTime = now + toggle() + } + } + + document.addEventListener('keydown', handleKeyDown) + return () => document.removeEventListener('keydown', handleKeyDown) + }, [toggle]) + + return ( + + {children} + + ) +} + +export function useSidebarState() { + const context = useContext(SidebarContext) + if (!context) { + throw new Error('useSidebarState must be used within a SidebarProvider') + } + return context +} \ No newline at end of file diff --git a/src/layout/index.tsx b/src/layout/index.tsx index 1ca9133b57..b3a8055c0b 100644 --- a/src/layout/index.tsx +++ b/src/layout/index.tsx @@ -32,13 +32,14 @@ function Layout({ ...props }: ILayoutProps) { const isClient = useIsClient() + return ( <>
-
- +
{chartType === 'Total Market Cap' && ( }> +
+ +
)} @@ -152,6 +154,10 @@ export function ChainsWithStablecoins({ valueSymbol="$" hideDefaultLegend={true} hideGradient={true} + chartOptions={chartOptions} + customComponents={ + + } /> )} @@ -165,12 +171,21 @@ export function ChainsWithStablecoins({ hideDefaultLegend={true} hideGradient={true} expandTo100Percent={true} + chartOptions={chartOptions} + customComponents={ + + } /> )} {chartType === 'Pie' && ( }> - + + } + /> )}
@@ -180,3 +195,14 @@ export function ChainsWithStablecoins({ ) } + +const chartOptions = { + grid: { + left: 12, + bottom: 68, + top: 12, + right: 12, + outerBoundsMode: 'same', + outerBoundsContain: 'axisLabel' + } +} diff --git a/src/containers/Stablecoins/ChartSelector.tsx b/src/containers/Stablecoins/ChartSelector.tsx index 1a2026c589..6127ffa386 100644 --- a/src/containers/Stablecoins/ChartSelector.tsx +++ b/src/containers/Stablecoins/ChartSelector.tsx @@ -16,7 +16,7 @@ export function ChartSelector({ options, selectedChart, onClick }: IProps) { return ( - + diff --git a/src/containers/Stablecoins/StablecoinsByChain.tsx b/src/containers/Stablecoins/StablecoinsByChain.tsx index b59c903456..a8a435ec34 100644 --- a/src/containers/Stablecoins/StablecoinsByChain.tsx +++ b/src/containers/Stablecoins/StablecoinsByChain.tsx @@ -281,12 +281,10 @@ export function StablecoinsByChain({

- - {chartType === 'Total Market Cap' && ( }> + } + chartOptions={chartOptions} /> )} @@ -310,6 +312,10 @@ export function StablecoinsByChain({ hideDefaultLegend={true} hideGradient={true} stackColors={tokenColors} + customComponents={ + + } + chartOptions={chartOptions} /> )} @@ -324,12 +330,22 @@ export function StablecoinsByChain({ hideGradient={true} expandTo100Percent={true} stackColors={tokenColors} + customComponents={ + + } + chartOptions={chartOptions} /> )} {chartType === 'Pie' && ( }> - + + } + /> )} {chartType === 'Token Inflows' && tokenInflows && ( @@ -343,12 +359,22 @@ export function StablecoinsByChain({ key={tokenInflowNames} // escape hatch to rerender state in legend options chartOptions={inflowsChartOptions} stackColors={tokenColors} + customComponents={ + + } /> )} {chartType === 'USD Inflows' && usdInflows && ( }> - + + } + /> )}
@@ -407,3 +433,13 @@ const tokenColors = { FDUSD: '#00FF00', Others: '#FF1493' } +const chartOptions = { + grid: { + left: 12, + bottom: 68, + top: 12, + right: 12, + outerBoundsMode: 'same', + outerBoundsContain: 'axisLabel' + } +} From aec23fa2a7e6731decffb5c52e3506629cc37cef Mon Sep 17 00:00:00 2001 From: mintdart <96025197+mintdart@users.noreply.github.com> Date: Tue, 7 Oct 2025 05:52:00 -0700 Subject: [PATCH 17/42] update styles --- src/containers/LlamaAI/index.tsx | 45 ++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/containers/LlamaAI/index.tsx b/src/containers/LlamaAI/index.tsx index a2f0ea9369..74debc3193 100644 --- a/src/containers/LlamaAI/index.tsx +++ b/src/containers/LlamaAI/index.tsx @@ -1,4 +1,4 @@ -import { RefObject, useCallback, useDeferredValue, useEffect, useRef, useState } from 'react' +import { RefObject, startTransition, useCallback, useDeferredValue, useEffect, useRef, useState } from 'react' import { useRouter } from 'next/router' import * as Ariakit from '@ariakit/react' import { useMutation } from '@tanstack/react-query' @@ -1080,7 +1080,7 @@ export function LlamaAI({ initialSessionId, sharedSession, readOnly = false, sho
{ const [value, setValue] = useState('') + const deferredValue = useDeferredValue(value) + const onKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter' && !event.shiftKey && !isStreaming) { event.preventDefault() @@ -1149,10 +1151,6 @@ const PromptInput = ({ } } - const onChange = (event: React.ChangeEvent) => { - setValue(event.target.value) - } - return ( <>