refactor/feat: ipod nano 7g-style now playing component, discontinue fontawesome, content updates

- redesign NowPlaying widget with iPod Nano 7th generation-inspired UI
- content updates (home, about)
- general code cleanup
- bump deps, clean up
- update README for self hosting
- remove old workflows
This commit is contained in:
Aidan 2025-08-02 02:39:24 -04:00
parent db86ce3277
commit d613b58dd6
17 changed files with 466 additions and 460 deletions

View file

@ -1,24 +0,0 @@
import React from 'react';
import Link from 'next/link';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';
interface BackButtonProps {
href: string;
label?: string;
}
const BackButton: React.FC<BackButtonProps> = ({ href, label = 'Back' }) => {
return (
<Link
href={href}
className="inline-flex items-center px-4 py-2 mt-4 text-white bg-gray-800 rounded-sm shadow-md transition-all duration-300 ease-in-out hover:bg-gray-700 hover:shadow-lg hover:-translate-y-0.5 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
aria-label={`Go back to ${label}`}
>
<FontAwesomeIcon icon={faArrowLeft} className="mr-2" />
{label}
</Link>
);
};
export default BackButton;

View file

@ -1,30 +1,40 @@
import React from 'react'
import Link from 'next/link'
import { cn } from '@/lib/utils'
interface ButtonProps {
href: string
label: string
icon?: React.ElementType
target?: string
variant?: "primary" | "rounded"
className?: string
icon?: React.ReactNode
children: React.ReactNode
}
const Button: React.FC<ButtonProps> = ({ href, label, icon, target, className }) => {
return (
<Link
href={href}
className={className ? (
cn("inline-flex items-center bg-gray-800 text-white font-bold py-2 px-4 rounded-sm shadow-md transition-all duration-300 ease-in-out hover:bg-gray-700 hover:shadow-lg hover:-translate-y-0.5 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-gray-500", className)
) : (
"inline-flex items-center bg-gray-800 text-white font-bold py-2 px-4 rounded-sm shadow-md transition-all duration-300 ease-in-out hover:bg-gray-700 hover:shadow-lg hover:-translate-y-0.5 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
)}
target={target}
>
{icon && React.createElement(icon, { size: 20, className: "mr-2" })}
{label}
</Link>
)
const Button: React.FC<ButtonProps> = ({ href, target, variant, className, icon, children }) => {
if (!variant || variant === "primary") {
return (
<Link
href={href}
className={`inline-flex items-center bg-gray-800 text-white font-bold py-2 px-4 rounded-sm shadow-md transition-all duration-300 ease-in-out hover:bg-gray-700 hover:shadow-lg hover:-translate-y-0.5 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 gap-2 ${className}`}
target={target}
>
{icon}
{children}
</Link>
)
} else if (variant === "rounded") {
return (
<Link
href={href}
target={target}
rel={target === "_blank" ? "noopener noreferrer" : undefined}
className={`bg-gray-700 text-white px-4 py-2 rounded-full hover:bg-gray-600 transition-colors inline-flex items-center justify-center gap-2 whitespace-nowrap ${className}`}
>
{icon}
{children}
</Link>
)
}
}
export default Button

View file

@ -1,26 +0,0 @@
import { IconDefinition } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import Link from 'next/link';
interface ContactButtonProps {
href: string;
icon: IconDefinition;
label: string;
className?: string;
}
function ContactButton({ href, icon, label, className }: ContactButtonProps) {
return (
<Link
href={href}
target="_blank"
rel="noopener noreferrer"
className={`bg-gray-700 text-white px-4 py-2 rounded-full hover:bg-gray-600 transition-colors inline-flex items-center ${className}`}
>
<FontAwesomeIcon icon={icon} className="text-xl mr-2" />
{label}
</Link>
)
}
export default ContactButton;

View file

@ -0,0 +1,37 @@
"use client"
import type React from "react"
interface ScrollTxtProps {
text: string
className?: string
type?: 'artist' | 'track' | 'release'
}
const ScrollTxt: React.FC<ScrollTxtProps> = ({ text, className = "", type }) => {
const getTypeClass = (type?: string) => {
switch(type) {
case 'artist':
return 'text-white text-xs opacity-90 font-medium text-[8px]'
case 'track':
return 'text-white text-xs font-bold'
case 'release':
return 'text-white text-xs opacity-90 font-medium text-[8px]'
default:
return ''
}
}
const textClass = getTypeClass(type)
return (
<div className={`overflow-hidden ${className}`}>
<div className="whitespace-nowrap inline-block">
<span className={textClass}>{text}</span>
</div>
</div>
)
}
export default ScrollTxt

View file

@ -1,47 +0,0 @@
"use client"
import type React from "react"
import { useEffect, useRef, useState } from "react"
interface ScrollTxtProps {
text: string
className?: string
}
const ScrollTxt: React.FC<ScrollTxtProps> = ({ text, className = "" }) => {
const containerRef = useRef<HTMLDivElement>(null)
const textRef = useRef<HTMLDivElement>(null)
const [shouldScroll, setShouldScroll] = useState(false)
useEffect(() => {
if (containerRef.current && textRef.current) {
const containerWidth = containerRef.current.offsetWidth
const textWidth = textRef.current.offsetWidth
setShouldScroll(textWidth > containerWidth)
}
}, []) // Updated dependency array
return (
<div ref={containerRef} className={`overflow-hidden ${className}`}>
<div
ref={textRef}
className={`whitespace-nowrap inline-block ${shouldScroll ? "animate-marquee hover:pause" : ""}`}
>
{shouldScroll ? (
<>
<span>{text}</span>
<span className="mx-4">&bull;</span>
<span>{text}</span>
<span className="mx-4">&bull;</span>
<span>{text}</span>
</>
) : (
text
)}
</div>
</div>
)
}
export default ScrollTxt