remove onboarding

ain't nobody got time for that
This commit is contained in:
FTCHD
2026-05-11 12:31:43 +03:00
parent 84a1759e56
commit fc82a4668b
8 changed files with 18 additions and 296 deletions
+1 -2
View File
@@ -15,8 +15,7 @@
<script>
if (
window.location.pathname.startsWith('/toolbar') ||
window.location.pathname.startsWith('/startup') ||
window.location.pathname.startsWith('/onboarding')
window.location.pathname.startsWith('/startup')
) {
document.documentElement.classList.add('!bg-transparent')
}
+1 -13
View File
@@ -9,25 +9,13 @@ import { openUrl } from '@tauri-apps/plugin-opener'
import { platform } from '@tauri-apps/plugin-os'
import { exit } from '@tauri-apps/plugin-process'
import { usePersistedStore } from '../store/persisted'
import { openSmallWindow, openWindow } from './window'
import { openWindow } from './window'
async function buildMenu() {
console.log('[buildMenu]')
const menuItems: (MenuItem | PredefinedMenuItem)[] = []
const onboardingMenuItem = await MenuItem.new({
id: 'onboarding',
text: 'Onboarding',
action: async () => {
await openSmallWindow({
name: 'Onboarding',
url: '/onboarding',
})
},
})
menuItems.push(onboardingMenuItem)
const openMenuItem = await MenuItem.new({
id: 'open',
text: 'Open',
+4 -23
View File
@@ -33,7 +33,6 @@
"@tauri-apps/plugin-store": "^2.4.1",
"@tauri-apps/plugin-updater": "^2.9.0",
"@tauri-apps/plugin-window-state": "^2.4.1",
"canvas-confetti": "^1.9.4",
"cron-parser": "^5.5.0",
"cronstrue": "^3.14.0",
"date-fns": "^4.1.0",
@@ -57,7 +56,6 @@
"@iarna/toml": "^2.2.5",
"@tauri-apps/cli": "^2.9.2",
"@total-typescript/ts-reset": "^0.6.1",
"@types/canvas-confetti": "^1.9.0",
"@types/node": "^22.10.7",
"@types/qrcode.react": "^1.0.5",
"@types/react": "^18.3.1",
@@ -102,7 +100,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
@@ -254,7 +252,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"devOptional": true,
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -6070,13 +6068,6 @@
"@babel/types": "^7.20.7"
}
},
"node_modules/@types/canvas-confetti": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.9.0.tgz",
"integrity": "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -6392,16 +6383,6 @@
],
"license": "CC-BY-4.0"
},
"node_modules/canvas-confetti": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.4.tgz",
"integrity": "sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==",
"license": "ISC",
"funding": {
"type": "donate",
"url": "https://www.paypal.me/kirilvatev"
}
},
"node_modules/castable-video": {
"version": "1.1.16",
"resolved": "https://registry.npmjs.org/castable-video/-/castable-video-1.1.16.tgz",
@@ -6609,7 +6590,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -7341,7 +7322,7 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"node_modules/mux-embed": {
+10 -3
View File
@@ -12,7 +12,16 @@
"url": "https://github.com/rclone-ui/rclone-ui"
},
"homepage": "https://rcloneui.com",
"keywords": ["rclone", "gui", "drive", "s3", "cloud", "storage", "sync", "backup"],
"keywords": [
"rclone",
"gui",
"drive",
"s3",
"cloud",
"storage",
"sync",
"backup"
],
"license": "Apache-2.0",
"scripts": {
"dev": "node scripts/buildExternal.js && vite",
@@ -56,7 +65,6 @@
"@tauri-apps/plugin-store": "^2.4.1",
"@tauri-apps/plugin-updater": "^2.9.0",
"@tauri-apps/plugin-window-state": "^2.4.1",
"canvas-confetti": "^1.9.4",
"cron-parser": "^5.5.0",
"cronstrue": "^3.14.0",
"date-fns": "^4.1.0",
@@ -80,7 +88,6 @@
"@iarna/toml": "^2.2.5",
"@tauri-apps/cli": "^2.9.2",
"@total-typescript/ts-reset": "^0.6.1",
"@types/canvas-confetti": "^1.9.0",
"@types/node": "^22.10.7",
"@types/qrcode.react": "^1.0.5",
"@types/react": "^18.3.1",
-1
View File
@@ -21,7 +21,6 @@
"Commander",
"Startup",
"Toolbar",
"Onboarding",
"Test"
],
"permissions": [
+1 -8
View File
@@ -18,7 +18,6 @@ import Download from './pages/Download'
import Home from './pages/Home'
import Mount from './pages/Mount'
import Move from './pages/Move'
import Onboarding from './pages/Onboarding'
import Purge from './pages/Purge'
import Schedules from './pages/Schedules'
import Serve from './pages/Serve'
@@ -56,7 +55,6 @@ forwardConsole('error', error)
if (
!window.location?.pathname.startsWith('/toolbar') &&
!window.location?.pathname.startsWith('/startup') &&
!window.location?.pathname.startsWith('/onboarding') &&
!window.location?.pathname.startsWith('/commander')
) {
import('./setupDragRegions').then(({ initTauriDragRegions }) => {
@@ -144,10 +142,6 @@ const router = createBrowserRouter([
path: '/toolbar',
element: <Toolbar />,
},
{
path: '/onboarding',
element: <Onboarding />,
},
{
path: '/test',
element: <Test />,
@@ -189,8 +183,7 @@ function ThemeProvider({ children }: { children: React.ReactNode }) {
<main
className={
window.location?.pathname.startsWith('/toolbar') ||
window.location?.pathname.startsWith('/startup') ||
window.location?.pathname.startsWith('/onboarding')
window.location?.pathname.startsWith('/startup')
? undefined
: 'bg-transparent dark:bg-[#121212] overflow-scroll overscroll-y-none'
}
-227
View File
@@ -1,227 +0,0 @@
import { Button } from '@heroui/react'
import { invoke } from '@tauri-apps/api/core'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { openUrl } from '@tauri-apps/plugin-opener'
import { platform } from '@tauri-apps/plugin-os'
import confetti from 'canvas-confetti'
import { AnimatePresence, motion } from 'framer-motion'
import { ChevronRightIcon } from 'lucide-react'
import { useCallback, useMemo, useState } from 'react'
const SLIDES = [
{
title: 'Welcome to Rclone UI',
description:
'All the power of rclone — combined with a battle-tested interface that gets out of your way, and right back in when needed.',
image: '/banner.png',
},
{
title: 'Schedule Tasks',
description:
'Set it and forget it! Schedule any task to run automatically whenever you want. Perfect for hands-free backups, regular syncs & everything in between.',
image: '/onboarding/schedules.png',
className: 'px-10',
},
{
title: 'Templates',
description:
'Use templates to store reusable flags that you often need. Quickly create templates by pasting your existing CLI commands.',
image: '/onboarding/templates.png',
link: {
label: 'Browse gallery',
url: 'https://rcloneui.com/templates/',
},
className: 'w-80',
},
{
title: 'Remote Control',
description:
'Control rclone instances on other machines straight from your current device. No vendor lock, custom docker setups, or shoddy solutions.',
image: '/onboarding/remote.png',
},
{
title: 'Private & Open Source',
description:
'RCUI is 100% private and open source. No tracking, no ads, no nonsense. You can rest assured that your data never leaves your device.',
image: '/onboarding/github.png',
className: 'w-full pt-24',
},
{
title: 'Coming to phones & tablets near you!',
description:
'Rclone UI Mobile is right around the corner. The launch is scheduled at 2,000 stars on GitHub. We guarantee it will be worth it!',
image: '/onboarding/mobile.png',
link: {
label: 'Give it a ⭐️',
url: 'https://github.com/rclone-ui/rclone-ui',
},
className: 'pt-48 w-96',
},
] as const
export default function Onboarding() {
const [currentSlide, setCurrentSlide] = useState(0)
const [direction, setDirection] = useState(1) // 1 for forward, -1 for backward
const [isFinishing, setIsFinishing] = useState(false)
const slide = useMemo(() => SLIDES[currentSlide], [currentSlide])
const isFirstSlide = useMemo(() => currentSlide === 0, [currentSlide])
const isLastSlide = useMemo(() => currentSlide === SLIDES.length - 1, [currentSlide])
const handleFinish = useCallback(async () => {
const currentWindow = getCurrentWindow()
if (platform() === 'windows') {
await currentWindow.hide()
await new Promise((resolve) => setTimeout(resolve, 690))
await invoke('show_toolbar')
await currentWindow.destroy()
return
}
await currentWindow.setAlwaysOnTop(false)
setIsFinishing(true)
const duration = 2500
confetti({
particleCount: 150,
spread: 100,
origin: { x: 0.5, y: 0.8 },
gravity: 0.8,
ticks: 300,
startVelocity: 45,
colors: ['#ff6b6b', '#4ecdc4', '#ffe66d', '#95e1d3', '#f38181'],
shapes: ['square', 'circle', 'star'],
scalar: 1.1,
drift: 0,
})
await invoke('show_toolbar')
await new Promise((resolve) => setTimeout(resolve, duration * 2))
await currentWindow.hide()
await currentWindow.destroy()
}, [])
const handleNext = useCallback(() => {
if (isLastSlide) {
handleFinish()
return
}
setDirection(1)
setCurrentSlide((prev) => prev + 1)
}, [isLastSlide, handleFinish])
const handleBack = useCallback(() => {
if (isFirstSlide) return
setDirection(-1)
setCurrentSlide((prev) => prev - 1)
}, [isFirstSlide])
const imageVariants = useMemo(
() => ({
enter: (ctx: { dir: number; slide: number }) => {
// Entering slide 0 (coming back from slide 1): fade in with scale + rotate
if (ctx.dir < 0 && ctx.slide === 0) {
return { x: 0, opacity: 0, scale: 1.15, rotate: -3 }
}
return {
x: ctx.dir > 0 ? 80 : -80,
opacity: 0,
scale: 1,
rotate: 0,
}
},
center: {
x: 0,
opacity: 1,
scale: 1,
rotate: 0,
},
exit: (ctx: { dir: number; slide: number }) => {
// Exiting slide 0 (going to slide 1): fade out with scale + rotate
if (ctx.dir > 0 && ctx.slide === 1) {
return { x: 0, opacity: 0, scale: 1.15, rotate: 3 }
}
return {
x: ctx.dir > 0 ? -80 : 80,
opacity: 0,
scale: 1,
rotate: 0,
}
},
}),
[]
)
return (
<div className="flex flex-col items-center justify-center w-full h-screen p-0.5 bg-transparent">
<div
className={`w-full h-full bg-content1 rounded-large overflow-hidden transition-opacity duration-300 ${isFinishing ? 'opacity-0' : 'opacity-100'}`}
>
<div className="flex flex-col items-center justify-center overflow-hidden h-3/5 bg-gradient-to-b from-primary/90 dark:from-primary/10 to-transparent">
<AnimatePresence mode="wait" custom={{ dir: direction, slide: currentSlide }}>
<motion.img
key={currentSlide}
src={slide.image}
alt={slide.title}
custom={{ dir: direction, slide: currentSlide }}
variants={imageVariants}
initial="enter"
animate="center"
exit="exit"
transition={{
x: { type: 'spring', stiffness: 300, damping: 30 },
opacity: { duration: 0.2 },
scale: { type: 'spring', stiffness: 300, damping: 30 },
rotate: { type: 'spring', stiffness: 300, damping: 30 },
}}
className={'className' in slide ? slide.className : ''}
/>
</AnimatePresence>
</div>
<div className="flex flex-col justify-between pt-4 pb-5 pl-6 pr-7 h-2/5 bg-content1">
<div className="flex flex-col max-w-[690px] gap-2">
<p className="text-2xl font-medium">{slide.title}</p>
<p className=" text-foreground-500">{slide.description}</p>
</div>
<div className="flex flex-row items-center justify-between">
{isFirstSlide ? (
<Button color="primary" onPress={handleNext}>
Get Started
</Button>
) : (
<>
<Button variant="flat" onPress={handleBack}>
Back
</Button>
<div className="flex flex-row gap-2">
{'link' in slide && (
<Button
variant="faded"
onPress={() => openUrl(slide.link.url)}
>
{slide.link.label}
</Button>
)}
<Button
color="primary"
onPress={handleNext}
endContent={
isLastSlide ? undefined : (
<ChevronRightIcon className="size-4 mt-0.5" />
)
}
className="gap-0"
>
{isLastSlide ? 'START' : 'Next'}
</Button>
</div>
</>
)}
</div>
</div>
</div>
</div>
)
}
-18
View File
@@ -5,7 +5,6 @@ import { platform } from '@tauri-apps/plugin-os'
import { exit } from '@tauri-apps/plugin-process'
import { AnimatePresence, motion } from 'framer-motion'
import { useEffect, useMemo, useState } from 'react'
import { openSmallWindow } from '../../lib/window'
import { useStore } from '../../store/memory'
import { usePersistedStore } from '../../store/persisted'
@@ -197,24 +196,7 @@ export default function Startup() {
await currentWindow.hide()
if (
usePersistedStore
.getState()
.acknowledgements.includes('onboarding')
) {
await invoke('show_toolbar')
} else {
usePersistedStore.setState((prev) => ({
acknowledgements: [
...prev.acknowledgements,
'onboarding',
],
}))
await openSmallWindow({
name: 'Onboarding',
url: '/onboarding',
})
}
await new Promise((resolve) => setTimeout(resolve, 690))