auto-claude: subtask-5-1 - Create ChatMessage component for individual messages

Created ChatMessage.tsx component with:
- ChatMessage component for displaying user/assistant messages
- Styled differently based on role (user right-aligned, assistant left-aligned)
- Framer Motion animations for smooth appearance
- Streaming indicator for real-time response display
- TypingIndicator component for loading state
- WelcomeMessage component for empty chat state
- Timestamp formatting utility
- Responsive design following existing Tailwind patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-25 00:58:13 +01:00
co-authored by Claude Opus 4.5
parent 858d53fd2d
commit a1cfc33dea
9 changed files with 862 additions and 0 deletions
+227
View File
@@ -0,0 +1,227 @@
{
"base_commands": [
".",
"[",
"[[",
"ag",
"awk",
"basename",
"bash",
"bc",
"break",
"cat",
"cd",
"chmod",
"clear",
"cmp",
"column",
"comm",
"command",
"continue",
"cp",
"curl",
"cut",
"date",
"df",
"diff",
"dig",
"dirname",
"du",
"echo",
"egrep",
"env",
"eval",
"exec",
"exit",
"expand",
"export",
"expr",
"false",
"fd",
"fgrep",
"file",
"find",
"fmt",
"fold",
"gawk",
"gh",
"git",
"grep",
"gunzip",
"gzip",
"head",
"help",
"host",
"iconv",
"id",
"jobs",
"join",
"jq",
"kill",
"killall",
"less",
"let",
"ln",
"ls",
"lsof",
"man",
"mkdir",
"mktemp",
"more",
"mv",
"nl",
"paste",
"pgrep",
"ping",
"pkill",
"popd",
"printenv",
"printf",
"ps",
"pushd",
"pwd",
"read",
"readlink",
"realpath",
"reset",
"return",
"rev",
"rg",
"rm",
"rmdir",
"sed",
"seq",
"set",
"sh",
"shuf",
"sleep",
"sort",
"source",
"split",
"stat",
"tail",
"tar",
"tee",
"test",
"time",
"timeout",
"touch",
"tr",
"tree",
"true",
"type",
"uname",
"unexpand",
"uniq",
"unset",
"unzip",
"watch",
"wc",
"wget",
"whereis",
"which",
"whoami",
"xargs",
"yes",
"yq",
"zip",
"zsh"
],
"stack_commands": [
"ar",
"clang",
"clang++",
"cmake",
"composer",
"dive",
"docker",
"docker-buildx",
"docker-compose",
"dockerfile",
"eslint",
"g++",
"gcc",
"ipython",
"jupyter",
"ld",
"make",
"meson",
"next",
"ninja",
"nm",
"node",
"notebook",
"npm",
"npx",
"objdump",
"pdb",
"php",
"pip",
"pip3",
"pipx",
"pnpm",
"pnpx",
"pudb",
"python",
"python3",
"react-scripts",
"strip",
"ts-node",
"tsc",
"tsx",
"vitest"
],
"script_commands": [
"bun",
"npm",
"pnpm",
"yarn"
],
"custom_commands": [],
"detected_stack": {
"languages": [
"python",
"javascript",
"typescript",
"php",
"c"
],
"package_managers": [
"pnpm"
],
"frameworks": [
"nextjs",
"react",
"vitest",
"eslint"
],
"databases": [],
"infrastructure": [
"docker"
],
"cloud_providers": [],
"code_quality_tools": [],
"version_managers": []
},
"custom_scripts": {
"npm_scripts": [
"dev",
"build",
"start",
"lint",
"build:images",
"generate:images",
"generate:images:dry",
"test",
"test:coverage"
],
"make_targets": [],
"poetry_scripts": [],
"cargo_aliases": [],
"shell_scripts": []
},
"project_dir": "C:\\Users\\damja\\WebstormProjects\\Portfolio",
"created_at": "2026-01-22T15:28:38.237190",
"project_hash": "c4ad399e16be367eb4e6b076fe1d9ee3",
"inherited_from": "C:\\Users\\damja\\WebstormProjects\\Portfolio"
}
+25
View File
@@ -0,0 +1,25 @@
{
"active": true,
"spec": "002-recherchiere-weitere-seo-optimierungen-an-der-webs",
"state": "building",
"subtasks": {
"completed": 15,
"total": 27,
"in_progress": 1,
"failed": 0
},
"phase": {
"current": "Chatbot Frontend",
"id": null,
"total": 3
},
"workers": {
"active": 0,
"max": 1
},
"session": {
"number": 15,
"started_at": "2026-01-25T00:03:26.706142"
},
"last_update": "2026-01-25T00:57:00.939315"
}
+39
View File
@@ -0,0 +1,39 @@
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true
},
"permissions": {
"defaultMode": "acceptEdits",
"allow": [
"Read(./**)",
"Write(./**)",
"Edit(./**)",
"Glob(./**)",
"Grep(./**)",
"Read(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\002-recherchiere-weitere-seo-optimierungen-an-der-webs/**)",
"Write(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\002-recherchiere-weitere-seo-optimierungen-an-der-webs/**)",
"Edit(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\002-recherchiere-weitere-seo-optimierungen-an-der-webs/**)",
"Glob(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\002-recherchiere-weitere-seo-optimierungen-an-der-webs/**)",
"Grep(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\002-recherchiere-weitere-seo-optimierungen-an-der-webs/**)",
"Read(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\002-recherchiere-weitere-seo-optimierungen-an-der-webs\\.auto-claude\\specs\\002-recherchiere-weitere-seo-optimierungen-an-der-webs/**)",
"Write(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\002-recherchiere-weitere-seo-optimierungen-an-der-webs\\.auto-claude\\specs\\002-recherchiere-weitere-seo-optimierungen-an-der-webs/**)",
"Edit(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\002-recherchiere-weitere-seo-optimierungen-an-der-webs\\.auto-claude\\specs\\002-recherchiere-weitere-seo-optimierungen-an-der-webs/**)",
"Read(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)",
"Write(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)",
"Edit(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)",
"Glob(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)",
"Grep(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)",
"Bash(*)",
"WebFetch(*)",
"WebSearch(*)",
"mcp__context7__resolve-library-id(*)",
"mcp__context7__get-library-docs(*)",
"mcp__graphiti-memory__search_nodes(*)",
"mcp__graphiti-memory__search_facts(*)",
"mcp__graphiti-memory__add_episode(*)",
"mcp__graphiti-memory__get_episodes(*)",
"mcp__graphiti-memory__get_entity_edge(*)"
]
}
}
View File
File diff suppressed because one or more lines are too long
+171
View File
@@ -0,0 +1,171 @@
'use client';
import { motion } from 'framer-motion';
import { User, Bot } from 'lucide-react';
export type ChatRole = 'user' | 'assistant' | 'system';
export interface ChatMessageData {
id?: string;
role: ChatRole;
content: string;
created_at?: string;
isStreaming?: boolean;
}
interface ChatMessageProps {
message: ChatMessageData;
index?: number;
}
/**
* Formats a timestamp for display
*/
function formatTime(timestamp?: string): string {
if (!timestamp) return '';
try {
const date = new Date(timestamp);
return date.toLocaleTimeString(undefined, {
hour: '2-digit',
minute: '2-digit',
});
} catch {
return '';
}
}
/**
* ChatMessage component for displaying individual chat messages
* Styled differently based on message role (user vs assistant)
*/
export function ChatMessage({ message, index = 0 }: ChatMessageProps) {
const { role, content, created_at, isStreaming } = message;
// Don't render system messages
if (role === 'system') {
return null;
}
const isUser = role === 'user';
const formattedTime = formatTime(created_at);
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.05, duration: 0.2 }}
className={`flex gap-3 ${isUser ? 'flex-row-reverse' : 'flex-row'}`}
>
{/* Avatar */}
<div
className={`flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center ${
isUser
? 'bg-zinc-600/50'
: 'bg-zinc-800/50 border border-zinc-700'
}`}
>
{isUser ? (
<User className="w-4 h-4 text-zinc-300" />
) : (
<Bot className="w-4 h-4 text-zinc-400" />
)}
</div>
{/* Message Content */}
<div
className={`flex flex-col max-w-[80%] ${
isUser ? 'items-end' : 'items-start'
}`}
>
<div
className={`px-4 py-3 rounded-2xl ${
isUser
? 'bg-zinc-600/50 text-white rounded-tr-sm'
: 'bg-zinc-800/50 border border-zinc-800 text-zinc-200 rounded-tl-sm'
}`}
>
<p className="text-sm whitespace-pre-wrap break-words leading-relaxed">
{content}
{isStreaming && (
<span className="inline-block w-1.5 h-4 ml-1 bg-zinc-400 animate-pulse rounded-sm" />
)}
</p>
</div>
{/* Timestamp */}
{formattedTime && !isStreaming && (
<span className="text-xs text-zinc-500 mt-1 px-1">
{formattedTime}
</span>
)}
</div>
</motion.div>
);
}
/**
* Typing indicator shown when assistant is generating a response
*/
export function TypingIndicator() {
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="flex gap-3"
>
{/* Avatar */}
<div className="flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-zinc-800/50 border border-zinc-700">
<Bot className="w-4 h-4 text-zinc-400" />
</div>
{/* Typing dots */}
<div className="px-4 py-3 rounded-2xl rounded-tl-sm bg-zinc-800/50 border border-zinc-800">
<div className="flex gap-1.5">
{[0, 1, 2].map((i) => (
<motion.span
key={i}
className="w-2 h-2 bg-zinc-500 rounded-full"
animate={{
opacity: [0.4, 1, 0.4],
scale: [0.8, 1, 0.8],
}}
transition={{
duration: 1,
repeat: Infinity,
delay: i * 0.2,
}}
/>
))}
</div>
</div>
</motion.div>
);
}
/**
* Welcome message component for empty chat state
*/
export function WelcomeMessage() {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="flex flex-col items-center justify-center py-8 px-4 text-center"
>
<div className="w-12 h-12 rounded-full bg-zinc-800/50 border border-zinc-700 flex items-center justify-center mb-4">
<Bot className="w-6 h-6 text-zinc-400" />
</div>
<h3 className="text-lg font-medium text-white mb-2">
Hi, I&apos;m Damjan&apos;s AI Assistant
</h3>
<p className="text-sm text-zinc-400 max-w-xs">
I can help you learn about Damjan&apos;s skills, services, and experience.
Feel free to ask me anything!
</p>
</motion.div>
);
}
export default ChatMessage;
+14
View File
@@ -0,0 +1,14 @@
{
"status": "failed",
"failedTests": [
"b15d6361b2fc38288977-cc6cca7f3eb8cd82590c",
"b15d6361b2fc38288977-5e1c665e664697d61b48",
"b15d6361b2fc38288977-61ae5d34d2c913db9b2a",
"b15d6361b2fc38288977-284f9ff9499f4f985752",
"b15d6361b2fc38288977-554360e215668a72f3c5",
"b15d6361b2fc38288977-d4fe6d5b85e4848564f5",
"b15d6361b2fc38288977-e6c0964e023326d4a6c7",
"b15d6361b2fc38288977-252e2d4a1453fc1d2a15",
"b15d6361b2fc38288977-e2864a9c91ff6b5e2798"
]
}
@@ -0,0 +1,301 @@
# Page snapshot
```yaml
- generic [active] [ref=e1]:
- generic [ref=e2]:
- generic:
- img
- navigation "Main navigation" [ref=e5]:
- generic [ref=e7]:
- link "Damjan Savić Logo" [ref=e8] [cursor=pointer]:
- /url: /de
- img "Damjan Savić Logo" [ref=e9]
- generic [ref=e10]:
- link "Startseite" [ref=e11] [cursor=pointer]:
- /url: /de
- img [ref=e12]
- generic [ref=e15]: Startseite
- link "Über mich" [ref=e16] [cursor=pointer]:
- /url: /de/about
- img [ref=e17]
- generic [ref=e20]: Über mich
- link "Leistungen" [ref=e21] [cursor=pointer]:
- /url: /de/leistungen
- img [ref=e22]
- generic [ref=e33]: Leistungen
- link "Portfolio" [ref=e34] [cursor=pointer]:
- /url: /de/portfolio
- img [ref=e35]
- generic [ref=e38]: Portfolio
- link "Blog" [ref=e39] [cursor=pointer]:
- /url: /de/blog
- img [ref=e40]
- generic [ref=e42]: Blog
- link "Kontakt" [ref=e43] [cursor=pointer]:
- /url: /de/contact
- img [ref=e44]
- generic [ref=e46]: Kontakt
- generic [ref=e48]:
- button "Select language" [ref=e49] [cursor=pointer]:
- img [ref=e50]
- generic [ref=e53]: Deutsch
- listbox "Languages":
- generic:
- option "English"
- option "Deutsch" [selected]
- option "Srpski"
- main [ref=e54]:
- generic [ref=e55]:
- img "Damjan Savić" [ref=e57]
- generic [ref=e59]:
- link "LinkedIn" [ref=e60] [cursor=pointer]:
- /url: https://www.linkedin.com/in/damjan-savi%C4%87-720288127/
- img [ref=e61]
- link "GitHub" [ref=e65] [cursor=pointer]:
- /url: https://github.com/damjan1996
- img [ref=e66]
- generic [ref=e70]:
- paragraph [ref=e71]: FULLSTACK DEVELOPER
- heading "DAMJAN SAVIC|" [level=1] [ref=e72]
- paragraph [ref=e73]: Entwicklung von KI-Agenten und Automatisierungslösungen. Von Voice-AI-Plattformen bis zu autonomen Web-Agenten.
- generic [ref=e74]:
- link "KONTAKT AUFNEHMEN" [ref=e75] [cursor=pointer]:
- /url: mailto:info@damjan-savic.com
- link "PROJEKTE" [ref=e76] [cursor=pointer]:
- /url: "#projects"
- navigation [ref=e77]:
- link "ERFAHRUNG" [ref=e78] [cursor=pointer]:
- /url: "#experience"
- link "FÄHIGKEITEN" [ref=e79] [cursor=pointer]:
- /url: "#skills"
- link "ÜBER MICH" [ref=e80] [cursor=pointer]:
- /url: "#about"
- img [ref=e82]
- generic [ref=e86]:
- heading "Berufserfahrung" [level=2] [ref=e87]
- generic [ref=e89]:
- generic [ref=e90]:
- heading "Process Automation Specialist" [level=3] [ref=e91]
- heading "Everlast Consulting GmbH" [level=4] [ref=e92]
- paragraph [ref=e93]: 12/2024 - HEUTE
- list [ref=e94]:
- listitem [ref=e95]:
- generic [ref=e96]:
- generic [ref=e97]: Entwicklung von KI-Agenten mit n8n und Zapier
- listitem [ref=e98]:
- generic [ref=e99]:
- generic [ref=e100]: Aufbau von Web-Scraping-Lösungen
- listitem [ref=e101]:
- generic [ref=e102]:
- generic [ref=e103]: Migration von Power Automate zu n8n
- generic [ref=e104]:
- heading "Consultant Digital Solutions" [level=3] [ref=e105]
- heading "Ritter Digital GmbH" [level=4] [ref=e106]
- paragraph [ref=e107]: 08/2023 - 11/2024
- list [ref=e108]:
- listitem [ref=e109]:
- generic [ref=e110]:
- generic [ref=e111]: Backend-Services auf dedizierten Servern
- listitem [ref=e112]:
- generic [ref=e113]:
- generic [ref=e114]: KI-Integration via Power Automate
- listitem [ref=e115]:
- generic [ref=e116]:
- generic [ref=e117]: RFID/IoT-Lösungen mit Zebra-Hardware
- generic [ref=e118]:
- button "Previous page" [disabled] [ref=e119]:
- generic [ref=e120]:
- button "Page 1" [ref=e121] [cursor=pointer]
- button "Page 2" [ref=e123] [cursor=pointer]
- button "Next page" [ref=e125] [cursor=pointer]:
- generic [ref=e128]:
- heading "Fähigkeiten" [level=2] [ref=e129]
- generic [ref=e130]:
- generic [ref=e131]:
- generic [ref=e132]:
- generic [ref=e133]:
- generic [ref=e134]: AI & LLMs
- generic [ref=e135]: 90%
- progressbar [ref=e136]
- generic [ref=e137]:
- generic [ref=e138]:
- generic [ref=e139]: Automation
- generic [ref=e140]: 85%
- progressbar [ref=e141]
- generic [ref=e142]:
- generic [ref=e143]:
- generic [ref=e144]: Python
- generic [ref=e145]: 90%
- progressbar [ref=e146]
- generic [ref=e147]:
- generic [ref=e148]:
- generic [ref=e149]: TypeScript
- generic [ref=e150]: 85%
- progressbar [ref=e151]
- generic [ref=e152]:
- generic [ref=e153]:
- generic [ref=e154]: Datenbank
- generic [ref=e155]: 85%
- progressbar [ref=e156]
- generic [ref=e157]:
- generic [ref=e158]:
- generic [ref=e159]: DevOps
- generic [ref=e160]: 75%
- progressbar [ref=e161]
- generic [ref=e162]:
- button "AI & LLMs" [ref=e164] [cursor=pointer]:
- img [ref=e166]
- generic [ref=e169]: AI & LLMs
- button "Automation" [ref=e171] [cursor=pointer]:
- img [ref=e173]
- generic [ref=e177]: Automation
- button "Python" [ref=e179] [cursor=pointer]:
- img [ref=e181]
- generic [ref=e185]: Python
- button "TypeScript" [ref=e187] [cursor=pointer]:
- img [ref=e189]
- generic [ref=e194]: TypeScript
- button "Datenbank" [ref=e196] [cursor=pointer]:
- img [ref=e198]
- generic [ref=e202]: Datenbank
- button "DevOps" [ref=e204] [cursor=pointer]:
- img [ref=e206]
- generic [ref=e209]: DevOps
- generic [ref=e211]:
- generic [ref=e212]:
- heading "Portfolio" [level=2] [ref=e213]
- link "Alle Projekte" [ref=e214] [cursor=pointer]:
- /url: /de/portfolio
- generic [ref=e215]:
- link "AI Document Reader 2024-01 4 Technologies AI Document Reader KI-gestützte Dokumentenanalyse mit OLLAMA und Python Python OLLAMA FastAPI +1 more" [ref=e216] [cursor=pointer]:
- /url: /de/portfolio/ai-data-reader
- generic [ref=e217]:
- img "AI Document Reader" [ref=e219]
- generic [ref=e220]:
- generic [ref=e221]:
- generic [ref=e222]:
- img [ref=e223]
- generic [ref=e225]: 2024-01
- generic [ref=e226]:
- img [ref=e227]
- generic [ref=e230]: 4 Technologies
- heading "AI Document Reader" [level=3] [ref=e231]
- paragraph [ref=e232]: KI-gestützte Dokumentenanalyse mit OLLAMA und Python
- generic [ref=e233]:
- generic [ref=e234]: Python
- generic [ref=e235]: OLLAMA
- generic [ref=e236]: FastAPI
- generic [ref=e237]: +1 more
- img [ref=e239]
- link "Smart Warehouse RFID 2024-02 4 Technologies Smart Warehouse RFID RFID-basiertes Lagerverwaltungssystem mit IoT-Integration Python RFID IoT +1 more" [ref=e243] [cursor=pointer]:
- /url: /de/portfolio/smart-warehouse
- generic [ref=e244]:
- img "Smart Warehouse RFID" [ref=e246]
- generic [ref=e247]:
- generic [ref=e248]:
- generic [ref=e249]:
- img [ref=e250]
- generic [ref=e252]: 2024-02
- generic [ref=e253]:
- img [ref=e254]
- generic [ref=e257]: 4 Technologies
- heading "Smart Warehouse RFID" [level=3] [ref=e258]
- paragraph [ref=e259]: RFID-basiertes Lagerverwaltungssystem mit IoT-Integration
- generic [ref=e260]:
- generic [ref=e261]: Python
- generic [ref=e262]: RFID
- generic [ref=e263]: IoT
- generic [ref=e264]: +1 more
- img [ref=e266]
- link "Portfolio mit KI 2024-03 4 Technologies Portfolio mit KI Moderne Portfolio-Website mit KI-Integration Next.js TypeScript Tailwind +1 more" [ref=e270] [cursor=pointer]:
- /url: /de/portfolio/website-mit-ki
- generic [ref=e271]:
- img "Portfolio mit KI" [ref=e273]
- generic [ref=e274]:
- generic [ref=e275]:
- generic [ref=e276]:
- img [ref=e277]
- generic [ref=e279]: 2024-03
- generic [ref=e280]:
- img [ref=e281]
- generic [ref=e284]: 4 Technologies
- heading "Portfolio mit KI" [level=3] [ref=e285]
- paragraph [ref=e286]: Moderne Portfolio-Website mit KI-Integration
- generic [ref=e287]:
- generic [ref=e288]: Next.js
- generic [ref=e289]: TypeScript
- generic [ref=e290]: Tailwind
- generic [ref=e291]: +1 more
- img [ref=e293]
- generic [ref=e299]:
- generic [ref=e300]:
- heading "Über Damjan Savić - Senior Fullstack Entwickler & Digital Solutions Consultant" [level=2] [ref=e302]
- generic [ref=e304]:
- generic [ref=e305]:
- img [ref=e307]
- generic [ref=e310]: Köln, Deutschland
- generic [ref=e311]:
- img [ref=e313]
- generic [ref=e316]: 5+ Jahre Erfahrung
- generic [ref=e317]:
- img [ref=e319]
- generic [ref=e322]: Full-Stack Developer
- generic [ref=e323]:
- img [ref=e325]
- generic [ref=e328]: ERP Spezialist
- paragraph [ref=e330]: Damjan Savić ist AI & Automation Specialist mit Fokus auf Voice AI, autonome Agenten und Prozessautomatisierung. Mit einem M.A. in Software Development und über 7 Jahren Erfahrung in der Entwicklung digitaler Lösungen verbindet er technische Expertise mit strategischem Geschäftsverständnis.
- generic [ref=e331]:
- link "Mehr erfahren" [ref=e332] [cursor=pointer]:
- /url: /de/about
- img [ref=e333]
- text: Mehr erfahren
- link "CV herunterladen" [ref=e336] [cursor=pointer]:
- /url: /damjan_savic_cv_de.pdf
- img [ref=e337]
- text: CV herunterladen
- img "Damjan Savić" [ref=e342]
- contentinfo [ref=e343]:
- generic [ref=e345]:
- generic [ref=e346]:
- heading "Kontakt" [level=3] [ref=e347]
- generic [ref=e348]:
- link "info@damjan-savic.com" [ref=e349] [cursor=pointer]:
- /url: mailto:info@damjan-savic.com
- img [ref=e350]
- generic [ref=e353]: info@damjan-savic.com
- generic [ref=e354]:
- img [ref=e355]
- generic [ref=e358]: Köln, Deutschland
- generic [ref=e359]:
- heading "Navigation" [level=3] [ref=e360]
- navigation [ref=e361]:
- link "Portfolio" [ref=e362] [cursor=pointer]:
- /url: /de/portfolio
- link "Blog" [ref=e363] [cursor=pointer]:
- /url: /de/blog
- link "Über mich" [ref=e364] [cursor=pointer]:
- /url: /de/about
- link "Kontakt" [ref=e365] [cursor=pointer]:
- /url: /de/contact
- generic [ref=e366]:
- heading "Social Media" [level=3] [ref=e367]
- generic [ref=e368]:
- link "LinkedIn" [ref=e369] [cursor=pointer]:
- /url: https://www.linkedin.com/in/damjan-savić-720288127/
- img [ref=e370]
- link "GitHub" [ref=e374] [cursor=pointer]:
- /url: https://github.com/damjan1996
- img [ref=e375]
- generic [ref=e379]:
- paragraph [ref=e380]: © 2026 Damjan Savić. Alle Rechte vorbehalten
- generic [ref=e381]:
- link "Datenschutz" [ref=e382] [cursor=pointer]:
- /url: /de/privacy
- link "Impressum" [ref=e383] [cursor=pointer]:
- /url: /de/imprint
- link "AGB" [ref=e384] [cursor=pointer]:
- /url: /de/terms
- button "Open Next.js Dev Tools" [ref=e390] [cursor=pointer]:
- img [ref=e391]
- alert [ref=e394]
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB