feat: remove tutorial functionality and related UI components

This commit is contained in:
Kuingsmile
2025-07-15 14:24:15 +08:00
parent 1d74daf8a5
commit ccb8b12f1e
10 changed files with 5 additions and 848 deletions

View File

@@ -178,7 +178,6 @@ winget install OpenListTeam.OpenListDesktop
1. **初始设置**:首次启动时,应用程序将指导您完成初始配置
2. **服务安装**:在提示时安装 OpenList 服务
3. **存储配置**:配置您的第一个云存储连接
4. **教程**:完成交互式教程以学习关键功能
### 基本操作

View File

@@ -178,7 +178,6 @@ winget install OpenListTeam.OpenListDesktop
1. **Initial Setup**: On first launch, the application will guide you through initial configuration
2. **Service Installation**: Install the OpenList service when prompted
3. **Storage Configuration**: Configure your first cloud storage connection
4. **Tutorial**: Complete the interactive tutorial to learn key features
### Basic Operations

View File

@@ -8,7 +8,6 @@ import { useTray } from './composables/useTray'
import { TauriAPI } from './api/tauri'
import Navigation from './components/Navigation.vue'
import TitleBar from './components/ui/TitleBar.vue'
import TutorialOverlay from './components/ui/TutorialOverlay.vue'
const appStore = useAppStore()
const { t } = useTranslation()
@@ -147,8 +146,6 @@ onUnmounted(() => {
</router-view>
</div>
</main>
<TutorialOverlay />
</div>
</template>

View File

@@ -6,16 +6,12 @@
<h4>{{ t('dashboard.quickActions.openlistService') }}</h4>
</div>
<div class="action-buttons">
<button
@click="toggleCore"
:class="['action-btn', 'service-btn', { running: isCoreRunning }]"
:disabled="loading"
>
<button @click="toggleCore" :class="['action-btn', 'service-btn', { running: isCoreRunning }]">
<component :is="serviceButtonIcon" :size="20" />
<span>{{ serviceButtonText }}</span>
</button>
<button @click="restartCore" :disabled="!isCoreRunning || loading" class="action-btn restart-btn">
<button @click="restartCore" :disabled="!isCoreRunning" class="action-btn restart-btn">
<RotateCcw :size="18" />
<span>{{ t('dashboard.quickActions.restart') }}</span>
</button>
@@ -47,7 +43,6 @@
<div class="action-buttons">
<button
@click="rcloneStore.serviceRunning ? stopBackend() : startBackend()"
:disabled="loading || rcloneStore.loading"
:class="['action-btn', 'service-indicator-btn', { active: rcloneStore.serviceRunning }]"
>
<component :is="rcloneStore.serviceRunning ? Square : Play" :size="18" />
@@ -93,7 +88,7 @@ import { useAppStore } from '../../stores/app'
import { useRcloneStore } from '../../stores/rclone'
import { useTranslation } from '../../composables/useI18n'
import Card from '../ui/Card.vue'
import { Play, Square, RotateCcw, ExternalLink, Settings, HardDrive, Loader, Key } from 'lucide-vue-next'
import { Play, Square, RotateCcw, ExternalLink, Settings, HardDrive, Key } from 'lucide-vue-next'
import { TauriAPI } from '@/api/tauri'
const { t } = useTranslation()
@@ -102,17 +97,14 @@ const appStore = useAppStore()
const rcloneStore = useRcloneStore()
const isCoreRunning = computed(() => appStore.isCoreRunning)
const loading = computed(() => appStore.loading)
const settings = computed(() => appStore.settings)
let statusCheckInterval: number | null = null
const serviceButtonIcon = computed(() => {
if (loading.value) return Loader
return isCoreRunning.value ? Square : Play
})
const serviceButtonText = computed(() => {
if (loading.value) return t('dashboard.quickActions.processing')
return isCoreRunning.value
? t('dashboard.quickActions.stopOpenListCore')
: t('dashboard.quickActions.startOpenListCore')

View File

@@ -1,636 +0,0 @@
<script setup lang="ts">
import { computed, ref, onMounted, nextTick, watch } from 'vue'
import { useAppStore } from '../../stores/app'
import { useTranslation } from '../../composables/useI18n'
import { ChevronLeft, ChevronRight, X, Check, Play, FileText, Settings, HardDrive, Home } from 'lucide-vue-next'
const appStore = useAppStore()
const { t } = useTranslation()
const tutorialSteps = computed(() => [
{
title: t('tutorial.welcome.title'),
content: t('tutorial.welcome.content'),
target: '.app-title',
position: 'center',
showNext: true,
showSkip: true,
icon: Home
},
{
title: t('tutorial.navigation.title'),
content: t('tutorial.navigation.content'),
target: '.nav-menu',
position: 'right',
showNext: true,
showPrev: true,
showSkip: true,
icon: HardDrive
},
{
title: t('tutorial.service.title'),
content: t('tutorial.service.content'),
target: '.service-management-card',
position: 'top',
showNext: true,
showPrev: true,
showSkip: true,
icon: Play
},
{
title: t('tutorial.openlist.title'),
content: t('tutorial.openlist.content'),
target: '.quick-actions-card',
position: 'top',
showNext: true,
showPrev: true,
showSkip: true,
icon: HardDrive
},
{
title: t('tutorial.documentation.title'),
content: t('tutorial.documentation.content'),
target: '.documentation-card',
position: 'bottom',
showNext: true,
showPrev: true,
showSkip: true,
icon: FileText
},
{
title: t('tutorial.settings.title'),
content: t('tutorial.settings.content'),
target: '.nav-item[href="/settings"]',
position: 'right',
showPrev: true,
showComplete: true,
icon: Settings
}
])
const currentStep = computed(() => tutorialSteps.value[appStore.tutorialStep] || tutorialSteps.value[0])
const highlightStyle = ref({})
const updateHighlight = async () => {
await nextTick()
if (!currentStep.value.target) {
highlightStyle.value = {}
return
}
const targetElement = document.querySelector(currentStep.value.target) as HTMLElement
if (!targetElement) {
highlightStyle.value = {}
return
}
const rect = targetElement.getBoundingClientRect()
const padding = 8
highlightStyle.value = {
top: `${rect.top - padding}px`,
left: `${rect.left - padding}px`,
width: `${rect.width + padding * 2}px`,
height: `${rect.height + padding * 2}px`
}
}
const getTooltipStyle = () => {
if (!currentStep.value.target)
return {
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
}
const targetElement = document.querySelector(currentStep.value.target) as HTMLElement
if (!targetElement)
return {
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
}
const rect = targetElement.getBoundingClientRect()
const position = currentStep.value.position || 'bottom'
const offset = 16
const tooltipWidth = 320
const tooltipHeight = 200
let style: any = {}
let adjustedPosition = position
if (position === 'left' && rect.left < tooltipWidth + offset) {
adjustedPosition = 'right'
} else if (position === 'right' && rect.right + tooltipWidth + offset > window.innerWidth) {
adjustedPosition = 'left'
} else if (position === 'top' && rect.top < tooltipHeight + offset) {
adjustedPosition = 'bottom'
} else if (position === 'bottom' && rect.bottom + tooltipHeight + offset > window.innerHeight) {
adjustedPosition = 'top'
}
switch (adjustedPosition) {
case 'center':
style = {
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
}
break
case 'top':
style = {
bottom: `${window.innerHeight - rect.top + offset}px`,
left: `${rect.left + rect.width / 2}px`,
transform: 'translateX(-50%)'
}
break
case 'bottom':
style = {
top: `${rect.bottom + offset}px`,
left: `${rect.left + rect.width / 2}px`,
transform: 'translateX(-50%)'
}
break
case 'left':
style = {
top: `${rect.top + rect.height / 2}px`,
right: `${window.innerWidth - rect.left + offset}px`,
transform: 'translateY(-50%)'
}
break
case 'right':
style = {
top: `${rect.top + rect.height / 2}px`,
left: `${rect.right + offset}px`,
transform: 'translateY(-50%)'
}
break
case 'bottom-right':
style = {
top: `${rect.bottom + offset}px`,
left: `${Math.max(16, rect.right - tooltipWidth)}px`
}
break
default:
style = {
top: `${rect.bottom + offset}px`,
left: `${rect.left + rect.width / 2}px`,
transform: 'translateX(-50%)'
}
}
if (style.left && !style.transform?.includes('translateX')) {
const leftPos = parseInt(style.left)
if (leftPos + tooltipWidth > window.innerWidth) {
style.left = `${window.innerWidth - tooltipWidth - 16}px`
}
if (leftPos < 16) {
style.left = '16px'
}
}
if (style.top && !style.transform?.includes('translateY')) {
const topPos = parseInt(style.top)
if (topPos + tooltipHeight > window.innerHeight) {
style.top = `${window.innerHeight - tooltipHeight - 16}px`
}
if (topPos < 16) {
style.top = '16px'
}
}
if (style.bottom) {
const bottomPos = parseInt(style.bottom)
if (window.innerHeight - bottomPos - tooltipHeight < 16) {
delete style.bottom
style.top = '16px'
}
}
if (style.right) {
const rightPos = parseInt(style.right)
if (window.innerWidth - rightPos - tooltipWidth < 16) {
delete style.right
style.left = '16px'
delete style.transform
}
}
if (style.transform?.includes('translate(-50%, -50%)')) {
return style
}
if (style.transform?.includes('translateX(-50%)') && style.left) {
const leftPos = parseInt(style.left)
const halfWidth = tooltipWidth / 2
if (leftPos - halfWidth < 16) {
style.left = `${halfWidth + 16}px`
}
if (leftPos + halfWidth > window.innerWidth - 16) {
style.left = `${window.innerWidth - halfWidth - 16}px`
}
}
if (style.transform?.includes('translateY(-50%)') && style.top) {
const topPos = parseInt(style.top)
const halfHeight = tooltipHeight / 2
if (topPos - halfHeight < 16) {
style.top = `${halfHeight + 16}px`
}
if (topPos + halfHeight > window.innerHeight - 16) {
style.top = `${window.innerHeight - halfHeight - 16}px`
}
}
return style
}
const handleNext = () => {
if (appStore.tutorialStep < tutorialSteps.value.length - 1) {
appStore.nextTutorialStep()
updateHighlight()
}
}
const handlePrev = () => {
if (appStore.tutorialStep > 0) {
appStore.prevTutorialStep()
updateHighlight()
}
}
const handleSkip = () => {
appStore.skipTutorial()
}
const handleComplete = () => {
appStore.completeTutorial()
}
const handleClose = () => {
appStore.closeTutorial()
}
onMounted(() => {
updateHighlight()
watch(
() => appStore.tutorialStep,
() => {
setTimeout(() => {
updateHighlight()
}, 100)
}
)
const handleResize = () => {
updateHighlight()
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
})
</script>
<template>
<Teleport to="body">
<div v-if="appStore.showTutorial" class="tutorial-overlay">
<div class="tutorial-backdrop" @click="handleClose" />
<div
v-if="currentStep.target && currentStep.position !== 'center'"
class="tutorial-highlight"
:style="highlightStyle"
/>
<div class="tutorial-tooltip" :style="getTooltipStyle()">
<div class="tooltip-header">
<div class="tooltip-icon">
<component :is="currentStep.icon" :size="20" />
</div>
<h3 class="tooltip-title">{{ currentStep.title }}</h3>
<button class="tooltip-close" @click="handleClose" :title="t('common.close')">
<X :size="18" />
</button>
</div>
<div class="tooltip-content">
<p>{{ currentStep.content }}</p>
</div>
<div class="tooltip-footer">
<div class="step-indicator">
<span class="step-current">{{ appStore.tutorialStep + 1 }}</span>
<span class="step-divider">/</span>
<span class="step-total">{{ tutorialSteps.length }}</span>
</div>
<div class="tutorial-actions">
<button v-if="currentStep.showSkip" class="btn-skip" @click="handleSkip">
{{ t('tutorial.skip') }}
</button>
<button v-if="currentStep.showPrev" class="btn-prev" @click="handlePrev">
<ChevronLeft :size="16" />
{{ t('tutorial.previous') }}
</button>
<button v-if="currentStep.showNext" class="btn-next" @click="handleNext">
{{ t('tutorial.next') }}
<ChevronRight :size="16" />
</button>
<button v-if="currentStep.showComplete" class="btn-complete" @click="handleComplete">
<Check :size="16" />
{{ t('tutorial.complete') }}
</button>
</div>
</div>
</div>
</div>
</Teleport>
</template>
<style scoped>
.tutorial-overlay {
position: fixed;
inset: 0;
z-index: 10000;
pointer-events: none;
}
.tutorial-backdrop {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(2px);
pointer-events: all;
}
.tutorial-highlight {
position: absolute;
border: 2px solid var(--color-accent);
border-radius: var(--radius-md);
box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.2), var(--shadow-lg);
background: rgba(255, 255, 255, 0.05);
pointer-events: none;
transition: all var(--transition-medium);
animation: pulse 2s infinite;
}
@keyframes pulse {
0%,
100% {
box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.2), var(--shadow-lg);
}
50% {
box-shadow: 0 0 0 8px rgba(0, 122, 255, 0.1), var(--shadow-xl);
}
}
.tutorial-tooltip {
position: absolute;
width: 320px;
max-width: calc(100vw - 32px);
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-xl);
backdrop-filter: blur(20px);
pointer-events: all;
animation: tooltipEnter 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
z-index: 10001;
}
@keyframes tooltipEnter {
0% {
opacity: 0;
transform: translateY(16px) scale(0.9);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.tooltip-header {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 20px 12px 20px;
border-bottom: 1px solid var(--color-border-secondary);
}
.tooltip-icon {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: var(--color-accent);
color: white;
border-radius: var(--radius-sm);
flex-shrink: 0;
}
.tooltip-title {
flex: 1;
font-size: 1rem;
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
}
.tooltip-close {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
background: transparent;
border: none;
color: var(--color-text-tertiary);
border-radius: var(--radius-sm);
cursor: pointer;
transition: all var(--transition-fast);
}
.tooltip-close:hover {
background: var(--color-background-secondary);
color: var(--color-text-primary);
}
.tooltip-content {
padding: 16px 20px;
}
.tooltip-content p {
margin: 0;
font-size: 0.875rem;
line-height: 1.5;
color: var(--color-text-secondary);
}
.tooltip-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 20px 16px 20px;
border-top: 1px solid var(--color-border-secondary);
}
.step-indicator {
display: flex;
align-items: center;
font-size: 0.75rem;
color: var(--color-text-tertiary);
}
.step-current {
font-weight: 600;
color: var(--color-accent);
}
.step-divider {
margin: 0 4px;
}
.tutorial-actions {
display: flex;
align-items: center;
gap: 8px;
}
.btn-skip {
padding: 6px 12px;
font-size: 0.75rem;
background: transparent;
border: 1px solid var(--color-border);
color: var(--color-text-tertiary);
border-radius: var(--radius-sm);
cursor: pointer;
transition: all var(--transition-fast);
}
.btn-skip:hover {
background: var(--color-background-secondary);
color: var(--color-text-secondary);
}
.btn-prev,
.btn-next,
.btn-complete {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px;
font-size: 0.75rem;
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
transition: all var(--transition-fast);
font-weight: 500;
}
.btn-prev {
background: var(--color-background-secondary);
color: var(--color-text-primary);
}
.btn-prev:hover {
background: var(--color-background-tertiary);
}
.btn-next,
.btn-complete {
background: var(--color-accent);
color: white;
}
.btn-next:hover,
.btn-complete:hover {
background: var(--color-accent-hover);
}
.btn-complete {
background: var(--color-success);
}
.btn-complete:hover {
background: #2fb344;
}
:root.dark .tutorial-backdrop {
background: rgba(0, 0, 0, 0.8);
}
:root.dark .tutorial-highlight {
border-color: var(--color-accent);
box-shadow: 0 0 0 4px rgba(10, 132, 255, 0.3), var(--shadow-lg);
}
:root.dark .tutorial-tooltip {
background: var(--color-surface-elevated);
border-color: var(--color-border);
}
@media (max-width: 768px) {
.tutorial-tooltip {
width: 280px;
max-width: calc(100vw - 24px);
position: fixed !important;
top: auto !important;
bottom: 20px !important;
left: 50% !important;
right: auto !important;
transform: translateX(-50%) !important;
}
.tooltip-header {
padding: 12px 16px 8px 16px;
}
.tooltip-content {
padding: 12px 16px;
}
.tooltip-footer {
padding: 8px 16px 12px 16px;
flex-direction: column;
gap: 8px;
align-items: stretch;
}
.tutorial-actions {
justify-content: space-between;
width: 100%;
}
.step-indicator {
align-self: center;
}
}
@media (max-width: 480px) {
.tutorial-tooltip {
width: calc(100vw - 32px);
}
.tutorial-actions {
flex-direction: column;
gap: 8px;
}
.btn-prev,
.btn-next,
.btn-complete,
.btn-skip {
width: 100%;
justify-content: center;
}
}
</style>

View File

@@ -163,15 +163,6 @@
"auto": "Auto",
"autoDesc": "Follow system"
},
"monitor": {
"title": "Monitoring",
"subtitle": "System monitoring and refresh settings",
"interval": {
"label": "Monitor Interval (seconds)",
"placeholder": "5",
"help": "How often to refresh system metrics and status"
}
},
"ghProxy": {
"title": "GitHub Proxy",
"subtitle": "Accelerate GitHub with proxy service",
@@ -191,12 +182,6 @@
"description": "Use system default browser instead of built-in window"
}
},
"tutorial": {
"title": "Tutorial",
"subtitle": "Learn how to use OpenList Desktop",
"restart": "Start Tutorial",
"help": "Restart the tutorial to learn about app features and navigation"
},
"updates": {
"title": "Updates",
"subtitle": "Manage automatic updates and notifications",
@@ -429,36 +414,6 @@
"title": "OpenList",
"loading": "Initializing OpenList Desktop..."
},
"tutorial": {
"welcome": {
"title": "Welcome to OpenList Desktop",
"content": "Welcome to OpenList Desktop! This tutorial will guide you through the key features and help you get started quickly."
},
"navigation": {
"title": "Navigation Panel",
"content": "Use the navigation panel to access different sections: Dashboard for monitoring, Mount for storage management, Logs for troubleshooting, and Settings for configuration."
},
"service": {
"title": "Install & Start Service",
"content": "First, you need to install and start the OpenList service. This is the core component that manages your cloud storage connections."
},
"openlist": {
"title": "OpenList Core Access",
"content": "Once the service is running, you can access the OpenList web interface to manage your files and configurations."
},
"documentation": {
"title": "Read Documentation",
"content": "For detailed information and advanced configurations, check out the documentation section. You'll find guides, API docs, and troubleshooting tips."
},
"settings": {
"title": "Settings & Configuration",
"content": "Customize your OpenList experience in the Settings section. Configure themes, service options, and storage preferences."
},
"skip": "Skip Tutorial",
"next": "Next",
"previous": "Previous",
"complete": "Complete Tutorial"
},
"update": {
"title": "App Updates",
"subtitle": "Keep your application up to date with the latest features and security improvements",

View File

@@ -163,15 +163,6 @@
"auto": "自动",
"autoDesc": "跟随系统"
},
"monitor": {
"title": "监控",
"subtitle": "系统监控和刷新设置",
"interval": {
"label": "监控间隔(秒)",
"placeholder": "5",
"help": "刷新系统指标和状态的频率"
}
},
"ghProxy": {
"title": "GitHub 代理",
"subtitle": "使用代理服务加速 GitHub",
@@ -191,12 +182,6 @@
"description": "使用系统默认浏览器而不是内置窗口"
}
},
"tutorial": {
"title": "教程",
"subtitle": "学习如何使用 OpenList 桌面版",
"restart": "开始教程",
"help": "重新启动教程以了解应用功能和导航"
},
"updates": {
"title": "更新",
"subtitle": "管理自动更新和通知",
@@ -429,36 +414,6 @@
"title": "OpenList",
"loading": "正在初始化"
},
"tutorial": {
"welcome": {
"title": "欢迎使用 OpenList 桌面版",
"content": "欢迎使用 OpenList 桌面版!本教程将引导您了解主要功能,帮助您快速上手。"
},
"navigation": {
"title": "导航面板",
"content": "使用导航面板访问不同部分:仪表板用于监控,挂载用于存储管理,日志用于故障排除,设置用于配置。"
},
"service": {
"title": "安装并启动服务",
"content": "首先,您需要安装并启动 OpenList 服务。这是管理云存储连接的核心组件。"
},
"openlist": {
"title": "OpenList 核心访问",
"content": "服务运行后,您可以访问 OpenList 网页界面来管理文件和配置。"
},
"documentation": {
"title": "阅读文档",
"content": "有关详细信息和高级配置请查看文档部分。您会找到指南、API 文档和故障排除提示。"
},
"settings": {
"title": "设置和配置",
"content": "在设置部分自定义您的 OpenList 体验。配置主题、服务选项和存储首选项。"
},
"skip": "跳过教程",
"next": "下一步",
"previous": "上一步",
"complete": "完成教程"
},
"update": {
"title": "应用更新",
"subtitle": "保持应用程序最新,获取最新功能和安全改进",

View File

@@ -375,10 +375,6 @@ export const useAppStore = defineStore('app', () => {
const openlistProcessId = ref<string | undefined>(undefined)
const showTutorial = ref(false)
const tutorialStep = ref(0)
const tutorialSkipped = ref(false)
async function getRcloneMountProcessId(name: string): Promise<string | undefined> {
try {
const processList = await TauriAPI.process.list()
@@ -683,7 +679,6 @@ export const useAppStore = defineStore('app', () => {
async function init() {
try {
initTutorial()
await loadSettings()
await refreshOpenListCoreStatus()
await TauriAPI.tray.updateDelayed(openlistCoreStatus.value.running)
@@ -697,43 +692,6 @@ export const useAppStore = defineStore('app', () => {
}
}
function initTutorial() {
const hasSeenTutorial = localStorage.getItem('openlist-tutorial-completed')
const tutorialDisabled = localStorage.getItem('openlist-tutorial-disabled')
if (!hasSeenTutorial && tutorialDisabled !== 'true') {
showTutorial.value = true
tutorialStep.value = 0
}
}
function startTutorial() {
showTutorial.value = true
tutorialStep.value = 0
localStorage.removeItem('openlist-tutorial-disabled')
}
function nextTutorialStep() {
tutorialStep.value++
}
function prevTutorialStep() {
if (tutorialStep.value > 0) {
tutorialStep.value--
}
}
function skipTutorial() {
showTutorial.value = false
tutorialSkipped.value = true
localStorage.setItem('openlist-tutorial-disabled', 'true')
}
function completeTutorial() {
showTutorial.value = false
localStorage.setItem('openlist-tutorial-completed', 'true')
}
async function getAdminPassword(): Promise<string | null> {
try {
return await TauriAPI.logs.adminPassword()
@@ -743,10 +701,6 @@ export const useAppStore = defineStore('app', () => {
}
}
function closeTutorial() {
showTutorial.value = false
}
// Update management functions
function setUpdateAvailable(available: boolean, updateInfo?: UpdateCheck) {
updateAvailable.value = available
@@ -783,10 +737,6 @@ export const useAppStore = defineStore('app', () => {
updateAvailable,
updateCheck,
showTutorial,
tutorialStep,
tutorialSkipped,
isCoreRunning,
openListCoreUrl,
@@ -814,13 +764,6 @@ export const useAppStore = defineStore('app', () => {
toggleTheme,
applyTheme,
initTutorial,
startTutorial,
nextTutorialStep,
prevTutorialStep,
skipTutorial,
completeTutorial,
closeTutorial,
setUpdateAvailable,
clearUpdateStatus
}

View File

@@ -1,14 +1,13 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useRoute } from 'vue-router'
import { useAppStore } from '../stores/app'
import { useTranslation } from '../composables/useI18n'
import { Settings, Server, HardDrive, Save, RotateCcw, AlertCircle, CheckCircle, Play } from 'lucide-vue-next'
import { Settings, Server, HardDrive, Save, RotateCcw, AlertCircle, CheckCircle } from 'lucide-vue-next'
import { enable, isEnabled, disable } from '@tauri-apps/plugin-autostart'
const appStore = useAppStore()
const route = useRoute()
const router = useRouter()
const { t } = useTranslation()
const isSaving = ref(false)
const message = ref('')
@@ -129,11 +128,6 @@ const handleSave = async () => {
}
}
async function startTutorial() {
router.push({ name: 'Dashboard' })
appStore.startTutorial()
}
const handleReset = async () => {
if (!confirm(t('settings.confirmReset'))) {
return
@@ -373,21 +367,6 @@ const handleReset = async () => {
</label>
</div>
</div>
<div class="settings-section">
<h2>{{ t('settings.app.tutorial.title') }}</h2>
<p>{{ t('settings.app.tutorial.subtitle') }}</p>
<div class="form-grid">
<div class="form-group">
<button @click="startTutorial" class="tutorial-btn" type="button">
<Play :size="16" />
{{ t('settings.app.tutorial.restart') }}
</button>
<small>{{ t('settings.app.tutorial.help') }}</small>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -604,29 +604,3 @@
padding: 1.5rem;
}
}
.tutorial-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 20px;
background: var(--color-accent);
color: white;
border: none;
border-radius: var(--radius-md);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all var(--transition-fast);
}
.tutorial-btn:hover {
background: var(--color-accent-hover);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
}
.tutorial-btn:active {
transform: translateY(0);
box-shadow: 0 2px 6px rgba(0, 122, 255, 0.3);
}