diff --git a/.gitea/workflows/dev-deploy.yml b/.gitea/workflows/dev-deploy.yml index 068053d..2f34bdc 100644 --- a/.gitea/workflows/dev-deploy.yml +++ b/.gitea/workflows/dev-deploy.yml @@ -70,10 +70,15 @@ jobs: echo "๐Ÿ”„ Removing old images to force re-pull..." docker rmi postgres:15-alpine redis:7-alpine 2>/dev/null || true + # Ensure networks exist before compose starts (network is external) + echo "๐ŸŒ Ensuring networks exist..." + docker network create portfolio_dev 2>/dev/null || true + docker network create proxy 2>/dev/null || true + # Pull images with correct architecture (Docker will auto-detect) echo "๐Ÿ“ฅ Pulling images for current architecture..." docker compose -f $COMPOSE_FILE pull postgres redis - + # Start containers echo "๐Ÿ“ฆ Starting PostgreSQL and Redis containers..." docker compose -f $COMPOSE_FILE up -d postgres redis @@ -192,25 +197,6 @@ jobs: fi fi - # Ensure networks exist - echo "๐ŸŒ Checking for networks..." - if ! docker network inspect proxy >/dev/null 2>&1; then - echo "โš ๏ธ Proxy network not found, creating it..." - docker network create proxy 2>/dev/null || echo "Network might already exist or creation failed" - else - echo "โœ… Proxy network exists" - fi - - if ! docker network inspect portfolio_dev >/dev/null 2>&1; then - echo "โš ๏ธ Portfolio dev network not found, creating it..." - docker network create portfolio_dev 2>/dev/null || echo "Network might already exist or creation failed" - else - echo "โœ… Portfolio dev network exists" - fi - - # Connect proxy network to portfolio_dev network if needed - # (This allows the app to access both proxy and DB/Redis) - # Start new container with updated image echo "๐Ÿ†• Starting new dev container..." docker run -d \ diff --git a/app/_ui/HomePage.tsx b/app/_ui/HomePage.tsx index 4ea418b..6a95652 100644 --- a/app/_ui/HomePage.tsx +++ b/app/_ui/HomePage.tsx @@ -39,12 +39,12 @@ export default function HomePage() { />
{/* Spacer to prevent navbar overlap */} - +
{/* Wavy Separator 1 - Hero to About */} -
+
{/* Wavy Separator 2 - About to Projects */} -
+
{/* Wavy Separator 3 - Projects to Contact */} -
+
{ }; return ( -
+
- -
+ +
{/* 1. Large Bio Text */} -
-

+
+

{t("title")}.

-
+
{isLoading ? (
@@ -105,10 +105,10 @@ const About = () => {

{t("p1")} {t("p2")}

)}
-
-
-

{t("funFactTitle")}

- {isLoading ? :

{t("funFactBody")}

} +
+
+

{t("funFactTitle")}

+ {isLoading ? :

{t("funFactBody")}

}
@@ -120,10 +120,10 @@ const About = () => { whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: 0.1 }} - className="md:col-span-4 bg-stone-900 rounded-[3rem] p-10 border border-stone-800 shadow-2xl text-white overflow-hidden relative flex flex-col" + className="md:col-span-4 bg-stone-900 rounded-2xl sm:rounded-[2.5rem] md:rounded-[3rem] p-6 sm:p-8 md:p-10 border border-stone-800 shadow-2xl text-white overflow-hidden relative flex flex-col" >
-

+

Status

@@ -137,11 +137,11 @@ const About = () => { whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: 0.2 }} - className="md:col-span-12 lg:col-span-4 bg-stone-50 dark:bg-stone-900 rounded-[3rem] p-10 border border-stone-200/60 dark:border-stone-800/60 flex flex-col shadow-sm" + className="md:col-span-12 lg:col-span-4 bg-stone-50 dark:bg-stone-900 rounded-2xl sm:rounded-[2.5rem] md:rounded-[3rem] p-6 sm:p-8 md:p-10 border border-stone-200/60 dark:border-stone-800/60 flex flex-col shadow-sm" > -
- -

AI Assistant

+
+ +

AI Assistant

