Skip to content

Commit b1ea0e6

Browse files
committed
Update documentation and add usage guide page
1 parent 94c0764 commit b1ea0e6

File tree

4 files changed

+266
-71
lines changed

4 files changed

+266
-71
lines changed

public/video/demo2.webm

4.65 MB
Binary file not shown.

src/app/about/page.tsx

Lines changed: 0 additions & 60 deletions
This file was deleted.

src/app/docs/page.tsx

Lines changed: 183 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,194 @@
33
import { useRouter } from 'next/navigation';
44
import Link from 'next/link';
55
import { Button } from '@/components/ui/button';
6-
import { ArrowLeft } from 'lucide-react';
6+
import { ArrowLeft, Play, Pause, Volume2, VolumeX, Maximize } from 'lucide-react';
7+
import { useState, useRef, useEffect } from 'react';
78
import {
89
Accordion,
910
AccordionContent,
1011
AccordionItem,
1112
AccordionTrigger,
1213
} from "@/components/ui/accordion"
1314

15+
const MinimalistVideoPlayer = ({ src }: { src: string }) => {
16+
const videoRef = useRef<HTMLVideoElement>(null);
17+
const [isPlaying, setIsPlaying] = useState(false);
18+
const [isMuted, setIsMuted] = useState(false);
19+
const [progress, setProgress] = useState(0);
20+
const [duration, setDuration] = useState(0);
21+
const [currentTime, setCurrentTime] = useState(0);
22+
const [showControls, setShowControls] = useState(true);
23+
24+
useEffect(() => {
25+
const video = videoRef.current;
26+
if (!video) return;
27+
28+
const updateTime = () => {
29+
setCurrentTime(video.currentTime);
30+
setProgress((video.currentTime / video.duration) * 100);
31+
};
32+
33+
const updateDuration = () => {
34+
setDuration(video.duration);
35+
};
36+
37+
video.addEventListener('timeupdate', updateTime);
38+
video.addEventListener('loadedmetadata', updateDuration);
39+
40+
return () => {
41+
video.removeEventListener('timeupdate', updateTime);
42+
video.removeEventListener('loadedmetadata', updateDuration);
43+
};
44+
}, []);
45+
46+
const togglePlay = () => {
47+
const video = videoRef.current;
48+
if (!video) return;
49+
50+
if (isPlaying) {
51+
video.pause();
52+
} else {
53+
video.play();
54+
}
55+
setIsPlaying(!isPlaying);
56+
};
57+
58+
const toggleMute = () => {
59+
const video = videoRef.current;
60+
if (!video) return;
61+
62+
video.muted = !isMuted;
63+
setIsMuted(!isMuted);
64+
};
65+
66+
const handleProgressClick = (e: React.MouseEvent<HTMLDivElement>) => {
67+
const video = videoRef.current;
68+
if (!video) return;
69+
70+
const rect = e.currentTarget.getBoundingClientRect();
71+
const clickX = e.clientX - rect.left;
72+
const width = rect.width;
73+
const newTime = (clickX / width) * duration;
74+
75+
video.currentTime = newTime;
76+
};
77+
78+
const toggleFullscreen = () => {
79+
const video = videoRef.current;
80+
if (!video) return;
81+
82+
if (document.fullscreenElement) {
83+
document.exitFullscreen();
84+
} else {
85+
video.requestFullscreen();
86+
}
87+
};
88+
89+
const formatTime = (time: number) => {
90+
const minutes = Math.floor(time / 60);
91+
const seconds = Math.floor(time % 60);
92+
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
93+
};
94+
95+
return (
96+
<div
97+
className="relative w-full bg-black rounded-lg overflow-hidden group"
98+
onMouseEnter={() => setShowControls(true)}
99+
onMouseLeave={() => setShowControls(false)}
100+
>
101+
<video
102+
ref={videoRef}
103+
className="w-full h-auto"
104+
src={src}
105+
onClick={togglePlay}
106+
/>
107+
108+
{/* Controls Overlay */}
109+
<div className={`absolute inset-0 bg-gradient-to-t from-black/50 to-transparent transition-opacity duration-300 ${showControls ? 'opacity-100' : 'opacity-0'}`}>
110+
{/* Play/Pause Button in Center */}
111+
<div className="absolute inset-0 flex items-center justify-center">
112+
<button
113+
onClick={togglePlay}
114+
className="bg-black/50 hover:bg-black/70 text-white p-4 rounded-full transition-all duration-200 hover:scale-110"
115+
aria-label={isPlaying ? "Pause video" : "Play video"}
116+
title={isPlaying ? "Pause video" : "Play video"}
117+
>
118+
{isPlaying ? (
119+
<Pause className="w-8 h-8" />
120+
) : (
121+
<Play className="w-8 h-8 ml-1" />
122+
)}
123+
</button>
124+
</div>
125+
126+
{/* Bottom Controls */}
127+
<div className="absolute bottom-0 left-0 right-0 p-4">
128+
{/* Progress Bar */}
129+
<div
130+
className="w-full h-1 bg-white/30 rounded-full cursor-pointer mb-3 hover:h-2 transition-all duration-200"
131+
onClick={handleProgressClick}
132+
role="progressbar"
133+
aria-label="Video progress"
134+
aria-valuenow={progress}
135+
aria-valuemin={0}
136+
aria-valuemax={100}
137+
title={`Video progress: ${Math.round(progress)}%`}
138+
>
139+
<div
140+
className="h-full bg-blue-500 rounded-full transition-all duration-200"
141+
style={{ width: `${progress}%` }}
142+
/>
143+
</div>
144+
145+
{/* Control Buttons */}
146+
<div className="flex items-center justify-between">
147+
<div className="flex items-center space-x-3">
148+
<button
149+
onClick={togglePlay}
150+
className="text-white hover:text-blue-400 transition-colors duration-200"
151+
aria-label={isPlaying ? "Pause video" : "Play video"}
152+
title={isPlaying ? "Pause video" : "Play video"}
153+
>
154+
{isPlaying ? (
155+
<Pause className="w-5 h-5" />
156+
) : (
157+
<Play className="w-5 h-5" />
158+
)}
159+
</button>
160+
161+
<button
162+
onClick={toggleMute}
163+
className="text-white hover:text-blue-400 transition-colors duration-200"
164+
aria-label={isMuted ? "Unmute video" : "Mute video"}
165+
title={isMuted ? "Unmute video" : "Mute video"}
166+
>
167+
{isMuted ? (
168+
<VolumeX className="w-5 h-5" />
169+
) : (
170+
<Volume2 className="w-5 h-5" />
171+
)}
172+
</button>
173+
174+
<span className="text-white text-sm">
175+
{formatTime(currentTime)} / {formatTime(duration)}
176+
</span>
177+
</div>
178+
179+
<button
180+
onClick={toggleFullscreen}
181+
className="text-white hover:text-blue-400 transition-colors duration-200"
182+
aria-label="Toggle fullscreen"
183+
title="Toggle fullscreen"
184+
>
185+
<Maximize className="w-5 h-5" />
186+
</button>
187+
</div>
188+
</div>
189+
</div>
190+
</div>
191+
);
192+
};
193+
14194
const DocsPage = () => {
15195
const router = useRouter();
16196

@@ -96,16 +276,8 @@ const DocsPage = () => {
96276

97277
<section id="demo" className="mb-8">
98278
<h2 className="text-2xl font-semibold mb-4">Demo</h2>
99-
<div className="relative w-full overflow-hidden rounded-lg" style={{ paddingTop: '56.25%' }}>
100-
<iframe
101-
className="absolute top-0 left-0 w-full h-full"
102-
src="https://www.youtube.com/embed/49qcrWgHT9M?si=2BnYxyBXXmh1EQd5"
103-
title="YouTube video player"
104-
frameBorder="0"
105-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
106-
referrerPolicy="strict-origin-when-cross-origin"
107-
allowFullScreen
108-
></iframe>
279+
<div className="w-full">
280+
<MinimalistVideoPlayer src="/video/demo2.webm" />
109281
</div>
110282
</section>
111283

src/app/guide/page.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use client';
2+
3+
import Link from 'next/link';
4+
import { useRouter } from 'next/navigation';
5+
import { Button } from '@/components/ui/button';
6+
import { ArrowLeft } from 'lucide-react';
7+
8+
const GuidePage = () => {
9+
const router = useRouter();
10+
11+
return (
12+
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white transition-colors duration-300 py-12 px-4 sm:px-6 lg:px-8">
13+
<div className="max-w-3xl mx-auto">
14+
<Button
15+
onClick={() => router.back()}
16+
className="mb-8 flex items-center text-white rounded-full bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 transition-colors duration-300"
17+
>
18+
<ArrowLeft className="h-4 w-4 mr-2" />
19+
Go back
20+
</Button>
21+
22+
<h1 className="text-3xl font-bold mb-6">How to Use RepoTree Generator?</h1>
23+
24+
<div className="space-y-6">
25+
<ol className="list-decimal pl-6 space-y-2">
26+
<li>Select your repository type and enter the GitHub or GitLab repository URL.</li>
27+
<li>Click the <strong>Generate</strong> button.</li>
28+
<li>Explore the structure with ASCII or interactive mode for easy navigation.</li>
29+
<li>Customize your ASCII theme to match your style.</li>
30+
<li>Use the real-time search to quickly find files or folders.</li>
31+
<li>Copy or download the tree structure in multiple formats.</li>
32+
</ol>
33+
34+
<div>
35+
<h2 className="text-xl font-semibold">For Public Repositories</h2>
36+
<p className="mt-2">
37+
<strong>GitHub API Rate Limit:</strong> 60 requests per hour for unauthenticated users.
38+
<br />
39+
See the official GitHub rate limit documentation{' '}
40+
<a
41+
href="https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28"
42+
target="_blank"
43+
rel="noopener noreferrer"
44+
className="text-blue-600 underline hover:text-blue-800"
45+
>
46+
here
47+
</a>
48+
.
49+
<br />
50+
<br />
51+
<strong>GitLab API:</strong> Rate limits vary based on server configuration.
52+
<br />
53+
Learn more from the GitLab rate limit documentation{' '}
54+
<a
55+
href="https://docs.gitlab.com/security/rate_limits/"
56+
target="_blank"
57+
rel="noopener noreferrer"
58+
className="text-blue-600 underline hover:text-blue-800"
59+
>
60+
here
61+
</a>
62+
.
63+
<br />
64+
<br />
65+
To avoid rate limiting and access private repositories, it&apos;s recommended to use a Personal Access Token (PAT) for both platforms.
66+
</p>
67+
</div>
68+
69+
<div className="pt-4">
70+
<Link
71+
href="/generator"
72+
className="bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full transition-colors duration-300"
73+
>
74+
Try RepoTree Now
75+
</Link>
76+
</div>
77+
</div>
78+
</div>
79+
</div>
80+
);
81+
};
82+
83+
export default GuidePage;

0 commit comments

Comments
 (0)