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 = () => {
/>
)}
+
+
+