diff --git a/package.json b/package.json index dbc24c3..58b69f6 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@vitejs/plugin-react": "^4.3.1", "apexcharts": "^3.53.0", "autoprefixer": "^10.4.20", + "driver.js": "^1.3.1", "electron": "^32.0.2", "electron-builder": "^25.0.5", "electron-vite": "^2.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2163dc3..0a085dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,6 +93,9 @@ importers: autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.45) + driver.js: + specifier: ^1.3.1 + version: 1.3.1 electron: specifier: ^32.0.2 version: 32.0.2 @@ -2784,6 +2787,9 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} + driver.js@1.3.1: + resolution: {integrity: sha512-MvUdXbqSgEsgS/H9KyWb5Rxy0aE6BhOVT4cssi2x2XjmXea6qQfgdx32XKVLLSqTaIw7q/uxU5Xl3NV7+cN6FQ==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -8753,6 +8759,8 @@ snapshots: dotenv@16.4.5: {} + driver.js@1.3.1: {} + eastasianwidth@0.2.0: {} ee-first@1.1.1: {} diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index d16a085..e8b25b1 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -33,6 +33,8 @@ import { platform } from '@renderer/utils/init' import { TitleBarOverlayOptions } from 'electron' import SubStoreCard from '@renderer/components/sider/substore-card' import MihomoIcon from './components/base/mihomo-icon' +import { driver } from 'driver.js' +import 'driver.js/dist/driver.css' const App: React.FC = () => { const { appConfig, patchAppConfig } = useAppConfig() @@ -66,6 +68,14 @@ const App: React.FC = () => { setOrder(siderOrder) }, [siderOrder]) + useEffect(() => { + const tourShown = window.localStorage.getItem('tourShown') + if (!tourShown) { + window.localStorage.setItem('tourShown', 'true') + firstDriver.drive() + } + }, []) + useEffect(() => { if (appTheme.includes('light')) { setNativeTheme('light') @@ -195,3 +205,52 @@ const App: React.FC = () => { } export default App + +export const firstDriver = driver({ + showProgress: true, + nextBtnText: '下一步', + prevBtnText: '上一步', + doneBtnText: '完成', + progressText: '{{current}} / {{total}}', + overlayOpacity: 0.9, + steps: [ + { + element: '.side', + popover: { + title: '导航栏', + description: + '左侧是应用的导航栏,兼顾仪表盘功能,在这里可以切换不同页面,也可以概览常用的状态信息', + side: 'left', + align: 'start' + } + }, + { + element: '.sysproxy-card', + popover: { + title: '卡片', + description: '点击导航栏卡片可以跳转到对应页面,拖动导航栏卡片可以自由排列卡片顺序', + side: 'bottom', + align: 'start' + } + }, + { + element: '.main', + popover: { + title: '主要区域', + description: '右侧是应用的主要区域,展示了导航栏所选页面的内容', + side: 'right', + align: 'start' + } + }, + { + element: '.profile-card', + popover: { + title: '订阅管理', + description: + '订阅管理卡片展示当前运行的订阅配置信息,点击进入订阅管理页面可以在这里管理订阅配置\n更多功能请查阅 官方文档', + side: 'bottom', + align: 'start' + } + } + ] +}) diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 68c892e..a56027c 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -7,6 +7,61 @@ src: url('./NotoColorEmoji.ttf'); } +.driver-popover { + background-color: hsl(var(--nextui-content2)) !important; + border-radius: 8px !important; + color: hsl(var(--nextui-foreground)) !important; +} + +.driver-popover a { + color: hsl(var(--nextui-primary)) !important; + text-decoration: underline !important; +} + +.driver-popover-close-btn { + color: hsl(var(--nextui-foreground)) !important; +} + +.driver-popover-progress-text { + color: hsl(var(--nextui-default-500)) !important; +} + +.driver-popover-prev-btn { + color: white !important; + text-shadow: none !important; + border: none !important; + padding: 8px !important; + border-radius: 5px !important; + font-size: 12px !important; + background-color: hsl(var(--nextui-primary)) !important; +} + +.driver-popover-next-btn { + color: white !important; + text-shadow: none !important; + border: none !important; + padding: 8px !important; + border-radius: 5px !important; + font-size: 12px !important; + background-color: hsl(var(--nextui-primary)) !important; +} + +.driver-popover-arrow-side-bottom { + border-bottom-color: hsl(var(--nextui-content2)) !important; +} + +.driver-popover-arrow-side-top { + border-top-color: hsl(var(--nextui-content2)) !important; +} + +.driver-popover-arrow-side-left { + border-left-color: hsl(var(--nextui-content2)) !important; +} + +.driver-popover-arrow-side-right { + border-right-color: hsl(var(--nextui-content2)) !important; +} + .app-nodrag { -webkit-app-region: none; } diff --git a/src/renderer/src/components/settings/actions.tsx b/src/renderer/src/components/settings/actions.tsx index 0b92f6f..60356be 100644 --- a/src/renderer/src/components/settings/actions.tsx +++ b/src/renderer/src/components/settings/actions.tsx @@ -6,6 +6,7 @@ import { useState } from 'react' import UpdaterModal from '../updater/updater-modal' import { version } from '@renderer/utils/init' import { IoIosHelpCircle } from 'react-icons/io' +import { firstDriver } from '@renderer/App' const Actions: React.FC = () => { const [newVersion, setNewVersion] = useState('') @@ -23,6 +24,11 @@ const Actions: React.FC = () => { /> )} + + +