diff --git a/src/assets/styles/MemeGrid.css b/src/assets/styles/MemeGrid.css
index 2ee82a5..7fbd931 100644
--- a/src/assets/styles/MemeGrid.css
+++ b/src/assets/styles/MemeGrid.css
@@ -1,3 +1,26 @@
+.meme-dialog-tooltip-wrapper {
+ position: relative;
+}
+
+.meme-dialog-tooltip {
+ position: absolute;
+ bottom: -1.5rem;
+ left: 50%;
+ transform: translateX(-50%);
+ background: rgba(0, 0, 0, 0.8);
+ color: #fff;
+ padding: 0.15rem 0.4rem;
+ border-radius: 4px;
+ font-size: 0.7rem;
+ white-space: nowrap;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.2s ease;
+}
+
+.meme-dialog-tooltip-wrapper:hover .meme-dialog-tooltip {
+ opacity: 1;
+}
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -22,3 +45,59 @@
text-align: center;
margin: 2rem 0;
}
+
+.meme-dialog-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.meme-dialog-actions {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.meme-dialog-actions button {
+ padding: 0.2rem 0.5rem;
+ border-radius: 6px;
+ border: 1px solid rgba(255, 255, 255, 0.35);
+ background: transparent;
+ color: inherit;
+ cursor: pointer;
+ font-size: 0.85rem;
+}
+
+.meme-dialog-body {
+ position: relative;
+}
+
+.meme-dialog-nav {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ border: none;
+ background: rgba(0, 0, 0, 0.35);
+ color: #fff;
+ width: 2.25rem;
+ height: 2.25rem;
+ border-radius: 999px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+}
+
+.meme-dialog-nav:disabled {
+ opacity: 0.3;
+ cursor: not-allowed;
+}
+
+.meme-dialog-nav-prev {
+ left: 0.5rem;
+}
+
+.meme-dialog-nav-next {
+ right: 0.5rem;
+}
diff --git a/src/assets/styles/global.css b/src/assets/styles/global.css
index f560aa9..da01f36 100644
--- a/src/assets/styles/global.css
+++ b/src/assets/styles/global.css
@@ -232,8 +232,35 @@ Custom
}
#lyric-search-input {
- margin-right: 1.5%;
- padding-bottom: 1rem;
+ margin: 0;
+ padding: 0;
+}
+
+.lyric-search-input-wrapper {
+ position: relative;
+ width: 100%;
+ max-width: 900px;
+}
+
+.lyric-search-input-wrapper .p-autocomplete {
+ width: 100%;
+}
+
+.lyric-search-input-wrapper .p-autocomplete-input {
+ padding-right: 2.5rem;
+}
+
+.input-status-icon {
+ position: absolute;
+ right: 0.85rem;
+ top: 0;
+ bottom: 0;
+ transform: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ pointer-events: none;
+ transition: opacity 0.2s ease, color 0.2s ease;
}
#lyrics-info {
@@ -253,6 +280,54 @@ Custom
transition: background 0.3s;
}
+.lyrics-toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+ margin-bottom: 1rem;
+}
+
+.lyrics-title {
+ font-weight: 600;
+ flex: 1;
+ text-align: left;
+}
+
+.lyrics-actions {
+ display: flex;
+ align-items: center;
+ gap: 0.35rem;
+}
+
+.text-size-buttons {
+ display: flex;
+ border: 1px solid rgba(79, 70, 229, 0.25);
+ border-radius: 999px;
+ overflow: hidden;
+ background: rgba(79, 70, 229, 0.06);
+}
+
+.text-size-btn {
+ background: transparent;
+ border: none;
+ color: inherit;
+ padding: 0.15rem 0.5rem;
+ font-size: 0.85rem;
+ cursor: pointer;
+ transition: background 0.2s, color 0.2s;
+}
+
+.text-size-btn.text-size-large {
+ font-size: 0.95rem;
+}
+
+.text-size-btn.active {
+ background: rgba(79, 70, 229, 0.15);
+ font-weight: 600;
+}
+
.lyrics-content {
line-height: 2.0;
font-family: 'Inter', sans-serif;
@@ -260,6 +335,22 @@ Custom
white-space: pre-wrap;
}
+.lyrics-content-large {
+ font-size: 1.08rem;
+ line-height: 1.85;
+}
+
+.lyrics-action-button {
+ color: inherit;
+ border: 1px solid transparent;
+ padding: 0.25rem;
+}
+
+.lyrics-action-button:hover {
+ border-color: rgba(79, 70, 229, 0.2);
+ background: rgba(79, 70, 229, 0.08);
+}
+
.lyrics-card-dark {
background-color: oklch(from rgba(18, 18, 18, 2.0) calc(l + 0.05) c h);
}
@@ -298,6 +389,26 @@ Custom
outline: none;
}
+.lyric-search-input.has-error .p-autocomplete-input {
+ border-color: #f87171;
+}
+
+.lyric-search-input.has-ready .p-autocomplete-input {
+ border-color: #34d399;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
.d-dark > * {
background-color: rgba(35, 35, 35, 0.9);
color: #ffffff;
diff --git a/src/components/LyricSearch.jsx b/src/components/LyricSearch.jsx
index c383ef9..7f8d781 100644
--- a/src/components/LyricSearch.jsx
+++ b/src/components/LyricSearch.jsx
@@ -8,11 +8,15 @@ import React, {
useCallback,
} from "react";
import { toast } from 'react-toastify';
-import Alert from '@mui/joy/Alert';
import Box from '@mui/joy/Box';
import Button from "@mui/joy/Button";
+import IconButton from "@mui/joy/IconButton";
import Checkbox from "@mui/joy/Checkbox";
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
+import LinkIcon from '@mui/icons-material/Link';
+import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';
+import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
+import RemoveRoundedIcon from '@mui/icons-material/RemoveRounded';
import { AutoComplete } from 'primereact/autocomplete';
import { API_URL } from '../config';
@@ -29,12 +33,11 @@ export default function LyricSearch() {
]*>/gi, "") + .replace(/<\/p>/gi, "\n\n"); + const plainText = temp.textContent || temp.innerText || ""; + + try { + await navigator.clipboard.writeText(plainText); + toast.success("Lyrics copied to clipboard", { autoClose: 1500 }); + } catch (err) { + toast.error("Unable to copy lyrics"); + console.error("Copy failed", err); + } + }, [lyricsResult]); + + const handleCopyLink = useCallback(async () => { + if (!lyricsResult) { + return; + } + try { + const url = new URL(window.location.href); + const hash = `#${encodeURIComponent(lyricsResult.artist)}/${encodeURIComponent(lyricsResult.song)}`; + url.hash = hash; + await navigator.clipboard.writeText(url.toString()); + toast.success("Lyric link copied", { autoClose: 1500 }); + } catch (err) { + toast.error("Unable to copy link"); + console.error("Link copy failed", err); + } + }, [lyricsResult]); + + const statusTitle = statusLabels[inputStatus]; + const StatusIcon = statusIcons[inputStatus] || RemoveRoundedIcon; + return (