feat: 优化展会演示前端界面和功能
主要更新: - 重新设计首页UI,采用现代黑色科技风格 - 修复结果弹窗按钮显示问题,固定在底部 - 添加Duoduo Agent Logo和三击调试功能 - 添加右下角浮动查看结果按钮 - 优化功能卡片布局和对齐 - 简化按钮文字为'开始' - 移除首页演示案例内容 - 创建Windows启动脚本start_cn.bat 技术改进: - 使用深色主题配色方案 - 添加网格背景和动态光效 - 优化组件动画效果 - 改进响应式布局 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
500
n8n-n8n-1.109.2/start_cn.bat
Normal file
500
n8n-n8n-1.109.2/start_cn.bat
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
@echo off
|
||||||
|
REM Save original codepage
|
||||||
|
for /f "tokens=2 delims=:" %%a in ('chcp') do set OLDCP=%%a
|
||||||
|
chcp 65001 >nul 2>&1
|
||||||
|
REM n8n Chinese Version Quick Start Script (Windows)
|
||||||
|
REM Author: xiaoqi
|
||||||
|
REM Last Update: 2025-09-09
|
||||||
|
REM Compatibility: Windows 7/8/10/11
|
||||||
|
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
REM Display banner
|
||||||
|
:show_banner
|
||||||
|
echo ======================================
|
||||||
|
echo n8n Chinese Version Launcher
|
||||||
|
echo Version: n8n-1.109.2 with CN
|
||||||
|
echo Maintainer: xiaoqi
|
||||||
|
echo ======================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Parse command line arguments
|
||||||
|
set "DEV_MODE="
|
||||||
|
set "FORCE_BUILD="
|
||||||
|
set "CHECK_ONLY="
|
||||||
|
set "SKIP_DEPS="
|
||||||
|
set "USE_NPM="
|
||||||
|
set "CUSTOM_PORT=5678"
|
||||||
|
|
||||||
|
:parse_args
|
||||||
|
if "%~1"=="" goto main_execution
|
||||||
|
if /I "%~1"=="-h" goto show_help
|
||||||
|
if /I "%~1"=="--help" goto show_help
|
||||||
|
if /I "%~1"=="-d" (
|
||||||
|
set "DEV_MODE=true"
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
if /I "%~1"=="--dev" (
|
||||||
|
set "DEV_MODE=true"
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
if /I "%~1"=="-b" (
|
||||||
|
set "FORCE_BUILD=true"
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
if /I "%~1"=="--build" (
|
||||||
|
set "FORCE_BUILD=true"
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
if /I "%~1"=="-c" (
|
||||||
|
set "CHECK_ONLY=true"
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
if /I "%~1"=="--check" (
|
||||||
|
set "CHECK_ONLY=true"
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
if /I "%~1"=="--skip-deps" (
|
||||||
|
set "SKIP_DEPS=true"
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
if /I "%~1"=="--npm" (
|
||||||
|
set "USE_NPM=true"
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
if /I "%~1"=="-p" (
|
||||||
|
set "CUSTOM_PORT=%~2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
if /I "%~1"=="--port" (
|
||||||
|
set "CUSTOM_PORT=%~2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
goto parse_args
|
||||||
|
)
|
||||||
|
echo [ERROR] Unknown option: %~1
|
||||||
|
goto show_help
|
||||||
|
|
||||||
|
:main_execution
|
||||||
|
REM Check OS version
|
||||||
|
call :check_os_version
|
||||||
|
|
||||||
|
REM Check and change to correct directory
|
||||||
|
call :check_and_change_directory
|
||||||
|
if errorlevel 1 goto error_exit
|
||||||
|
|
||||||
|
REM Check dependencies
|
||||||
|
if not defined SKIP_DEPS (
|
||||||
|
echo [INFO] Checking system dependencies...
|
||||||
|
call :check_dependencies
|
||||||
|
if errorlevel 1 goto error_exit
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Check port
|
||||||
|
call :check_port
|
||||||
|
if errorlevel 1 goto error_exit
|
||||||
|
|
||||||
|
REM Stop existing processes
|
||||||
|
call :stop_existing
|
||||||
|
|
||||||
|
REM Setup environment
|
||||||
|
call :setup_environment
|
||||||
|
|
||||||
|
REM Check only mode
|
||||||
|
if defined CHECK_ONLY (
|
||||||
|
echo [SUCCESS] System check completed
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Install dependencies if needed
|
||||||
|
if not exist "node_modules\" (
|
||||||
|
echo [INFO] Installing dependencies...
|
||||||
|
if defined USE_NPM (
|
||||||
|
call :install_with_npm
|
||||||
|
) else (
|
||||||
|
call :install_with_pnpm
|
||||||
|
)
|
||||||
|
if errorlevel 1 goto error_exit
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Check and install run-script-os
|
||||||
|
if not exist "node_modules\run-script-os" (
|
||||||
|
echo [FIX] Installing run-script-os...
|
||||||
|
call npm install run-script-os
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Build or check build
|
||||||
|
if defined FORCE_BUILD (
|
||||||
|
echo [INFO] Force rebuilding project...
|
||||||
|
call :build_project
|
||||||
|
if errorlevel 1 goto error_exit
|
||||||
|
) else (
|
||||||
|
call :check_build
|
||||||
|
if errorlevel 1 goto error_exit
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Dev mode or normal start
|
||||||
|
if defined DEV_MODE (
|
||||||
|
echo [INFO] Starting n8n in development mode...
|
||||||
|
echo [WARNING] Dev mode takes longer to start...
|
||||||
|
if defined USE_NPM (
|
||||||
|
call npm run dev
|
||||||
|
) else (
|
||||||
|
call pnpm dev
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
call :start_n8n
|
||||||
|
)
|
||||||
|
goto end
|
||||||
|
|
||||||
|
REM ========== Functions ==========
|
||||||
|
|
||||||
|
:check_os_version
|
||||||
|
ver | findstr /i "5\.1\." >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo [WARNING] Windows XP detected
|
||||||
|
)
|
||||||
|
ver | findstr /i "6\.0\." >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo [INFO] Windows Vista detected
|
||||||
|
)
|
||||||
|
ver | findstr /i "6\.1\." >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo [INFO] Windows 7 detected
|
||||||
|
)
|
||||||
|
ver | findstr /i "6\.2\." >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo [INFO] Windows 8 detected
|
||||||
|
)
|
||||||
|
ver | findstr /i "6\.3\." >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo [INFO] Windows 8.1 detected
|
||||||
|
)
|
||||||
|
ver | findstr /i "10\.0\." >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo [INFO] Windows 10/11 detected
|
||||||
|
)
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:check_and_change_directory
|
||||||
|
if exist "package.json" if exist "packages" (
|
||||||
|
echo [INFO] Already in n8n project directory
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
|
||||||
|
set "SCRIPT_DIR=%~dp0"
|
||||||
|
if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
|
||||||
|
|
||||||
|
echo %SCRIPT_DIR% | findstr /I /C:"n8n-n8n-1.109.2" >nul
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo [INFO] Switching to n8n project directory
|
||||||
|
cd /d "%SCRIPT_DIR%" 2>nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd "%SCRIPT_DIR%" 2>nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Cannot change to directory: %SCRIPT_DIR%
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo [SUCCESS] Changed to correct n8n project directory
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
|
||||||
|
if exist "n8n-n8n-1.109.2\" (
|
||||||
|
cd n8n-n8n-1.109.2
|
||||||
|
) else if exist "..\n8n-n8n-1.109.2\" (
|
||||||
|
cd ..\n8n-n8n-1.109.2
|
||||||
|
) else if exist "..\..\n8n-n8n-1.109.2\" (
|
||||||
|
cd ..\..\n8n-n8n-1.109.2
|
||||||
|
) else (
|
||||||
|
echo [ERROR] n8n-n8n-1.109.2 directory not found
|
||||||
|
echo [INFO] Please run this script from the correct project directory
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exist "package.json" (
|
||||||
|
echo [ERROR] Invalid directory structure, missing package.json
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "packages" (
|
||||||
|
echo [ERROR] Invalid directory structure, missing packages directory
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [SUCCESS] Changed to correct n8n project directory
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:check_dependencies
|
||||||
|
where node >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
if exist "C:\Program Files\nodejs\node.exe" (
|
||||||
|
set "PATH=C:\Program Files\nodejs;%PATH%"
|
||||||
|
) else if exist "C:\Program Files (x86)\nodejs\node.exe" (
|
||||||
|
set "PATH=C:\Program Files (x86)\nodejs;%PATH%"
|
||||||
|
) else (
|
||||||
|
echo [ERROR] Node.js not installed or not in PATH
|
||||||
|
echo [INFO] Please visit https://nodejs.org to download and install
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [INFO] Node.js version:
|
||||||
|
node --version
|
||||||
|
|
||||||
|
if not defined USE_NPM (
|
||||||
|
where pnpm >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [WARNING] pnpm not installed, trying npm
|
||||||
|
set "USE_NPM=true"
|
||||||
|
) else (
|
||||||
|
echo [INFO] Using pnpm as package manager
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo [INFO] Using npm as package manager
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [SUCCESS] Dependencies check passed
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:install_with_pnpm
|
||||||
|
echo [INFO] Installing dependencies with pnpm...
|
||||||
|
where pnpm >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [INFO] Installing pnpm...
|
||||||
|
call npm install -g pnpm@8
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] pnpm installation failed
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
call pnpm install --no-frozen-lockfile
|
||||||
|
exit /b %errorlevel%
|
||||||
|
|
||||||
|
:install_with_npm
|
||||||
|
echo [WARNING] Using npm install
|
||||||
|
echo [INFO] Recommend installing pnpm: npm install -g pnpm
|
||||||
|
call npm install --legacy-peer-deps
|
||||||
|
exit /b %errorlevel%
|
||||||
|
|
||||||
|
:check_port
|
||||||
|
echo [INFO] Checking port %CUSTOM_PORT% availability...
|
||||||
|
netstat -ano 2>nul | findstr ":%CUSTOM_PORT%" >nul 2>&1
|
||||||
|
if not errorlevel 1 (
|
||||||
|
echo [WARNING] Port %CUSTOM_PORT% is already in use
|
||||||
|
echo [INFO] Checking process using the port...
|
||||||
|
netstat -ano | findstr ":%CUSTOM_PORT%"
|
||||||
|
echo.
|
||||||
|
|
||||||
|
set /p "kill_process=Terminate the process? (y/N): "
|
||||||
|
if /I "!kill_process!"=="y" (
|
||||||
|
echo [INFO] Terminating process...
|
||||||
|
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":%CUSTOM_PORT%"') do (
|
||||||
|
taskkill /PID %%a /F >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
tskill %%a >nul 2>&1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo [SUCCESS] Process terminated
|
||||||
|
timeout /t 2 /nobreak >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
ping -n 3 127.0.0.1 >nul
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
echo [ERROR] Cannot start n8n, port is in use
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:stop_existing
|
||||||
|
echo [INFO] Stopping existing n8n processes...
|
||||||
|
|
||||||
|
taskkill /F /IM node.exe /FI "WINDOWTITLE eq *n8n*" >nul 2>&1
|
||||||
|
taskkill /F /IM node.exe /FI "WINDOWTITLE eq *pnpm*" >nul 2>&1
|
||||||
|
taskkill /F /IM node.exe /FI "WINDOWTITLE eq *npm*" >nul 2>&1
|
||||||
|
|
||||||
|
timeout /t 2 /nobreak >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
ping -n 3 127.0.0.1 >nul
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [SUCCESS] Existing processes stopped
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:setup_environment
|
||||||
|
echo [INFO] Setting environment variables...
|
||||||
|
|
||||||
|
set "N8N_PORT=%CUSTOM_PORT%"
|
||||||
|
set "N8N_DEFAULT_LOCALE=zh-CN"
|
||||||
|
set "N8N_SECURE_COOKIE=false"
|
||||||
|
set "DB_SQLITE_POOL_SIZE=5"
|
||||||
|
set "N8N_RUNNERS_ENABLED=true"
|
||||||
|
set "N8N_BLOCK_ENV_ACCESS_IN_NODE=false"
|
||||||
|
set "NODE_OPTIONS=--max-old-space-size=4096"
|
||||||
|
|
||||||
|
echo [SUCCESS] Environment variables set
|
||||||
|
echo - N8N_PORT=%CUSTOM_PORT%
|
||||||
|
echo - N8N_DEFAULT_LOCALE=zh-CN
|
||||||
|
echo - N8N_SECURE_COOKIE=false
|
||||||
|
echo - DB_SQLITE_POOL_SIZE=5
|
||||||
|
echo - NODE_OPTIONS=--max-old-space-size=4096
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:check_build
|
||||||
|
if not exist "packages\cli\dist\" goto need_build
|
||||||
|
if not exist "packages\core\dist\" goto need_build
|
||||||
|
if not exist "packages\workflow\dist\" goto need_build
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:need_build
|
||||||
|
echo [WARNING] Project needs to be built
|
||||||
|
set /p "build_project=Build project now? This may take a few minutes (y/N): "
|
||||||
|
if /I "!build_project!"=="y" (
|
||||||
|
call :build_project
|
||||||
|
)
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:build_project
|
||||||
|
echo [INFO] Building project...
|
||||||
|
if defined USE_NPM (
|
||||||
|
call npm run build > build.log 2>&1
|
||||||
|
) else (
|
||||||
|
call pnpm build > build.log 2>&1
|
||||||
|
)
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Build failed, check build.log
|
||||||
|
type build.log | more
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo [SUCCESS] Project built successfully
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:start_n8n
|
||||||
|
echo [INFO] Starting n8n Chinese version...
|
||||||
|
|
||||||
|
set "DATETIME=%date:~0,4%%date:~5,2%%date:~8,2%-%time:~0,2%%time:~3,2%"
|
||||||
|
set "DATETIME=%DATETIME: =0%"
|
||||||
|
set "DATETIME=%DATETIME:/=%"
|
||||||
|
set "DATETIME=%DATETIME:\=%"
|
||||||
|
set "LOG_FILE=n8n-%DATETIME%.log"
|
||||||
|
|
||||||
|
echo [INFO] Starting n8n service...
|
||||||
|
echo [INFO] Log file: %LOG_FILE%
|
||||||
|
|
||||||
|
if defined USE_NPM (
|
||||||
|
start "n8n Server" /B cmd /c "npm start > %LOG_FILE% 2>&1"
|
||||||
|
) else (
|
||||||
|
start "n8n Server" /B cmd /c "pnpm start > %LOG_FILE% 2>&1"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Waiting for n8n to start
|
||||||
|
set "count=0"
|
||||||
|
:wait_loop
|
||||||
|
set /a count+=1
|
||||||
|
if %count% gtr 60 goto start_failed
|
||||||
|
|
||||||
|
netstat -an 2>nul | findstr ":%CUSTOM_PORT%" | findstr "LISTENING" >nul 2>&1
|
||||||
|
if not errorlevel 1 goto start_success
|
||||||
|
|
||||||
|
set /p "=." <nul
|
||||||
|
timeout /t 1 /nobreak >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
ping -n 2 127.0.0.1 >nul
|
||||||
|
)
|
||||||
|
goto wait_loop
|
||||||
|
|
||||||
|
:start_success
|
||||||
|
echo.
|
||||||
|
echo [SUCCESS] n8n started successfully!
|
||||||
|
echo.
|
||||||
|
echo ========== START INFO ==========
|
||||||
|
echo Access URL: http://localhost:%CUSTOM_PORT%
|
||||||
|
echo Interface Language: Chinese (zh-CN)
|
||||||
|
echo Log file: %LOG_FILE%
|
||||||
|
echo =================================
|
||||||
|
echo.
|
||||||
|
echo [INFO] Use Ctrl+C to stop service
|
||||||
|
echo [TIP] First visit requires account setup
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:start_failed
|
||||||
|
echo.
|
||||||
|
echo [ERROR] n8n failed to start
|
||||||
|
echo [INFO] Check log file: %LOG_FILE%
|
||||||
|
if exist "%LOG_FILE%" (
|
||||||
|
echo.
|
||||||
|
echo ===== ERROR LOG =====
|
||||||
|
type "%LOG_FILE%" | more
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo [INFO] Trying alternative start method...
|
||||||
|
call npx n8n
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Alternative method also failed
|
||||||
|
echo.
|
||||||
|
echo Please try:
|
||||||
|
echo 1. Run: npm install run-script-os
|
||||||
|
echo 2. Run: node packages/cli/bin/n8n
|
||||||
|
)
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:show_help
|
||||||
|
echo Usage: %~nx0 [options]
|
||||||
|
echo.
|
||||||
|
echo Options:
|
||||||
|
echo -h, --help Show this help
|
||||||
|
echo -d, --dev Start in development mode
|
||||||
|
echo -b, --build Force rebuild project
|
||||||
|
echo -c, --check Only check system status
|
||||||
|
echo -p, --port Specify port (default: 5678)
|
||||||
|
echo --skip-deps Skip dependency check
|
||||||
|
echo --npm Use npm instead of pnpm
|
||||||
|
echo.
|
||||||
|
echo Examples:
|
||||||
|
echo %~nx0 # Normal start (port 5678)
|
||||||
|
echo %~nx0 -p 8080 # Use port 8080
|
||||||
|
echo %~nx0 -d # Development mode
|
||||||
|
echo %~nx0 -b # Rebuild and start
|
||||||
|
echo %~nx0 --npm # Use npm
|
||||||
|
echo %~nx0 -p 3000 -d # Dev mode on port 3000
|
||||||
|
echo.
|
||||||
|
echo Troubleshooting:
|
||||||
|
echo 1. If pnpm install fails, use --npm option
|
||||||
|
echo 2. If port is in use, script will prompt to terminate process
|
||||||
|
echo 3. Check build.log when build fails
|
||||||
|
echo.
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:error_exit
|
||||||
|
echo [ERROR] Script execution failed
|
||||||
|
echo.
|
||||||
|
echo Common solutions:
|
||||||
|
echo 1. Ensure Node.js version is 18.x or 20.x
|
||||||
|
echo 2. Try running with administrator privileges
|
||||||
|
echo 3. Use --npm option if pnpm has issues
|
||||||
|
echo 4. Check network connection and proxy settings
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:end
|
||||||
|
REM Restore original codepage
|
||||||
|
if defined OLDCP (
|
||||||
|
chcp %OLDCP% >nul 2>&1
|
||||||
|
)
|
||||||
|
endlocal
|
||||||
|
exit /b 0
|
||||||
@@ -47,9 +47,9 @@ const ResultModal: React.FC<ResultModalProps> = ({ isOpen, onClose, onViewDetail
|
|||||||
className="fixed inset-0 flex items-center justify-center z-50 p-4"
|
className="fixed inset-0 flex items-center justify-center z-50 p-4"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<div className="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-hidden">
|
<div className="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-hidden flex flex-col">
|
||||||
{/* 头部 */}
|
{/* 头部 */}
|
||||||
<div className="relative bg-gradient-to-br from-green-500 to-emerald-600 p-8 text-white">
|
<div className="relative bg-gradient-to-br from-green-500 to-emerald-600 p-8 text-white flex-shrink-0">
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="absolute top-4 right-4 p-2 hover:bg-white/20 rounded-lg transition-colors"
|
className="absolute top-4 right-4 p-2 hover:bg-white/20 rounded-lg transition-colors"
|
||||||
@@ -74,7 +74,7 @@ const ResultModal: React.FC<ResultModalProps> = ({ isOpen, onClose, onViewDetail
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="p-6 overflow-y-auto flex-1">
|
||||||
{/* 统计信息 */}
|
{/* 统计信息 */}
|
||||||
<div className="grid grid-cols-3 gap-4 mb-6">
|
<div className="grid grid-cols-3 gap-4 mb-6">
|
||||||
{stats.map((stat, index) => (
|
{stats.map((stat, index) => (
|
||||||
@@ -138,11 +138,13 @@ const ResultModal: React.FC<ResultModalProps> = ({ isOpen, onClose, onViewDetail
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 操作按钮 */}
|
{/* 操作按钮 - 固定在底部 */}
|
||||||
|
<div className="p-6 bg-gray-50 border-t border-gray-200 flex-shrink-0">
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => window.open('http://localhost:4155', '_blank')}
|
onClick={() => window.open('http://localhost:4155/index.html', '_blank')}
|
||||||
className="px-8 py-3 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-xl font-medium hover:shadow-lg transform hover:scale-105 transition-all flex items-center justify-center gap-2"
|
className="px-8 py-3 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-xl font-medium hover:shadow-lg transform hover:scale-105 transition-all flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
<Eye className="w-5 h-5" />
|
<Eye className="w-5 h-5" />
|
||||||
|
|||||||
@@ -8,9 +8,27 @@ interface LandingPageProps {
|
|||||||
|
|
||||||
const LandingPage: React.FC<LandingPageProps> = ({ onStart }) => {
|
const LandingPage: React.FC<LandingPageProps> = ({ onStart }) => {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center p-8 relative">
|
<div className="min-h-screen flex items-center justify-center p-8 relative overflow-hidden bg-black">
|
||||||
{/* 背景图片和蒙版 */}
|
{/* 高级背景效果 */}
|
||||||
<div className="absolute inset-0 z-0">
|
<div className="absolute inset-0 z-0">
|
||||||
|
{/* 渐变背景 */}
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950" />
|
||||||
|
|
||||||
|
{/* 网格背景 */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 opacity-20"
|
||||||
|
style={{
|
||||||
|
backgroundImage: `linear-gradient(to right, #1e293b 1px, transparent 1px),
|
||||||
|
linear-gradient(to bottom, #1e293b 1px, transparent 1px)`,
|
||||||
|
backgroundSize: '50px 50px'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 动态光效 */}
|
||||||
|
<div className="absolute top-1/4 -left-1/4 w-96 h-96 bg-blue-500/30 rounded-full blur-[120px] animate-pulse" />
|
||||||
|
<div className="absolute bottom-1/4 -right-1/4 w-96 h-96 bg-purple-500/30 rounded-full blur-[120px] animate-pulse" />
|
||||||
|
|
||||||
|
{/* 图片叠加 */}
|
||||||
<div
|
<div
|
||||||
className="absolute inset-0"
|
className="absolute inset-0"
|
||||||
style={{
|
style={{
|
||||||
@@ -18,11 +36,9 @@ const LandingPage: React.FC<LandingPageProps> = ({ onStart }) => {
|
|||||||
backgroundSize: 'cover',
|
backgroundSize: 'cover',
|
||||||
backgroundPosition: 'center',
|
backgroundPosition: 'center',
|
||||||
backgroundRepeat: 'no-repeat',
|
backgroundRepeat: 'no-repeat',
|
||||||
opacity: 0.08
|
opacity: 0.03
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-gray-900/50 via-purple-900/30 to-blue-900/40" />
|
|
||||||
<div className="absolute inset-0 backdrop-blur-sm" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 主内容 */}
|
{/* 主内容 */}
|
||||||
@@ -38,9 +54,12 @@ const LandingPage: React.FC<LandingPageProps> = ({ onStart }) => {
|
|||||||
initial={{ scale: 0 }}
|
initial={{ scale: 0 }}
|
||||||
animate={{ scale: 1 }}
|
animate={{ scale: 1 }}
|
||||||
transition={{ delay: 0.2, type: "spring", stiffness: 200 }}
|
transition={{ delay: 0.2, type: "spring", stiffness: 200 }}
|
||||||
className="inline-flex items-center justify-center w-24 h-24 rounded-3xl bg-gradient-to-br from-blue-500 to-purple-500 shadow-2xl"
|
className="inline-flex items-center justify-center w-24 h-24 rounded-2xl bg-gradient-to-br from-slate-800 to-slate-900 shadow-2xl border border-slate-700/50"
|
||||||
|
style={{
|
||||||
|
boxShadow: '0 0 40px rgba(59, 130, 246, 0.2), 0 0 80px rgba(147, 51, 234, 0.1)'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Sparkles className="w-12 h-12 text-white" />
|
<Sparkles className="w-12 h-12 text-blue-400" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
@@ -49,10 +68,10 @@ const LandingPage: React.FC<LandingPageProps> = ({ onStart }) => {
|
|||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ delay: 0.4 }}
|
transition={{ delay: 0.4 }}
|
||||||
>
|
>
|
||||||
<h1 className="text-6xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
|
<h1 className="text-7xl font-bold text-white tracking-tight">
|
||||||
DuoDuo Agent
|
DuoDuo Agent
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mt-4 text-xl text-neutral-600 dark:text-neutral-400">
|
<p className="mt-4 text-xl text-slate-400 font-light tracking-wide">
|
||||||
多Agent协同 · 智能生成 · 专业方案
|
多Agent协同 · 智能生成 · 专业方案
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -62,54 +81,39 @@ const LandingPage: React.FC<LandingPageProps> = ({ onStart }) => {
|
|||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.6 }}
|
transition={{ delay: 0.6 }}
|
||||||
className="grid grid-cols-3 gap-6 mt-12"
|
className="grid grid-cols-3 gap-8 mt-16 max-w-3xl mx-auto"
|
||||||
>
|
>
|
||||||
<div className="glass-morphism rounded-2xl p-6 hover:scale-105 transition-transform">
|
<div className="relative group">
|
||||||
<Layers className="w-8 h-8 text-blue-500 mb-4" />
|
<div className="absolute inset-0 bg-gradient-to-r from-blue-600/20 to-cyan-600/20 rounded-2xl blur-xl group-hover:blur-2xl transition-all opacity-0 group-hover:opacity-100" />
|
||||||
<h3 className="font-semibold text-lg mb-2">7个专业Agent</h3>
|
<div className="relative bg-slate-900/50 backdrop-blur-xl rounded-2xl p-8 border border-slate-800 hover:border-slate-700 transition-all h-full flex flex-col items-center text-center">
|
||||||
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
<Layers className="w-10 h-10 text-blue-400 mb-4" />
|
||||||
|
<h3 className="font-semibold text-lg mb-2 text-white">7个专业Agent</h3>
|
||||||
|
<p className="text-sm text-slate-400">
|
||||||
覆盖策划全流程
|
覆盖策划全流程
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="glass-morphism rounded-2xl p-6 hover:scale-105 transition-transform">
|
<div className="relative group">
|
||||||
<Zap className="w-8 h-8 text-purple-500 mb-4" />
|
<div className="absolute inset-0 bg-gradient-to-r from-purple-600/20 to-pink-600/20 rounded-2xl blur-xl group-hover:blur-2xl transition-all opacity-0 group-hover:opacity-100" />
|
||||||
<h3 className="font-semibold text-lg mb-2">实时协作</h3>
|
<div className="relative bg-slate-900/50 backdrop-blur-xl rounded-2xl p-8 border border-slate-800 hover:border-slate-700 transition-all h-full flex flex-col items-center text-center">
|
||||||
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
<Zap className="w-10 h-10 text-purple-400 mb-4" />
|
||||||
|
<h3 className="font-semibold text-lg mb-2 text-white">实时协作</h3>
|
||||||
|
<p className="text-sm text-slate-400">
|
||||||
智能工作流编排
|
智能工作流编排
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="glass-morphism rounded-2xl p-6 hover:scale-105 transition-transform">
|
<div className="relative group">
|
||||||
<Sparkles className="w-8 h-8 text-green-500 mb-4" />
|
<div className="absolute inset-0 bg-gradient-to-r from-emerald-600/20 to-green-600/20 rounded-2xl blur-xl group-hover:blur-2xl transition-all opacity-0 group-hover:opacity-100" />
|
||||||
<h3 className="font-semibold text-lg mb-2">专业输出</h3>
|
<div className="relative bg-slate-900/50 backdrop-blur-xl rounded-2xl p-8 border border-slate-800 hover:border-slate-700 transition-all h-full flex flex-col items-center text-center">
|
||||||
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
<Sparkles className="w-10 h-10 text-emerald-400 mb-4" />
|
||||||
|
<h3 className="font-semibold text-lg mb-2 text-white">专业输出</h3>
|
||||||
|
<p className="text-sm text-slate-400">
|
||||||
完整策划方案
|
完整策划方案
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
|
||||||
|
|
||||||
{/* Demo Info */}
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
transition={{ delay: 0.8 }}
|
|
||||||
className="glass-morphism rounded-2xl p-8 text-left"
|
|
||||||
>
|
|
||||||
<h2 className="text-2xl font-semibold mb-4">演示案例</h2>
|
|
||||||
<div className="space-y-2 text-neutral-600 dark:text-neutral-400">
|
|
||||||
<p>
|
|
||||||
<span className="font-medium text-neutral-900 dark:text-neutral-100">项目:</span>
|
|
||||||
2024长三角国际新能源汽车与智能交通产业博览会
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span className="font-medium text-neutral-900 dark:text-neutral-100">规模:</span>
|
|
||||||
50,000平方米展览面积,350家参展商,预计50,000人次参观
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span className="font-medium text-neutral-900 dark:text-neutral-100">时长:</span>
|
|
||||||
约3分钟完整演示
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
@@ -117,17 +121,19 @@ const LandingPage: React.FC<LandingPageProps> = ({ onStart }) => {
|
|||||||
<motion.button
|
<motion.button
|
||||||
initial={{ opacity: 0, scale: 0.9 }}
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
transition={{ delay: 1 }}
|
transition={{ delay: 0.8 }}
|
||||||
whileHover={{ scale: 1.05 }}
|
whileHover={{ scale: 1.05 }}
|
||||||
whileTap={{ scale: 0.95 }}
|
whileTap={{ scale: 0.95 }}
|
||||||
onClick={onStart}
|
onClick={onStart}
|
||||||
className="group relative inline-flex items-center gap-3 px-8 py-4 bg-gradient-to-r from-blue-500 to-purple-500 text-white font-semibold text-lg rounded-2xl shadow-xl hover:shadow-2xl transition-all"
|
className="relative mt-12 px-16 py-4 bg-white text-black font-semibold text-lg rounded-xl transition-all duration-300 group"
|
||||||
|
style={{
|
||||||
|
boxShadow: '0 4px 30px rgba(255, 255, 255, 0.1)'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span>开始演示</span>
|
<span className="flex items-center gap-3">
|
||||||
|
开始
|
||||||
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
|
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
|
||||||
|
</span>
|
||||||
{/* Glow effect */}
|
|
||||||
<div className="absolute inset-0 rounded-2xl bg-gradient-to-r from-blue-500 to-purple-500 blur-xl opacity-50 group-hover:opacity-70 transition-opacity" />
|
|
||||||
</motion.button>
|
</motion.button>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
@@ -135,7 +141,7 @@ const LandingPage: React.FC<LandingPageProps> = ({ onStart }) => {
|
|||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ delay: 1.2 }}
|
transition={{ delay: 1.2 }}
|
||||||
className="text-sm text-neutral-500 dark:text-neutral-500 mt-8"
|
className="text-xs text-slate-600 mt-8 tracking-wide"
|
||||||
>
|
>
|
||||||
Powered by n8n Workflow · DeepSeek · Google Gemini
|
Powered by n8n Workflow · DeepSeek · Google Gemini
|
||||||
</motion.p>
|
</motion.p>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { useDemoStore } from '@/store/demoStore';
|
import { useDemoStore } from '@/store/demoStore';
|
||||||
import { Play, Pause, RotateCcw, Maximize2, Terminal, FileInput } from 'lucide-react';
|
import { Play, Pause, RotateCcw, Maximize2, Terminal, FileInput, Eye } from 'lucide-react';
|
||||||
import RequirementModal from '@/components/RequirementModal';
|
import RequirementModal from '@/components/RequirementModal';
|
||||||
import ResultModal from '@/components/ResultModal';
|
import ResultModal from '@/components/ResultModal';
|
||||||
|
|
||||||
@@ -46,9 +46,12 @@ const WorkflowPageV4 = () => {
|
|||||||
const [showResultModal, setShowResultModal] = useState(false);
|
const [showResultModal, setShowResultModal] = useState(false);
|
||||||
const [userRequirement, setUserRequirement] = useState('');
|
const [userRequirement, setUserRequirement] = useState('');
|
||||||
const [imageLoadingStates, setImageLoadingStates] = useState<{ [key: string]: boolean }>({});
|
const [imageLoadingStates, setImageLoadingStates] = useState<{ [key: string]: boolean }>({});
|
||||||
|
const [logoClickCount, setLogoClickCount] = useState(0);
|
||||||
|
const [showFloatingButton, setShowFloatingButton] = useState(false);
|
||||||
const terminalRef = useRef<HTMLDivElement>(null);
|
const terminalRef = useRef<HTMLDivElement>(null);
|
||||||
const intervalRef = useRef<number | null>(null);
|
const intervalRef = useRef<number | null>(null);
|
||||||
const progressLineIdRef = useRef<string | null>(null);
|
const progressLineIdRef = useRef<string | null>(null);
|
||||||
|
const logoClickTimerRef = useRef<number | null>(null);
|
||||||
|
|
||||||
// 启动序列
|
// 启动序列
|
||||||
const startupSequence = [
|
const startupSequence = [
|
||||||
@@ -639,6 +642,7 @@ const WorkflowPageV4 = () => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('Setting showResultModal to true');
|
console.log('Setting showResultModal to true');
|
||||||
setShowResultModal(true);
|
setShowResultModal(true);
|
||||||
|
setShowFloatingButton(true);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -696,6 +700,7 @@ const WorkflowPageV4 = () => {
|
|||||||
}
|
}
|
||||||
}, [currentAgentIndex]);
|
}, [currentAgentIndex]);
|
||||||
|
|
||||||
|
|
||||||
// 计时器
|
// 计时器
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status === 'running') {
|
if (status === 'running') {
|
||||||
@@ -739,6 +744,30 @@ const WorkflowPageV4 = () => {
|
|||||||
setIsExecuting(false);
|
setIsExecuting(false);
|
||||||
setUserRequirement('');
|
setUserRequirement('');
|
||||||
setShowResultModal(false);
|
setShowResultModal(false);
|
||||||
|
setShowFloatingButton(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Logo三击处理
|
||||||
|
const handleLogoClick = () => {
|
||||||
|
const newCount = logoClickCount + 1;
|
||||||
|
setLogoClickCount(newCount);
|
||||||
|
|
||||||
|
// 清除之前的定时器
|
||||||
|
if (logoClickTimerRef.current) {
|
||||||
|
clearTimeout(logoClickTimerRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果点击了3次,打开结果弹窗并显示浮动按钮
|
||||||
|
if (newCount === 3) {
|
||||||
|
setShowResultModal(true);
|
||||||
|
setShowFloatingButton(true); // 调试模式也显示浮动按钮
|
||||||
|
setLogoClickCount(0);
|
||||||
|
} else {
|
||||||
|
// 设置500ms后重置计数
|
||||||
|
logoClickTimerRef.current = window.setTimeout(() => {
|
||||||
|
setLogoClickCount(0);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 格式化时间
|
// 格式化时间
|
||||||
@@ -788,7 +817,15 @@ const WorkflowPageV4 = () => {
|
|||||||
{/* 顶部控制栏 */}
|
{/* 顶部控制栏 */}
|
||||||
<div className="bg-white border-b border-gray-200 px-6 py-3 flex items-center justify-between">
|
<div className="bg-white border-b border-gray-200 px-6 py-3 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<h1 className="text-lg font-semibold text-gray-900">DuoDuo Agent - 多智能体协同生成·专业方案</h1>
|
{/* Duoduo Logo - 可三击打开结果 */}
|
||||||
|
<img
|
||||||
|
src="https://ddcz-1315997005.cos.ap-nanjing.myqcloud.com/static/img/duoduo_logo/LOGO_1097x300.png"
|
||||||
|
alt="Duoduo Agent"
|
||||||
|
className="h-7 cursor-pointer select-none"
|
||||||
|
onClick={handleLogoClick}
|
||||||
|
style={{ userSelect: 'none' }}
|
||||||
|
/>
|
||||||
|
<h1 className="text-lg font-semibold text-gray-900">多智能体协同生成·专业方案</h1>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={status === 'idle' ? () => setShowRequirementModal(true) : pauseDemo}
|
onClick={status === 'idle' ? () => setShowRequirementModal(true) : pauseDemo}
|
||||||
@@ -1056,6 +1093,41 @@ const WorkflowPageV4 = () => {
|
|||||||
onClose={() => setShowResultModal(false)}
|
onClose={() => setShowResultModal(false)}
|
||||||
onViewDetails={handleViewDetails}
|
onViewDetails={handleViewDetails}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* 右下角浮动按钮 - 只在完成后显示 */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{showFloatingButton && (
|
||||||
|
<motion.button
|
||||||
|
initial={{ opacity: 0, scale: 0.8, y: 20 }}
|
||||||
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.8, y: 20 }}
|
||||||
|
transition={{ type: 'spring', stiffness: 300, damping: 25 }}
|
||||||
|
onClick={() => setShowResultModal(true)}
|
||||||
|
className="fixed bottom-8 right-8 px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-full shadow-lg hover:shadow-xl transform hover:scale-105 transition-all flex items-center gap-2 z-50"
|
||||||
|
style={{
|
||||||
|
backdropFilter: 'blur(10px)',
|
||||||
|
background: 'linear-gradient(135deg, rgba(59, 130, 246, 0.9) 0%, rgba(147, 51, 234, 0.9) 100%)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Eye className="w-5 h-5" />
|
||||||
|
<span className="font-medium">查看结果</span>
|
||||||
|
|
||||||
|
{/* 呼吸灯效果 */}
|
||||||
|
<motion.div
|
||||||
|
className="absolute -inset-1 bg-gradient-to-r from-blue-600 to-purple-600 rounded-full opacity-30"
|
||||||
|
animate={{
|
||||||
|
scale: [1, 1.2, 1],
|
||||||
|
opacity: [0.3, 0.1, 0.3],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 2,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: "easeInOut"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</motion.button>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user