-
-
-
-
-
-
-
-
-
-
-
- Preview Volume
-
- {previewVolume}%
-
-
-
-
-
-
- Network
- {isSpotifyBlockedCountry && (
- (Blocked by Spotify)
- )}
-
-
-
-
- {detectedFlagPath ? (
) : (
)}
-
- {isLoadingCurrentIPInfo
- ? "Detecting..."
- : currentIPInfo
- ? showIPAddress
- ? `${currentIPInfo.ip} - ${currentIPInfo.country}${detectedCountryCode ? ` (${detectedCountryCode})` : ""}`
- : `${currentIPInfo.country}${detectedCountryCode ? ` (${detectedCountryCode})` : ""}`
- : "Unavailable"}
-
-
- {currentIPInfo && !isLoadingCurrentIPInfo && (
setShowIPAddress((prev) => !prev)} className="inline-flex h-6 w-6 items-center justify-center rounded-sm text-muted-foreground hover:bg-muted hover:text-foreground transition-colors" aria-label={showIPAddress ? "Hide IP" : "Show IP"}>
- {showIPAddress ? : }
- )}
-
- {!isLoadingCurrentIPInfo && !currentIPInfo && currentIPInfoError && (
- IP detection unavailable
-
)}
-
-
- openExternal("https://afkarxyz.fyi")} className="gap-2">
-
- Website
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >);
-}
diff --git a/frontend/src/components/TrackInfo.tsx b/frontend/src/components/TrackInfo.tsx
deleted file mode 100644
index 4ea8e7a..0000000
--- a/frontend/src/components/TrackInfo.tsx
+++ /dev/null
@@ -1,190 +0,0 @@
-import { Button } from "@/components/ui/button";
-import { Card, CardContent } from "@/components/ui/card";
-import { Download, FolderOpen, CheckCircle, XCircle, FileText, FileCheck, Globe, ImageDown, Play, Pause } from "lucide-react";
-import { Spinner } from "@/components/ui/spinner";
-import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip";
-import type { TrackMetadata, TrackAvailability } from "@/types/api";
-import { usePreview } from "@/hooks/usePreview";
-import { AvailabilityLinks, hasAvailabilityLinks } from "./AvailabilityLinks";
-import { buildClickableArtists } from "@/lib/artist-links";
-interface TrackInfoProps {
- track: TrackMetadata & {
- album_name: string;
- release_date: string;
- };
- isDownloading: boolean;
- downloadingTrack: string | null;
- isDownloaded: boolean;
- isFailed: boolean;
- isSkipped: boolean;
- downloadingLyricsTrack?: string | null;
- downloadedLyrics?: boolean;
- failedLyrics?: boolean;
- skippedLyrics?: boolean;
- checkingAvailability?: boolean;
- availability?: TrackAvailability;
- downloadingCover?: boolean;
- downloadedCover?: boolean;
- failedCover?: boolean;
- skippedCover?: boolean;
- onDownload: (id: string, name: string, artists: string, albumName?: string, spotifyId?: string, playlistName?: string, durationMs?: number, position?: number, albumArtist?: string, releaseDate?: string, coverUrl?: string, spotifyTrackNumber?: number, spotifyDiscNumber?: number, spotifyTotalTracks?: number, spotifyTotalDiscs?: number, copyright?: string, publisher?: string) => void;
- onDownloadLyrics?: (spotifyId: string, name: string, artists: string, albumName?: string, albumArtist?: string, releaseDate?: string, discNumber?: number) => void;
- onCheckAvailability?: (spotifyId: string) => void;
- onDownloadCover?: (coverUrl: string, trackName: string, artistName: string, albumName?: string, playlistName?: string, position?: number, trackId?: string, albumArtist?: string, releaseDate?: string, discNumber?: number) => void;
- onOpenFolder: () => void;
- onAlbumClick?: (album: {
- id: string;
- name: string;
- external_urls: string;
- }) => void;
- onArtistClick?: (artist: {
- id: string;
- name: string;
- external_urls: string;
- }) => void;
- onBack?: () => void;
-}
-export function TrackInfo({ track, isDownloading, downloadingTrack, isDownloaded, isFailed, isSkipped, downloadingLyricsTrack, downloadedLyrics, failedLyrics, skippedLyrics, checkingAvailability, availability, downloadingCover, downloadedCover, failedCover, skippedCover, onDownload, onDownloadLyrics, onCheckAvailability, onDownloadCover, onOpenFolder, onAlbumClick, onArtistClick, onBack, }: TrackInfoProps) {
- const { playPreview, loadingPreview, playingTrack } = usePreview();
- const hasAlbumClick = !!(onAlbumClick && track.album_id && track.album_url);
- const clickableArtists = buildClickableArtists(track.artists, track.artists_data, track.artist_id, track.artist_url);
- const formatDuration = (ms: number) => {
- const minutes = Math.floor(ms / 60000);
- const seconds = Math.floor((ms % 60000) / 1000);
- return `${minutes}:${seconds.toString().padStart(2, "0")}`;
- };
- const formatPlays = (plays: string) => {
- const num = parseInt(plays, 10);
- if (isNaN(num))
- return plays;
- return num.toLocaleString();
- };
- return (
- {onBack && (
-
-
-
-
)}
-
-
-
- {track.images && (
-
-
- {formatDuration(track.duration_ms)}
-
-
)}
-
-
-
-
-
{track.name}
- {track.is_explicit && (E )}
- {isSkipped ? ( ) : isDownloaded ? ( ) : isFailed ? ( ) : null}
-
-
- {clickableArtists.length > 0 ? clickableArtists.map((artist, index) => (
- {onArtistClick ? ( onArtistClick({
- id: artist.id,
- name: artist.name,
- external_urls: artist.external_urls,
- })}>
- {artist.name}
- ) : (artist.name)}
- {index < clickableArtists.length - 1 && ", "}
- )) : track.artists}
-
-
-
-
-
-
Album
-
{hasAlbumClick ? ( onAlbumClick?.({
- id: track.album_id!,
- name: track.album_name,
- external_urls: track.album_url!,
- })}>
- {track.album_name}
- ) : (track.album_name)}
-
- {track.plays && (
-
Total Plays
-
{formatPlays(track.plays)}
-
)}
-
-
-
-
Release Date
-
{track.release_date}
-
- {track.copyright && (
-
Copyright
-
- {track.copyright}
-
-
)}
-
-
- {track.spotify_id && (
-
onDownload(track.spotify_id || "", track.name, track.artists, track.album_name, track.spotify_id, undefined, track.duration_ms, track.track_number, track.album_artist, track.release_date, track.images, track.track_number, track.disc_number, track.total_tracks, track.total_discs, track.copyright, track.publisher)} disabled={isDownloading || downloadingTrack === track.spotify_id}>
- {downloadingTrack === track.spotify_id ? ( ) : (<>
-
- Download
- >)}
-
- {track.spotify_id && (
-
- playPreview(track.spotify_id!, track.name)} variant="outline" size="icon" disabled={loadingPreview === track.spotify_id}>
- {loadingPreview === track.spotify_id ? ( ) : playingTrack === track.spotify_id ? ( ) : ( )}
-
-
-
- {playingTrack === track.spotify_id ? "Stop Preview" : "Play Preview"}
-
- )}
- {track.spotify_id && onDownloadLyrics && (
-
- onDownloadLyrics(track.spotify_id!, track.name, track.artists, track.album_name, track.album_artist, track.release_date, track.disc_number)} variant="outline" size="icon" disabled={downloadingLyricsTrack === track.spotify_id}>
- {downloadingLyricsTrack === track.spotify_id ? ( ) : skippedLyrics ? ( ) : downloadedLyrics ? ( ) : failedLyrics ? ( ) : ( )}
-
-
-
- Download Separate Lyric
-
- )}
- {track.images && onDownloadCover && (
-
- onDownloadCover(track.images, track.name, track.artists, track.album_name, undefined, undefined, track.spotify_id, track.album_artist, track.release_date, track.disc_number)} variant="outline" size="icon" disabled={downloadingCover}>
- {downloadingCover ? ( ) : skippedCover ? ( ) : downloadedCover ? ( ) : failedCover ? ( ) : ( )}
-
-
-
- Download Separate Cover
-
- )}
- {track.spotify_id && onCheckAvailability && (
-
- onCheckAvailability(track.spotify_id!)} variant="outline" size="icon" disabled={checkingAvailability}>
- {checkingAvailability ? ( ) : availability ? (hasAvailabilityLinks(availability) ? ( ) : ( )) : ( )}
-
-
-
-
-
- )}
- {isDownloaded && (
-
-
-
-
-
-
- Open Folder
-
- )}
-
)}
-
-
-
- );
-}
diff --git a/frontend/src/components/TrackList.tsx b/frontend/src/components/TrackList.tsx
deleted file mode 100644
index c161814..0000000
--- a/frontend/src/components/TrackList.tsx
+++ /dev/null
@@ -1,384 +0,0 @@
-import { Button } from "@/components/ui/button";
-import { Checkbox } from "@/components/ui/checkbox";
-import { Download, CheckCircle, XCircle, FileCheck, FileText, Globe, ImageDown, Play, Pause } from "lucide-react";
-import { Spinner } from "@/components/ui/spinner";
-import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip";
-import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination";
-import type { TrackMetadata, TrackAvailability } from "@/types/api";
-import { usePreview } from "@/hooks/usePreview";
-import { AvailabilityLinks, hasAvailabilityLinks } from "./AvailabilityLinks";
-import { buildClickableArtists } from "@/lib/artist-links";
-interface TrackListProps {
- tracks: TrackMetadata[];
- searchQuery: string;
- sortBy: string;
- selectedTracks: string[];
- downloadedTracks: Set
;
- failedTracks: Set;
- skippedTracks: Set;
- downloadingTrack: string | null;
- isDownloading: boolean;
- currentPage: number;
- itemsPerPage: number;
- showCheckboxes?: boolean;
- hideAlbumColumn?: boolean;
- folderName?: string;
- isArtistDiscography?: boolean;
- downloadedLyrics?: Set;
- failedLyrics?: Set;
- skippedLyrics?: Set;
- downloadingLyricsTrack?: string | null;
- checkingAvailabilityTrack?: string | null;
- availabilityMap?: Map;
- downloadedCovers?: Set;
- failedCovers?: Set;
- skippedCovers?: Set;
- downloadingCoverTrack?: string | null;
- onToggleTrack: (id: string) => void;
- onToggleSelectAll: (tracks: TrackMetadata[]) => void;
- onDownloadTrack: (id: string, name: string, artists: string, albumName: string, spotifyId?: string, folderName?: string, durationMs?: number, position?: number, albumArtist?: string, releaseDate?: string, coverUrl?: string, spotifyTrackNumber?: number, spotifyDiscNumber?: number, spotifyTotalTracks?: number, spotifyTotalDiscs?: number, copyright?: string, publisher?: string) => void;
- onDownloadLyrics?: (spotifyId: string, name: string, artists: string, albumName: string, folderName?: string, isArtistDiscography?: boolean, position?: number, albumArtist?: string, releaseDate?: string, discNumber?: number) => void;
- onCheckAvailability?: (spotifyId: string) => void;
- onDownloadCover?: (coverUrl: string, trackName: string, artistName: string, albumName: string, folderName?: string, isArtistDiscography?: boolean, position?: number, trackId?: string, albumArtist?: string, releaseDate?: string, discNumber?: number) => void;
- onPageChange: (page: number) => void;
- onAlbumClick?: (album: {
- id: string;
- name: string;
- external_urls: string;
- }) => void;
- onArtistClick?: (artist: {
- id: string;
- name: string;
- external_urls: string;
- }) => void;
- onTrackClick?: (track: TrackMetadata) => void;
-}
-export function TrackList({ tracks, searchQuery, sortBy, selectedTracks, downloadedTracks, failedTracks, skippedTracks, downloadingTrack, isDownloading, currentPage, itemsPerPage, showCheckboxes = false, hideAlbumColumn = false, folderName, isArtistDiscography = false, downloadedLyrics, failedLyrics, skippedLyrics, downloadingLyricsTrack, checkingAvailabilityTrack, availabilityMap, downloadedCovers, failedCovers, skippedCovers, downloadingCoverTrack, onToggleTrack, onToggleSelectAll, onDownloadTrack, onDownloadLyrics, onCheckAvailability, onDownloadCover, onPageChange, onAlbumClick, onArtistClick, onTrackClick, }: TrackListProps) {
- const { playPreview, loadingPreview, playingTrack } = usePreview();
- let filteredTracks = tracks.filter((track) => {
- if (!searchQuery)
- return true;
- const query = searchQuery.toLowerCase();
- return (track.name.toLowerCase().includes(query) ||
- track.artists.toLowerCase().includes(query) ||
- track.album_name.toLowerCase().includes(query));
- });
- if (sortBy === "title-asc") {
- filteredTracks = [...filteredTracks].sort((a, b) => a.name.localeCompare(b.name));
- }
- else if (sortBy === "title-desc") {
- filteredTracks = [...filteredTracks].sort((a, b) => b.name.localeCompare(a.name));
- }
- else if (sortBy === "artist-asc") {
- filteredTracks = [...filteredTracks].sort((a, b) => a.artists.localeCompare(b.artists));
- }
- else if (sortBy === "artist-desc") {
- filteredTracks = [...filteredTracks].sort((a, b) => b.artists.localeCompare(a.artists));
- }
- else if (sortBy === "duration-asc") {
- filteredTracks = [...filteredTracks].sort((a, b) => a.duration_ms - b.duration_ms);
- }
- else if (sortBy === "duration-desc") {
- filteredTracks = [...filteredTracks].sort((a, b) => b.duration_ms - a.duration_ms);
- }
- else if (sortBy === "plays-asc") {
- filteredTracks = [...filteredTracks].sort((a, b) => {
- const aPlays = a.plays ? parseInt(a.plays, 10) : 0;
- const bPlays = b.plays ? parseInt(b.plays, 10) : 0;
- if (isNaN(aPlays))
- return 1;
- if (isNaN(bPlays))
- return -1;
- return aPlays - bPlays;
- });
- }
- else if (sortBy === "plays-desc") {
- filteredTracks = [...filteredTracks].sort((a, b) => {
- const aPlays = a.plays ? parseInt(a.plays, 10) : 0;
- const bPlays = b.plays ? parseInt(b.plays, 10) : 0;
- if (isNaN(aPlays))
- return 1;
- if (isNaN(bPlays))
- return -1;
- return bPlays - aPlays;
- });
- }
- else if (sortBy === "downloaded") {
- filteredTracks = [...filteredTracks].sort((a, b) => {
- const aDownloaded = a.spotify_id ? downloadedTracks.has(a.spotify_id) : false;
- const bDownloaded = b.spotify_id ? downloadedTracks.has(b.spotify_id) : false;
- return (bDownloaded ? 1 : 0) - (aDownloaded ? 1 : 0);
- });
- }
- else if (sortBy === "not-downloaded") {
- filteredTracks = [...filteredTracks].sort((a, b) => {
- const aDownloaded = a.spotify_id ? downloadedTracks.has(a.spotify_id) : false;
- const bDownloaded = b.spotify_id ? downloadedTracks.has(b.spotify_id) : false;
- return (aDownloaded ? 1 : 0) - (bDownloaded ? 1 : 0);
- });
- }
- else if (sortBy === "failed") {
- filteredTracks = [...filteredTracks].sort((a, b) => {
- const aFailed = a.spotify_id ? failedTracks.has(a.spotify_id) : false;
- const bFailed = b.spotify_id ? failedTracks.has(b.spotify_id) : false;
- return (bFailed ? 1 : 0) - (aFailed ? 1 : 0);
- });
- }
- const totalPages = Math.ceil(filteredTracks.length / itemsPerPage);
- const startIndex = (currentPage - 1) * itemsPerPage;
- const endIndex = startIndex + itemsPerPage;
- const paginatedTracks = filteredTracks.slice(startIndex, endIndex);
- const getPaginationPages = (current: number, total: number): (number | 'ellipsis')[] => {
- if (total <= 10) {
- return Array.from({ length: total }, (_, i) => i + 1);
- }
- const pages: (number | 'ellipsis')[] = [];
- pages.push(1);
- if (current <= 7) {
- for (let i = 2; i <= 10; i++) {
- pages.push(i);
- }
- pages.push('ellipsis');
- pages.push(total);
- }
- else if (current >= total - 7) {
- pages.push('ellipsis');
- for (let i = total - 9; i <= total; i++) {
- pages.push(i);
- }
- }
- else {
- pages.push('ellipsis');
- pages.push(current - 1);
- pages.push(current);
- pages.push(current + 1);
- pages.push('ellipsis');
- pages.push(total);
- }
- return pages;
- };
- const tracksWithId = filteredTracks.filter((track) => track.spotify_id);
- const allSelected = tracksWithId.length > 0 &&
- tracksWithId.every((track) => selectedTracks.includes(track.spotify_id!));
- const formatDuration = (ms: number) => {
- const minutes = Math.floor(ms / 60000);
- const seconds = Math.floor((ms % 60000) / 1000);
- return `${minutes}:${seconds.toString().padStart(2, "0")}`;
- };
- const formatPlays = (plays: string | undefined) => {
- if (!plays)
- return "";
- const num = parseInt(plays, 10);
- if (isNaN(num))
- return plays;
- return num.toLocaleString();
- };
- const getAvailabilityButtonIcon = (spotifyId?: string) => {
- if (!spotifyId) {
- return ;
- }
- if (checkingAvailabilityTrack === spotifyId) {
- return ;
- }
- const availability = availabilityMap?.get(spotifyId);
- if (!availability) {
- return ;
- }
- if (hasAvailabilityLinks(availability)) {
- return ;
- }
- return ;
- };
- return (
-
-
-
-
-
- {showCheckboxes && (
- onToggleSelectAll(filteredTracks)}/>
- )}
-
- #
-
-
- Title
-
- {!hideAlbumColumn && (
- Album
- )}
-
- Duration
-
-
- Plays
-
-
- Actions
-
-
-
-
- {paginatedTracks.map((track, index) => (
- {showCheckboxes && (
- {track.spotify_id && ( onToggleTrack(track.spotify_id!)}/>)}
- )}
-
-
- {startIndex + index + 1}
- {track.status && (track.status === "UP" || track.status === "DOWN" || track.status === "NEW") && (
- {track.status === "NEW" ? "●" : track.status === "UP" ? "▲" : "▼"}
- )}
-
-
-
-
- {track.images && (
)}
-
-
- {onTrackClick ? ( onTrackClick(track)}>
- {track.name}
- ) : ({track.name} )}
- {track.is_explicit && (E )}
-
- {track.spotify_id && skippedTracks.has(track.spotify_id) ? ( ) : track.spotify_id && downloadedTracks.has(track.spotify_id) ? ( ) : track.spotify_id && failedTracks.has(track.spotify_id) ? ( ) : null}
-
-
- {(() => {
- const clickableArtists = buildClickableArtists(track.artists, track.artists_data, track.artist_id, track.artist_url);
- if (clickableArtists.length === 0) {
- return track.artists;
- }
- return clickableArtists.map((artist, i) => (
- {onArtistClick ? ( onArtistClick({
- id: artist.id,
- name: artist.name,
- external_urls: artist.external_urls,
- })}>
- {artist.name}
- ) : (artist.name)}
- {i < clickableArtists.length - 1 && ", "}
- ));
- })()}
-
-
-
-
- {!hideAlbumColumn && (
- {onAlbumClick && track.album_id && track.album_url ? ( onAlbumClick({
- id: track.album_id!,
- name: track.album_name,
- external_urls: track.album_url!,
- })}>
- {track.album_name}
- ) : (track.album_name)}
- )}
-
- {formatDuration(track.duration_ms)}
-
-
- {track.plays ? formatPlays(track.plays) : ""}
-
-
-
- {track.spotify_id && (
-
- onDownloadTrack(track.spotify_id!, track.name, track.artists, track.album_name, track.spotify_id, folderName, track.duration_ms, startIndex + index + 1, track.album_artist, track.release_date, track.images, track.track_number, track.disc_number, track.total_tracks, track.total_discs, track.copyright, track.publisher)} size="icon" disabled={isDownloading || downloadingTrack === track.spotify_id}>
- {downloadingTrack === track.spotify_id ? ( ) : skippedTracks.has(track.spotify_id) ? ( ) : downloadedTracks.has(track.spotify_id) ? ( ) : failedTracks.has(track.spotify_id) ? ( ) : ( )}
-
-
-
- {downloadingTrack === track.spotify_id ? (Downloading...
) : skippedTracks.has(track.spotify_id) ? (Already exists
) : downloadedTracks.has(track.spotify_id) ? (Downloaded
) : failedTracks.has(track.spotify_id) ? (Failed
) : (Download Track
)}
-
- )}
- {track.spotify_id && (
-
- playPreview(track.spotify_id!, track.name)} size="icon" variant="outline" disabled={loadingPreview === track.spotify_id}>
- {loadingPreview === track.spotify_id ? ( ) : playingTrack === track.spotify_id ? ( ) : ( )}
-
-
-
- {playingTrack === track.spotify_id ? "Stop Preview" : "Play Preview"}
-
- )}
- {track.spotify_id && onDownloadLyrics && (
-
- onDownloadLyrics(track.spotify_id!, track.name, track.artists, track.album_name, folderName, isArtistDiscography, startIndex + index + 1, track.album_artist, track.release_date, track.disc_number)} size="icon" variant="outline" disabled={downloadingLyricsTrack === track.spotify_id}>
- {downloadingLyricsTrack === track.spotify_id ? ( ) : skippedLyrics?.has(track.spotify_id) ? ( ) : downloadedLyrics?.has(track.spotify_id) ? ( ) : failedLyrics?.has(track.spotify_id) ? ( ) : ( )}
-
-
-
- Download Separate Lyric
-
- )}
- {track.images && onDownloadCover && (
-
- {
- const trackId = track.spotify_id || `${track.name}-${track.artists}`;
- onDownloadCover(track.images, track.name, track.artists, track.album_name, folderName, isArtistDiscography, startIndex + index + 1, trackId, track.album_artist, track.release_date, track.disc_number);
- }} size="icon" variant="outline" disabled={downloadingCoverTrack === (track.spotify_id || `${track.name}-${track.artists}`)}>
- {downloadingCoverTrack === (track.spotify_id || `${track.name}-${track.artists}`) ? ( ) : skippedCovers?.has(track.spotify_id || `${track.name}-${track.artists}`) ? ( ) : downloadedCovers?.has(track.spotify_id || `${track.name}-${track.artists}`) ? ( ) : failedCovers?.has(track.spotify_id || `${track.name}-${track.artists}`) ? ( ) : ( )}
-
-
-
- Download Separate Cover
-
- )}
- {track.spotify_id && onCheckAvailability && (
-
- onCheckAvailability(track.spotify_id!)} size="icon" variant="outline" disabled={checkingAvailabilityTrack === track.spotify_id}>
- {getAvailabilityButtonIcon(track.spotify_id)}
-
-
-
-
-
- )}
-
-
- ))}
-
-
-
-
-
- {totalPages > 1 && (
-
-
- {
- e.preventDefault();
- if (currentPage > 1)
- onPageChange(currentPage - 1);
- }} className={currentPage === 1 ? "pointer-events-none opacity-50" : "cursor-pointer"}/>
-
-
- {getPaginationPages(currentPage, totalPages).map((page, index) => (page === 'ellipsis' ? (
-
- ) : (
- {
- e.preventDefault();
- onPageChange(page);
- }} isActive={currentPage === page} className="cursor-pointer">
- {page}
-
- )))}
-
-
- {
- e.preventDefault();
- if (currentPage < totalPages)
- onPageChange(currentPage + 1);
- }} className={currentPage === totalPages
- ? "pointer-events-none opacity-50"
- : "cursor-pointer"}/>
-
-
- )}
-
);
-}
diff --git a/frontend/src/components/ui/activity.tsx b/frontend/src/components/ui/activity.tsx
deleted file mode 100644
index 1cde669..0000000
--- a/frontend/src/components/ui/activity.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-'use client';
-import type { Variants } from 'motion/react';
-import type { HTMLAttributes } from 'react';
-import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
-import { motion, useAnimation } from 'motion/react';
-import { cn } from '@/lib/utils';
-export interface ActivityIconHandle {
- startAnimation: () => void;
- stopAnimation: () => void;
-}
-interface ActivityIconProps extends HTMLAttributes {
- size?: number;
-}
-const PATH_VARIANTS: Variants = {
- normal: {
- pathLength: 1,
- opacity: 1,
- pathOffset: 0,
- },
- animate: {
- pathLength: [0, 1],
- opacity: [0, 1],
- pathOffset: [1, 0],
- transition: {
- duration: 0.8,
- ease: 'easeInOut',
- },
- },
-};
-const ActivityIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
- const controls = useAnimation();
- const isControlledRef = useRef(false);
- useImperativeHandle(ref, () => {
- isControlledRef.current = true;
- return {
- startAnimation: () => controls.start('animate'),
- stopAnimation: () => controls.start('normal'),
- };
- });
- const handleMouseEnter = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('animate');
- }
- else {
- onMouseEnter?.(e);
- }
- }, [controls, onMouseEnter]);
- const handleMouseLeave = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('normal');
- }
- else {
- onMouseLeave?.(e);
- }
- }, [controls, onMouseLeave]);
- return (
-
-
-
-
);
-});
-ActivityIcon.displayName = 'ActivityIcon';
-export { ActivityIcon };
diff --git a/frontend/src/components/ui/audio-lines.tsx b/frontend/src/components/ui/audio-lines.tsx
deleted file mode 100644
index 0040347..0000000
--- a/frontend/src/components/ui/audio-lines.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-"use client";
-import { motion, useAnimation } from "motion/react";
-import type { HTMLAttributes } from "react";
-import { forwardRef, useCallback, useImperativeHandle, useRef } from "react";
-import { cn } from "@/lib/utils";
-export interface AudioLinesIconHandle {
- startAnimation: () => void;
- stopAnimation: () => void;
-}
-interface AudioLinesIconProps extends HTMLAttributes {
- size?: number;
-}
-const AudioLinesIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
- const controls = useAnimation();
- const isControlledRef = useRef(false);
- useImperativeHandle(ref, () => {
- isControlledRef.current = true;
- return {
- startAnimation: () => controls.start("animate"),
- stopAnimation: () => controls.start("normal"),
- };
- });
- const handleMouseEnter = useCallback((e: React.MouseEvent) => {
- if (isControlledRef.current) {
- onMouseEnter?.(e);
- }
- else {
- controls.start("animate");
- }
- }, [controls, onMouseEnter]);
- const handleMouseLeave = useCallback((e: React.MouseEvent) => {
- if (isControlledRef.current) {
- onMouseLeave?.(e);
- }
- else {
- controls.start("normal");
- }
- }, [controls, onMouseLeave]);
- return ();
-});
-AudioLinesIcon.displayName = "AudioLinesIcon";
-export { AudioLinesIcon };
diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx
deleted file mode 100644
index 569cbdd..0000000
--- a/frontend/src/components/ui/badge.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import * as React from "react";
-import { Slot } from "@radix-ui/react-slot";
-import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils";
-const badgeVariants = cva("inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", {
- variants: {
- variant: {
- default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
- secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
- destructive: "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
- outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
- },
- },
- defaultVariants: {
- variant: "default",
- },
-});
-function Badge({ className, variant, asChild = false, ...props }: React.ComponentProps<"span"> & VariantProps & {
- asChild?: boolean;
-}) {
- const Comp = asChild ? Slot : "span";
- return ( );
-}
-export { Badge, badgeVariants };
diff --git a/frontend/src/components/ui/blocks-icon.tsx b/frontend/src/components/ui/blocks-icon.tsx
deleted file mode 100644
index b7d7029..0000000
--- a/frontend/src/components/ui/blocks-icon.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-"use client";
-import type { Variants } from "motion/react";
-import { motion, useAnimation } from "motion/react";
-import type { HTMLAttributes } from "react";
-import { forwardRef, useCallback, useImperativeHandle, useRef } from "react";
-import { cn } from "@/lib/utils";
-export interface BlocksIconHandle {
- startAnimation: () => void;
- stopAnimation: () => void;
-}
-interface BlocksIconProps extends HTMLAttributes {
- size?: number;
- loop?: boolean;
-}
-const VARIANTS: Variants = {
- normal: { translateX: 0, translateY: 0 },
- animate: { translateX: -4, translateY: 4 },
-};
-const BlocksIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 28, loop = false, ...props }, ref) => {
- const controls = useAnimation();
- const isControlledRef = useRef(false);
- useImperativeHandle(ref, () => {
- isControlledRef.current = true;
- return {
- startAnimation: () => controls.start("animate"),
- stopAnimation: () => controls.start("normal"),
- };
- });
- const handleMouseEnter = useCallback((e: React.MouseEvent) => {
- if (isControlledRef.current) {
- onMouseEnter?.(e);
- }
- else {
- controls.start("animate");
- }
- }, [controls, onMouseEnter]);
- const handleMouseLeave = useCallback((e: React.MouseEvent) => {
- if (isControlledRef.current) {
- onMouseLeave?.(e);
- }
- else {
- controls.start("normal");
- }
- }, [controls, onMouseLeave]);
- return ();
-});
-BlocksIcon.displayName = "BlocksIcon";
-export { BlocksIcon };
diff --git a/frontend/src/components/ui/bug-report-icon.tsx b/frontend/src/components/ui/bug-report-icon.tsx
deleted file mode 100644
index 463f9ff..0000000
--- a/frontend/src/components/ui/bug-report-icon.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-"use client";
-
-import type { Transition, Variants } from "motion/react";
-import { AnimatePresence, motion } from "motion/react";
-import { useEffect, useState, type HTMLAttributes } from "react";
-import { cn } from "@/lib/utils";
-
-type ReportIconMode = "bug" | "bulb";
-
-interface BugReportIconProps extends HTMLAttributes {
- size?: number;
- loop?: boolean;
-}
-
-const LOOP_INTERVAL_MS = 2200;
-
-const GROUP_VARIANTS: Variants = {
- hidden: {
- opacity: 0,
- },
- visible: {
- opacity: 1,
- transition: {
- duration: 0.2,
- ease: [0, 0, 0.2, 1],
- },
- },
- exit: {
- opacity: 0,
- transition: {
- duration: 0.18,
- ease: [0.4, 0, 1, 1],
- },
- },
-};
-
-const DRAW_VARIANTS: Variants = {
- hidden: {
- pathLength: 0,
- opacity: 0,
- },
- visible: {
- pathLength: 1,
- opacity: 1,
- },
- exit: {
- pathLength: 1,
- opacity: 0,
- },
-};
-
-function createDrawTransition(delay = 0, duration = 0.36): Transition {
- return {
- duration,
- delay,
- ease: [0.4, 0, 0.2, 1],
- opacity: { delay },
- };
-}
-
-function BugPaths() {
- return (<>
-
-
-
-
-
-
-
-
-
-
-
- >);
-}
-
-function BulbPaths() {
- return (<>
-
-
-
- >);
-}
-
-function ReportIconGroup({ mode }: { mode: ReportIconMode }) {
- return (
- {mode === "bug" ? : }
- );
-}
-
-function StaticBugIcon() {
- return (
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-function BugReportIcon({ className, size = 28, loop = false, ...props }: BugReportIconProps) {
- const [mode, setMode] = useState("bug");
-
- useEffect(() => {
- if (!loop) {
- setMode("bug");
- return;
- }
-
- const intervalId = window.setInterval(() => {
- setMode((currentMode) => currentMode === "bug" ? "bulb" : "bug");
- }, LOOP_INTERVAL_MS);
-
- return () => window.clearInterval(intervalId);
- }, [loop]);
-
- return (
-
- {loop ? (
-
- ) : ( )}
-
-
);
-}
-
-export { BugReportIcon };
diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx
deleted file mode 100644
index a6bb15e..0000000
--- a/frontend/src/components/ui/button.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import * as React from "react";
-import { Slot } from "@radix-ui/react-slot";
-import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils";
-const buttonVariants = cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all cursor-pointer disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", {
- variants: {
- variant: {
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
- destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
- outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
- link: "text-primary underline-offset-4 hover:underline",
- },
- size: {
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
- icon: "h-9 w-9 p-0",
- "icon-sm": "h-8 w-8 p-0",
- "icon-lg": "h-10 w-10 p-0",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
-});
-function Button({ className, variant, size, asChild = false, ...props }: React.ComponentProps<"button"> & VariantProps & {
- asChild?: boolean;
-}) {
- const Comp = asChild ? Slot : "button";
- return ( );
-}
-export { Button, buttonVariants };
diff --git a/frontend/src/components/ui/card.tsx b/frontend/src/components/ui/card.tsx
deleted file mode 100644
index 9beb361..0000000
--- a/frontend/src/components/ui/card.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import * as React from "react";
-import { cn } from "@/lib/utils";
-function Card({ className, ...props }: React.ComponentProps<"div">) {
- return (
);
-}
-function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
- return (
);
-}
-function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
- return (
);
-}
-function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
- return (
);
-}
-function CardAction({ className, ...props }: React.ComponentProps<"div">) {
- return (
);
-}
-function CardContent({ className, ...props }: React.ComponentProps<"div">) {
- return (
);
-}
-function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
- return (
);
-}
-export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
diff --git a/frontend/src/components/ui/checkbox.tsx b/frontend/src/components/ui/checkbox.tsx
deleted file mode 100644
index 4da2c71..0000000
--- a/frontend/src/components/ui/checkbox.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-"use client";
-import * as React from "react";
-import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
-import { CheckIcon } from "lucide-react";
-import { cn } from "@/lib/utils";
-function Checkbox({ className, ...props }: React.ComponentProps) {
- return (
-
-
-
- );
-}
-export { Checkbox };
diff --git a/frontend/src/components/ui/coffee.tsx b/frontend/src/components/ui/coffee.tsx
deleted file mode 100644
index 0dd8da8..0000000
--- a/frontend/src/components/ui/coffee.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-'use client';
-import type { Variants } from 'motion/react';
-import type { HTMLAttributes } from 'react';
-import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
-import { motion, useAnimation } from 'motion/react';
-import { cn } from '@/lib/utils';
-export interface CoffeeIconHandle {
- startAnimation: () => void;
- stopAnimation: () => void;
-}
-interface CoffeeIconProps extends HTMLAttributes {
- size?: number;
- loop?: boolean;
-}
-const PATH_VARIANTS: Variants = {
- normal: {
- y: 0,
- opacity: 1,
- },
- animate: (custom: number) => ({
- y: -3,
- opacity: [0, 1, 0],
- transition: {
- repeat: Infinity,
- duration: 1.5,
- ease: 'easeInOut',
- delay: 0.2 * custom,
- },
- }),
-};
-const CoffeeIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 28, loop = false, ...props }, ref) => {
- const controls = useAnimation();
- const isControlledRef = useRef(false);
- useImperativeHandle(ref, () => {
- isControlledRef.current = true;
- return {
- startAnimation: () => controls.start('animate'),
- stopAnimation: () => controls.start('normal'),
- };
- });
- const handleMouseEnter = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('animate');
- }
- else {
- onMouseEnter?.(e);
- }
- }, [controls, onMouseEnter]);
- const handleMouseLeave = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('normal');
- }
- else {
- onMouseLeave?.(e);
- }
- }, [controls, onMouseLeave]);
- return ();
-});
-CoffeeIcon.displayName = 'CoffeeIcon';
-export { CoffeeIcon };
diff --git a/frontend/src/components/ui/context-menu.tsx b/frontend/src/components/ui/context-menu.tsx
deleted file mode 100644
index 5a334e3..0000000
--- a/frontend/src/components/ui/context-menu.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-"use client";
-import * as React from "react";
-import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
-import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
-import { cn } from "@/lib/utils";
-function ContextMenu({ ...props }: React.ComponentProps) {
- return ;
-}
-function ContextMenuTrigger({ ...props }: React.ComponentProps) {
- return ( );
-}
-function ContextMenuGroup({ ...props }: React.ComponentProps) {
- return ( );
-}
-function ContextMenuPortal({ ...props }: React.ComponentProps) {
- return ( );
-}
-function ContextMenuSub({ ...props }: React.ComponentProps) {
- return ;
-}
-function ContextMenuRadioGroup({ ...props }: React.ComponentProps) {
- return ( );
-}
-function ContextMenuSubTrigger({ className, inset, children, ...props }: React.ComponentProps & {
- inset?: boolean;
-}) {
- return (
- {children}
-
- );
-}
-function ContextMenuSubContent({ className, ...props }: React.ComponentProps) {
- return ( );
-}
-function ContextMenuContent({ className, ...props }: React.ComponentProps) {
- return (
-
- );
-}
-function ContextMenuItem({ className, inset, variant = "default", ...props }: React.ComponentProps & {
- inset?: boolean;
- variant?: "default" | "destructive";
-}) {
- return ( );
-}
-function ContextMenuCheckboxItem({ className, children, checked, ...props }: React.ComponentProps) {
- return (
-
-
-
-
-
- {children}
- );
-}
-function ContextMenuRadioItem({ className, children, ...props }: React.ComponentProps) {
- return (
-
-
-
-
-
- {children}
- );
-}
-function ContextMenuLabel({ className, inset, ...props }: React.ComponentProps & {
- inset?: boolean;
-}) {
- return ( );
-}
-function ContextMenuSeparator({ className, ...props }: React.ComponentProps) {
- return ( );
-}
-function ContextMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
- return ( );
-}
-export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup, };
diff --git a/frontend/src/components/ui/dialog.tsx b/frontend/src/components/ui/dialog.tsx
deleted file mode 100644
index 8c5ef4f..0000000
--- a/frontend/src/components/ui/dialog.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-"use client";
-import * as React from "react";
-import * as DialogPrimitive from "@radix-ui/react-dialog";
-import { XIcon } from "lucide-react";
-import { cn } from "@/lib/utils";
-function Dialog({ ...props }: React.ComponentProps) {
- return ;
-}
-function DialogTrigger({ ...props }: React.ComponentProps) {
- return ;
-}
-function DialogPortal({ ...props }: React.ComponentProps) {
- return ;
-}
-function DialogClose({ ...props }: React.ComponentProps) {
- return ;
-}
-function DialogOverlay({ className, ...props }: React.ComponentProps) {
- return ( );
-}
-function DialogContent({ className, children, showCloseButton = true, ...props }: React.ComponentProps & {
- showCloseButton?: boolean;
-}) {
- return (
-
-
- {children}
- {showCloseButton && (
-
- Close
- )}
-
- );
-}
-function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
- return (
);
-}
-function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
- return (
);
-}
-function DialogTitle({ className, ...props }: React.ComponentProps) {
- return ( );
-}
-function DialogDescription({ className, ...props }: React.ComponentProps) {
- return ( );
-}
-export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, };
diff --git a/frontend/src/components/ui/dropdown-menu.tsx b/frontend/src/components/ui/dropdown-menu.tsx
deleted file mode 100644
index c769f7e..0000000
--- a/frontend/src/components/ui/dropdown-menu.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import * as React from "react";
-import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
-import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
-import { cn } from "@/lib/utils";
-function DropdownMenu({ ...props }: React.ComponentProps) {
- return ;
-}
-function DropdownMenuPortal({ ...props }: React.ComponentProps) {
- return ( );
-}
-function DropdownMenuTrigger({ ...props }: React.ComponentProps) {
- return ( );
-}
-function DropdownMenuContent({ className, sideOffset = 4, ...props }: React.ComponentProps) {
- return (
-
- );
-}
-function DropdownMenuGroup({ ...props }: React.ComponentProps) {
- return ( );
-}
-function DropdownMenuItem({ className, inset, variant = "default", ...props }: React.ComponentProps & {
- inset?: boolean;
- variant?: "default" | "destructive";
-}) {
- return ( );
-}
-function DropdownMenuCheckboxItem({ className, children, checked, ...props }: React.ComponentProps) {
- return (
-
-
-
-
-
- {children}
- );
-}
-function DropdownMenuRadioGroup({ ...props }: React.ComponentProps) {
- return ( );
-}
-function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps) {
- return (
-
-
-
-
-
- {children}
- );
-}
-function DropdownMenuLabel({ className, inset, ...props }: React.ComponentProps & {
- inset?: boolean;
-}) {
- return ( );
-}
-function DropdownMenuSeparator({ className, ...props }: React.ComponentProps) {
- return ( );
-}
-function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
- return ( );
-}
-function DropdownMenuSub({ ...props }: React.ComponentProps) {
- return ;
-}
-function DropdownMenuSubTrigger({ className, inset, children, ...props }: React.ComponentProps & {
- inset?: boolean;
-}) {
- return (
- {children}
-
- );
-}
-function DropdownMenuSubContent({ className, ...props }: React.ComponentProps) {
- return ( );
-}
-export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
diff --git a/frontend/src/components/ui/file-music.tsx b/frontend/src/components/ui/file-music.tsx
deleted file mode 100644
index aa0d576..0000000
--- a/frontend/src/components/ui/file-music.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-'use client';
-import type { Variants } from 'motion/react';
-import type { HTMLAttributes } from 'react';
-import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
-import { motion, useAnimation } from 'motion/react';
-import { cn } from '@/lib/utils';
-export interface FileMusicIconHandle {
- startAnimation: () => void;
- stopAnimation: () => void;
-}
-interface FileMusicIconProps extends HTMLAttributes {
- size?: number;
-}
-const PATH_VARIANTS: Variants = {
- normal: {
- pathLength: 1,
- opacity: 1,
- },
- animate: {
- pathLength: [0, 1],
- opacity: [0, 1],
- transition: {
- duration: 0.6,
- ease: 'easeInOut',
- },
- },
-};
-const FileMusicIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
- const controls = useAnimation();
- const isControlledRef = useRef(false);
- useImperativeHandle(ref, () => {
- isControlledRef.current = true;
- return {
- startAnimation: () => controls.start('animate'),
- stopAnimation: () => controls.start('normal'),
- };
- });
- const handleMouseEnter = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('animate');
- }
- else {
- onMouseEnter?.(e);
- }
- }, [controls, onMouseEnter]);
- const handleMouseLeave = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('normal');
- }
- else {
- onMouseLeave?.(e);
- }
- }, [controls, onMouseLeave]);
- return (
-
-
-
-
-
-
-
);
-});
-FileMusicIcon.displayName = 'FileMusicIcon';
-export { FileMusicIcon };
diff --git a/frontend/src/components/ui/file-pen.tsx b/frontend/src/components/ui/file-pen.tsx
deleted file mode 100644
index ccc727f..0000000
--- a/frontend/src/components/ui/file-pen.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-'use client';
-import type { Variants } from 'motion/react';
-import type { HTMLAttributes } from 'react';
-import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
-import { motion, useAnimation } from 'motion/react';
-import { cn } from '@/lib/utils';
-export interface FilePenIconHandle {
- startAnimation: () => void;
- stopAnimation: () => void;
-}
-interface FilePenIconProps extends HTMLAttributes {
- size?: number;
-}
-const PATH_VARIANTS: Variants = {
- normal: {
- pathLength: 1,
- opacity: 1,
- },
- animate: {
- pathLength: [0, 1],
- opacity: [0, 1],
- transition: {
- duration: 0.6,
- ease: 'easeInOut',
- },
- },
-};
-const FilePenIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
- const controls = useAnimation();
- const isControlledRef = useRef(false);
- useImperativeHandle(ref, () => {
- isControlledRef.current = true;
- return {
- startAnimation: () => controls.start('animate'),
- stopAnimation: () => controls.start('normal'),
- };
- });
- const handleMouseEnter = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('animate');
- }
- else {
- onMouseEnter?.(e);
- }
- }, [controls, onMouseEnter]);
- const handleMouseLeave = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('normal');
- }
- else {
- onMouseLeave?.(e);
- }
- }, [controls, onMouseLeave]);
- return (
-
-
-
-
-
-
);
-});
-FilePenIcon.displayName = 'FilePenIcon';
-export { FilePenIcon };
diff --git a/frontend/src/components/ui/history-icon.tsx b/frontend/src/components/ui/history-icon.tsx
deleted file mode 100644
index a954d37..0000000
--- a/frontend/src/components/ui/history-icon.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-"use client";
-import type { Transition, Variants } from "motion/react";
-import { motion, useAnimation } from "motion/react";
-import type { HTMLAttributes } from "react";
-import { forwardRef, useCallback, useImperativeHandle, useRef } from "react";
-import { cn } from "@/lib/utils";
-export interface HistoryIconHandle {
- startAnimation: () => void;
- stopAnimation: () => void;
-}
-interface HistoryIconProps extends HTMLAttributes {
- size?: number;
-}
-const ARROW_TRANSITION: Transition = {
- type: "spring",
- stiffness: 250,
- damping: 25,
-};
-const ARROW_VARIANTS: Variants = {
- normal: {
- rotate: "0deg",
- },
- animate: {
- rotate: "-50deg",
- },
-};
-const HAND_TRANSITION: Transition = {
- duration: 0.6,
- ease: [0.4, 0, 0.2, 1],
-};
-const HAND_VARIANTS: Variants = {
- normal: {
- rotate: 0,
- originX: "0%",
- originY: "100%",
- },
- animate: {
- rotate: -360,
- originX: "0%",
- originY: "100%",
- },
-};
-const MINUTE_HAND_TRANSITION: Transition = {
- duration: 0.5,
- ease: "easeInOut",
-};
-const MINUTE_HAND_VARIANTS: Variants = {
- normal: {
- rotate: 0,
- originX: "0%",
- originY: "0%",
- },
- animate: {
- rotate: -45,
- originX: "0%",
- originY: "0%",
- },
-};
-const HistoryIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
- const controls = useAnimation();
- const isControlledRef = useRef(false);
- useImperativeHandle(ref, () => {
- isControlledRef.current = true;
- return {
- startAnimation: () => controls.start("animate"),
- stopAnimation: () => controls.start("normal"),
- };
- });
- const handleMouseEnter = useCallback((e: React.MouseEvent) => {
- if (isControlledRef.current) {
- onMouseEnter?.(e);
- }
- else {
- controls.start("animate");
- }
- }, [controls, onMouseEnter]);
- const handleMouseLeave = useCallback((e: React.MouseEvent) => {
- if (isControlledRef.current) {
- onMouseLeave?.(e);
- }
- else {
- controls.start("normal");
- }
- }, [controls, onMouseLeave]);
- return ();
-});
-HistoryIcon.displayName = "HistoryIcon";
-export { HistoryIcon };
diff --git a/frontend/src/components/ui/home.tsx b/frontend/src/components/ui/home.tsx
deleted file mode 100644
index 8b6eb25..0000000
--- a/frontend/src/components/ui/home.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-'use client';
-import type { Transition, Variants } from 'motion/react';
-import type { HTMLAttributes } from 'react';
-import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
-import { motion, useAnimation } from 'motion/react';
-import { cn } from '@/lib/utils';
-export interface HomeIconHandle {
- startAnimation: () => void;
- stopAnimation: () => void;
-}
-interface HomeIconProps extends HTMLAttributes {
- size?: number;
-}
-const DEFAULT_TRANSITION: Transition = {
- duration: 0.6,
- opacity: { duration: 0.2 },
-};
-const PATH_VARIANTS: Variants = {
- normal: {
- pathLength: 1,
- opacity: 1,
- },
- animate: {
- opacity: [0, 1],
- pathLength: [0, 1],
- },
-};
-const HomeIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
- const controls = useAnimation();
- const isControlledRef = useRef(false);
- useImperativeHandle(ref, () => {
- isControlledRef.current = true;
- return {
- startAnimation: () => controls.start('animate'),
- stopAnimation: () => controls.start('normal'),
- };
- });
- const handleMouseEnter = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('animate');
- }
- else {
- onMouseEnter?.(e);
- }
- }, [controls, onMouseEnter]);
- const handleMouseLeave = useCallback((e: React.MouseEvent) => {
- if (!isControlledRef.current) {
- controls.start('normal');
- }
- else {
- onMouseLeave?.(e);
- }
- }, [controls, onMouseLeave]);
- return ();
-});
-HomeIcon.displayName = 'HomeIcon';
-export { HomeIcon };
diff --git a/frontend/src/components/ui/input-with-context.tsx b/frontend/src/components/ui/input-with-context.tsx
deleted file mode 100644
index 1c3ee0e..0000000
--- a/frontend/src/components/ui/input-with-context.tsx
+++ /dev/null
@@ -1,156 +0,0 @@
-import * as React from "react";
-import { Input } from "@/components/ui/input";
-import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, } from "@/components/ui/context-menu";
-import { Scissors, Copy, Clipboard, Type } from "lucide-react";
-export interface InputWithContextProps extends React.InputHTMLAttributes {
- onValueChange?: (value: string) => void;
-}
-const InputWithContext = React.forwardRef(({ className, type, onValueChange, onChange, ...props }, ref) => {
- const inputRef = React.useRef(null);
- const [hasSelection, setHasSelection] = React.useState(false);
- const [canPaste, setCanPaste] = React.useState(false);
- React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement);
- const updateSelectionState = () => {
- const input = inputRef.current;
- if (!input)
- return;
- const start = input.selectionStart ?? 0;
- const end = input.selectionEnd ?? 0;
- setHasSelection(start !== end);
- };
- const checkClipboard = async () => {
- try {
- const text = await navigator.clipboard.readText();
- setCanPaste(text.length > 0);
- }
- catch {
- setCanPaste(false);
- }
- };
- const handleCut = async () => {
- const input = inputRef.current;
- if (!input)
- return;
- const start = input.selectionStart ?? 0;
- const end = input.selectionEnd ?? 0;
- const selectedText = input.value.substring(start, end);
- if (selectedText) {
- try {
- await navigator.clipboard.writeText(selectedText);
- const newValue = input.value.substring(0, start) + input.value.substring(end);
- input.value = newValue;
- input.setSelectionRange(start, start);
- if (onChange) {
- const event = {
- target: input,
- currentTarget: input,
- } as React.ChangeEvent;
- onChange(event);
- }
- if (onValueChange) {
- onValueChange(newValue);
- }
- input.focus();
- }
- catch (err) {
- console.error("Failed to cut:", err);
- }
- }
- };
- const handleCopy = async () => {
- const input = inputRef.current;
- if (!input)
- return;
- const start = input.selectionStart ?? 0;
- const end = input.selectionEnd ?? 0;
- const selectedText = input.value.substring(start, end);
- if (selectedText) {
- try {
- await navigator.clipboard.writeText(selectedText);
- input.focus();
- }
- catch (err) {
- console.error("Failed to copy:", err);
- }
- }
- };
- const handlePaste = async () => {
- const input = inputRef.current;
- if (!input)
- return;
- try {
- const text = await navigator.clipboard.readText();
- const start = input.selectionStart ?? 0;
- const end = input.selectionEnd ?? 0;
- const newValue = input.value.substring(0, start) + text + input.value.substring(end);
- input.value = newValue;
- const newPosition = start + text.length;
- input.setSelectionRange(newPosition, newPosition);
- if (onChange) {
- const event = {
- target: input,
- currentTarget: input,
- } as React.ChangeEvent;
- onChange(event);
- }
- if (onValueChange) {
- onValueChange(newValue);
- }
- input.focus();
- await checkClipboard();
- }
- catch (err) {
- console.error("Failed to paste:", err);
- }
- };
- const handleSelectAll = () => {
- const input = inputRef.current;
- if (!input)
- return;
- input.select();
- input.focus();
- updateSelectionState();
- };
- const handleInputChange = (e: React.ChangeEvent) => {
- if (onChange) {
- onChange(e);
- }
- if (onValueChange) {
- onValueChange(e.target.value);
- }
- };
- return ( {
- if (open) {
- checkClipboard();
- }
- }}>
-
-
-
-
-
-
- Cut
- Ctrl+X
-
-
-
- Copy
- Ctrl+C
-
-
-
- Paste
- Ctrl+V
-
-
-
-
- Select All
- Ctrl+A
-
-
- );
-});
-InputWithContext.displayName = "InputWithContext";
-export { InputWithContext };
diff --git a/frontend/src/components/ui/input.tsx b/frontend/src/components/ui/input.tsx
deleted file mode 100644
index 3a6e9b1..0000000
--- a/frontend/src/components/ui/input.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import * as React from "react";
-import { cn } from "@/lib/utils";
-function Input({ className, type, ...props }: React.ComponentProps<"input">) {
- return ( );
-}
-export { Input };
diff --git a/frontend/src/components/ui/label.tsx b/frontend/src/components/ui/label.tsx
deleted file mode 100644
index e4ef7b3..0000000
--- a/frontend/src/components/ui/label.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-"use client";
-import * as React from "react";
-import * as LabelPrimitive from "@radix-ui/react-label";
-import { cn } from "@/lib/utils";
-function Label({ className, ...props }: React.ComponentProps) {
- return ( );
-}
-export { Label };
diff --git a/frontend/src/components/ui/menubar.tsx b/frontend/src/components/ui/menubar.tsx
deleted file mode 100644
index 2dfd7f2..0000000
--- a/frontend/src/components/ui/menubar.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-"use client";
-import * as React from "react";
-import * as MenubarPrimitive from "@radix-ui/react-menubar";
-import { Check, ChevronRight, Circle } from "lucide-react";
-import { cn } from "@/lib/utils";
-const Menubar = React.forwardRef, React.ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( ));
-Menubar.displayName = MenubarPrimitive.Root.displayName;
-const MenubarMenu = MenubarPrimitive.Menu;
-const MenubarGroup = MenubarPrimitive.Group;
-const MenubarPortal = MenubarPrimitive.Portal;
-const MenubarSub = MenubarPrimitive.Sub;
-const MenubarRadioGroup = MenubarPrimitive.RadioGroup;
-const MenubarTrigger = React.forwardRef, React.ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( ));
-MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName;
-const MenubarSubTrigger = React.forwardRef, React.ComponentPropsWithoutRef & {
- inset?: boolean;
-}>(({ className, inset, children, ...props }, ref) => (
- {children}
-
- ));
-MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName;
-const MenubarSubContent = React.forwardRef, React.ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( ));
-MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName;
-const MenubarContent = React.forwardRef, React.ComponentPropsWithoutRef>(({ className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, ref) => (
-
- ));
-MenubarContent.displayName = MenubarPrimitive.Content.displayName;
-const MenubarItem = React.forwardRef, React.ComponentPropsWithoutRef & {
- inset?: boolean;
-}>(({ className, inset, ...props }, ref) => ( ));
-MenubarItem.displayName = MenubarPrimitive.Item.displayName;
-const MenubarCheckboxItem = React.forwardRef, React.ComponentPropsWithoutRef>(({ className, children, checked, ...props }, ref) => (
-
-