@@ -154,9 +154,9 @@ const About = () => { whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: 0.3 }} - className="md:col-span-12 lg:col-span-8 bg-white dark:bg-stone-900 rounded-[3rem] p-10 md:p-16 border border-stone-200/60 dark:border-stone-800/60 shadow-sm" + className="md:col-span-12 lg:col-span-8 bg-white dark:bg-stone-900 rounded-2xl sm:rounded-[2.5rem] md:rounded-[3rem] p-6 sm:p-8 md:p-16 border border-stone-200/60 dark:border-stone-800/60 shadow-sm" > -
+
{isLoading ? ( Array.from({ length: 4 }).map((_, i) => (
@@ -186,18 +186,18 @@ const About = () => { {/* 5. Library, Gear & Snippets */} -
+
{/* Library - Larger Span */} -
-
-

+
+

Library

@@ -211,20 +211,20 @@ const About = () => {
-
+
{/* My Gear (Uses) */} -
-

+

My Gear

-
+

Main

MacBook M4 Pro

@@ -246,15 +246,15 @@ const About = () => {
-
-

+

Snippets

@@ -291,20 +291,20 @@ const About = () => { transition={{ delay: 0.5 }} className="md:col-span-12" > -
-
+
+
{isLoading ? ( Array.from({ length: 8 }).map((_, i) => ) ) : ( hobbies.map((hobby) => { const Icon = iconMap[hobby.icon] || Lightbulb; return ( -
-
- -

{hobby.title}

+
+
+ +

{hobby.title}

-

+

{hobby.description}

@@ -312,9 +312,9 @@ const About = () => { }) )}
-
-

{t("hobbiesTitle")}

-

{locale === 'de' ? 'Neugier รผber die Softwareentwicklung hinaus.' : 'Curiosity beyond software engineering.'}

+
+

{t("hobbiesTitle")}

+

{locale === 'de' ? 'Neugier รผber die Softwareentwicklung hinaus.' : 'Curiosity beyond software engineering.'}

@@ -337,13 +337,13 @@ const About = () => { initial={{ opacity: 0, scale: 0.9, y: 20 }} animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.9, y: 20 }} - className="relative w-full max-w-3xl bg-white dark:bg-stone-900 rounded-[2.5rem] shadow-2xl border border-stone-200 dark:border-stone-800 overflow-hidden flex flex-col max-h-[90vh]" + className="relative w-full max-w-3xl bg-white dark:bg-stone-900 rounded-2xl sm:rounded-[2.5rem] shadow-2xl border border-stone-200 dark:border-stone-800 overflow-hidden flex flex-col max-h-[90vh]" > -
-
+
+
-

{selectedSnippet.category}

-

{selectedSnippet.title}

+

{selectedSnippet.category}

+

{selectedSnippet.title}

-

+

{selectedSnippet.description}

-
-
-
+                  
                     {selectedSnippet.code}
                   
diff --git a/app/components/ActivityFeed.tsx b/app/components/ActivityFeed.tsx index 1639123..2256a77 100644 --- a/app/components/ActivityFeed.tsx +++ b/app/components/ActivityFeed.tsx @@ -115,7 +115,7 @@ export default function ActivityFeed({
-
+
-

+

“{allQuotes[quoteIndex].content}”

diff --git a/app/components/BentoChat.tsx b/app/components/BentoChat.tsx index c82aeb7..46c81f3 100644 --- a/app/components/BentoChat.tsx +++ b/app/components/BentoChat.tsx @@ -22,7 +22,7 @@ export default function BentoChat() { const [inputValue, setInputValue] = useState(""); const [isLoading, setIsLoading] = useState(false); const [conversationId, setConversationId] = useState("default"); - const scrollRef = useRef(null); + const containerRef = useRef(null); useEffect(() => { try { @@ -44,8 +44,13 @@ export default function BentoChat() { }, []); useEffect(() => { - if (messages.length > 0) localStorage.setItem("chatMessages", JSON.stringify(messages)); - scrollRef.current?.scrollIntoView({ behavior: "smooth" }); + if (messages.length > 0) { + localStorage.setItem("chatMessages", JSON.stringify(messages)); + } + + if (containerRef.current) { + containerRef.current.scrollTop = containerRef.current.scrollHeight; + } }, [messages]); const handleSend = async () => { @@ -73,7 +78,10 @@ export default function BentoChat() { return (

-
+
{messages.map((m) => (
@@ -86,7 +94,6 @@ export default function BentoChat() {
)} -
diff --git a/app/components/Contact.tsx b/app/components/Contact.tsx index 8f798cb..4ed4699 100644 --- a/app/components/Contact.tsx +++ b/app/components/Contact.tsx @@ -155,26 +155,26 @@ const Contact = () => { return (
-
- +
+ {/* Header Card */}
-

+

{t("title")}.

{cmsDoc ? ( - + ) : ( -

+

{t("subtitle")}

)} @@ -187,11 +187,11 @@ const Contact = () => { whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: 0.1 }} - className="md:col-span-12 lg:col-span-4 flex flex-col gap-6" + className="md:col-span-12 lg:col-span-4 flex flex-col gap-4 sm:gap-6" > -
+
-
+

Connect

@@ -199,12 +199,12 @@ const Contact = () => {
-
+
{/* Email */}
Email - contact@dk0.dev + contact@dk0.dev
@@ -217,7 +217,7 @@ const Contact = () => {
Code - GitHub + GitHub
-
+

Location

@@ -255,14 +255,14 @@ const Contact = () => { whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: 0.2 }} - className="md:col-span-12 lg:col-span-8 bg-white dark:bg-stone-900 rounded-[3rem] p-10 md:p-12 border border-stone-200/60 dark:border-stone-800/60 shadow-sm" + className="md:col-span-12 lg:col-span-8 bg-white dark:bg-stone-900 rounded-2xl sm:rounded-[2.5rem] md:rounded-[3rem] p-6 sm:p-8 md:p-12 border border-stone-200/60 dark:border-stone-800/60 shadow-sm" > -

+

{tForm("title")}

-
-
+ +
@@ -292,7 +292,7 @@ const Contact = () => { onChange={handleChange} onBlur={handleBlur} required - className="w-full px-6 py-4 bg-stone-50 dark:bg-stone-800 border border-stone-100 dark:border-stone-700 rounded-2xl text-stone-900 dark:text-stone-50 focus:outline-none focus:ring-2 focus:ring-liquid-mint/50 transition-all font-medium" + className="w-full px-4 sm:px-6 py-3 sm:py-4 bg-stone-50 dark:bg-stone-800 border border-stone-100 dark:border-stone-700 rounded-xl sm:rounded-2xl text-sm sm:text-base text-stone-900 dark:text-stone-50 focus:outline-none focus:ring-2 focus:ring-liquid-mint/50 transition-all font-medium" placeholder={tForm("placeholders.email")} />
@@ -310,7 +310,7 @@ const Contact = () => { onChange={handleChange} onBlur={handleBlur} required - className="w-full px-6 py-4 bg-stone-50 dark:bg-stone-800 border border-stone-100 dark:border-stone-700 rounded-2xl text-stone-900 dark:text-stone-50 focus:outline-none focus:ring-2 focus:ring-liquid-mint/50 transition-all font-medium" + className="w-full px-4 sm:px-6 py-3 sm:py-4 bg-stone-50 dark:bg-stone-800 border border-stone-100 dark:border-stone-700 rounded-xl sm:rounded-2xl text-sm sm:text-base text-stone-900 dark:text-stone-50 focus:outline-none focus:ring-2 focus:ring-liquid-mint/50 transition-all font-medium" placeholder={tForm("placeholders.subject")} />
@@ -327,7 +327,7 @@ const Contact = () => { onBlur={handleBlur} required rows={5} - className="w-full px-6 py-4 bg-stone-50 dark:bg-stone-800 border border-stone-100 dark:border-stone-700 rounded-2xl text-stone-900 dark:text-stone-50 focus:outline-none focus:ring-2 focus:ring-liquid-mint/50 transition-all font-medium resize-none" + className="w-full px-4 sm:px-6 py-3 sm:py-4 bg-stone-50 dark:bg-stone-800 border border-stone-100 dark:border-stone-700 rounded-xl sm:rounded-2xl text-sm sm:text-base text-stone-900 dark:text-stone-50 focus:outline-none focus:ring-2 focus:ring-liquid-mint/50 transition-all font-medium resize-none" placeholder={tForm("placeholders.message")} />
@@ -337,7 +337,7 @@ const Contact = () => { disabled={isSubmitting} whileHover={{ scale: 1.01 }} whileTap={{ scale: 0.99 }} - className="w-full py-5 bg-stone-900 dark:bg-stone-50 text-white dark:text-stone-900 rounded-2xl font-black text-xs uppercase tracking-[0.3em] flex items-center justify-center gap-3 shadow-xl hover:shadow-2xl transition-all disabled:opacity-50" + className="w-full py-4 sm:py-5 bg-stone-900 dark:bg-stone-50 text-white dark:text-stone-900 rounded-xl sm:rounded-2xl font-black text-xs uppercase tracking-[0.2em] sm:tracking-[0.3em] flex items-center justify-center gap-3 shadow-xl hover:shadow-2xl transition-all disabled:opacity-50" > {isSubmitting ? (
diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx index 25c6a2d..b3310b6 100644 --- a/app/components/Footer.tsx +++ b/app/components/Footer.tsx @@ -15,10 +15,10 @@ const Footer = () => { }; return ( -