2 Commits

Author SHA1 Message Date
8d59b2a383 Bob AI: Add background music player to the website. Implement an aud 2026-02-24 17:58:28 +02:00
4953f53f6b Merge version_1 into main
Merge version_1 into main
2026-02-24 15:46:14 +00:00

View File

@@ -1,3 +1,5 @@
"use client";
import type { Metadata } from "next";
import { Archivo } from "next/font/google";
import "./globals.css";
@@ -8,10 +10,85 @@ const archivo = Archivo({
variable: "--font-archivo", subsets: ["latin"],
});
import { useState, useEffect } from 'react';
export const metadata: Metadata = {
title: "Sip Into Serenity | Premium Coffee Shop", description: "Discover artisanal coffee blends and cozy ambiance at our premium coffee shop. Experience the perfect cup in a warm, inviting space designed for coffee lovers.", keywords: ["coffee shop", "premium coffee", "artisanal coffee", "cozy cafe", "coffee blends", "local coffee", "coffee experience", "relaxing atmosphere"]
title: "Sip Into Serenity | Premium Coffee Shop",
description: "Discover artisanal coffee blends and cozy ambiance at our premium coffee shop. Experience the perfect cup in a warm, inviting space designed for coffee lovers.",
keywords: ["coffee shop", "premium coffee", "artisanal coffee", "cozy cafe", "coffee blends", "local coffee", "coffee experience", "relaxing atmosphere"]
};
function AudioPlayer() {
const [isPlaying, setIsPlaying] = useState(false);
const [volume, setVolume] = useState(0.5);
const [audioRef, setAudioRef] = useState<HTMLAudioElement | null>(null);
useEffect(() => {
const audio = new Audio('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3');
audio.loop = true;
audio.volume = volume;
setAudioRef(audio);
return () => {
audio.pause();
audio.src = '';
};
}, []);
useEffect(() => {
if (audioRef) {
audioRef.volume = volume;
}
}, [volume, audioRef]);
const togglePlay = () => {
if (audioRef) {
if (isPlaying) {
audioRef.pause();
} else {
audioRef.play().catch(() => {
console.log('Autoplay prevented');
});
}
setIsPlaying(!isPlaying);
}
};
return (
<div className="fixed bottom-6 right-6 z-50 bg-white rounded-full shadow-lg p-4 flex items-center gap-3 backdrop-blur-sm bg-opacity-95">
<button
onClick={togglePlay}
className="w-10 h-10 rounded-full bg-amber-600 hover:bg-amber-700 text-white flex items-center justify-center transition-colors"
aria-label={isPlaying ? 'Pause music' : 'Play music'}
>
{isPlaying ? (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M5.75 3a.75.75 0 00-.75.75v12.5c0 .414.336.75.75.75h1.5a.75.75 0 00.75-.75V3.75A.75.75 0 007.25 3h-1.5zm5.5 0a.75.75 0 00-.75.75v12.5c0 .414.336.75.75.75h1.5a.75.75 0 00.75-.75V3.75a.75.75 0 00-.75-.75h-1.5z" />
</svg>
) : (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M6.3 2.841A1.5 1.5 0 004 4.11V15.89a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z" />
</svg>
)}
</button>
<div className="flex items-center gap-2">
<svg className="w-4 h-4 text-amber-600" fill="currentColor" viewBox="0 0 20 20">
<path d="M9.383 3.076A1 1 0 0110 2c.894 0 1.71.51 2.119 1.324l.297.604a1 1 0 00.82.578l.658.044c.921.062 1.579.585 1.579 1.631 0 .89-.5 1.747-1.255 2.206l-.507.338a1 1 0 00-.278 1.268l.192.591c.247.757.245 1.645-.122 2.253-.365.607-.949.875-1.708.875-.632 0-1.227-.21-1.566-.731l-.199-.298a1 1 0 00-.82-.578l-.658-.044c-.921-.062-1.579-.585-1.579-1.631 0-.89.5-1.747 1.255-2.206l.507-.338a1 1 0 00.278-1.268l-.192-.591c-.247-.757-.245-1.645.122-2.253.365-.607.949-.875 1.708-.875.632 0 1.227.21 1.566.731l.199.298z" />
</svg>
<input
type="range"
min="0"
max="100"
value={volume * 100}
onChange={(e) => setVolume(Number(e.target.value) / 100)}
className="w-20 h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer accent-amber-600"
aria-label="Volume control"
/>
</div>
</div>
);
}
export default function RootLayout({
children,
}: Readonly<{
@@ -24,6 +101,7 @@ export default function RootLayout({
className={`${archivo.variable} antialiased`}
>
<Tag />
<AudioPlayer />
{children}
<script