180 lines
4.9 KiB
Vue
180 lines
4.9 KiB
Vue
<template>
|
|
<div class="schema-typer">
|
|
<span class="token-tag"><<span class="token-tag-name">script</span> <span class="token-attr">type</span>=<span class="token-string">"application/ld+json"</span>></span><br >
|
|
<span
|
|
v-for="(line, lineIndex) in displayedLines"
|
|
:key="lineIndex"
|
|
class="code-line"
|
|
>
|
|
<template v-for="(segment, index) in line" :key="index">
|
|
<span :class="segment.class">{{ segment.text }}</span>
|
|
</template>
|
|
<template v-if="(lineIndex === currentLine || (isTypingDone && lineIndex === displayedLines.length - 1)) && showCursor">
|
|
<span class="cursor">|</span>
|
|
</template>
|
|
<br >
|
|
</span>
|
|
<span class="token-tag"></<span class="token-tag-name">script</span>></span>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
const schema = `{
|
|
"@context": "https://schema.org",
|
|
"@type": "HowTo",
|
|
"name": "So machen wir deine Website KI-kompatibel",
|
|
"description": "Wir optimieren deine Website gezielt für künstliche Intelligenz. Strukturiert, effizient und zukunftssicher.",
|
|
"step1": "Wir integrieren strukturierte Daten nach schema.org, damit KI-Systeme Inhalte korrekt interpretieren können.",
|
|
"step2": "Wir überarbeiten den Quellcode mit semantischem HTML und klaren Überschriften zur besseren maschinellen Lesbarkeit.",
|
|
"step3": "Wir stellen relevante Inhalte zusätzlich in maschinenfreundlichen Formaten wie JSON-LD oder über APIs bereit.",
|
|
"step4": "Wir sorgen für barrierefreie Gestaltung und klare Sprache, so verstehen auch KI-Sprachmodelle deine Inhalte besser.",
|
|
"step5": "Wir ergänzen deine Seiten mit Metadaten wie OpenGraph, Twitter Cards und Robots-Tags für optimalen Kontext.",
|
|
"estimatedTime": "ca. 45 Minuten Analyse + Umsetzung je nach Umfang",
|
|
"image": "/images/ki-optimierung.jpg"
|
|
}`
|
|
|
|
function parseLine(line) {
|
|
const segments = []
|
|
const regex = /("[^"]+":)|("[^"]+")|(\d+)|(true|false|null)|(\s+|[{}[\],:])/g
|
|
let match
|
|
let lastIndex = 0
|
|
|
|
while ((match = regex.exec(line)) !== null) {
|
|
const start = match.index
|
|
if (start > lastIndex) {
|
|
segments.push({ text: line.slice(lastIndex, start), class: 'token-text' })
|
|
}
|
|
|
|
const [matched, key, str, num, bool, symbol] = match
|
|
let cls = 'token-text'
|
|
if (key) cls = 'token-key'
|
|
else if (str) cls = 'token-string'
|
|
else if (num) cls = 'token-number'
|
|
else if (bool) cls = 'token-boolean'
|
|
else if (symbol) cls = 'token-text'
|
|
|
|
segments.push({ text: matched, class: cls })
|
|
lastIndex = start + matched.length
|
|
}
|
|
|
|
if (lastIndex < line.length) {
|
|
segments.push({ text: line.slice(lastIndex), class: 'token-text' })
|
|
}
|
|
|
|
return segments
|
|
}
|
|
|
|
const lines = schema.split('\n').map(parseLine)
|
|
const displayedLines = ref([])
|
|
const showCursor = ref(true)
|
|
const currentLine = ref(0)
|
|
const isTypingDone = ref(false)
|
|
|
|
const startTyping = () => {
|
|
let lineIndex = 0
|
|
let charIndex = 0
|
|
displayedLines.value.push([])
|
|
|
|
const typeChar = () => {
|
|
if (lineIndex >= lines.length) {
|
|
isTypingDone.value = true
|
|
return
|
|
}
|
|
|
|
const segment = lines[lineIndex][charIndex]
|
|
if (segment) {
|
|
displayedLines.value[lineIndex].push(segment)
|
|
charIndex++
|
|
} else {
|
|
lineIndex++
|
|
charIndex = 0
|
|
currentLine.value = lineIndex
|
|
if (lineIndex < lines.length) displayedLines.value.push([])
|
|
}
|
|
|
|
setTimeout(typeChar, Math.random() * 80 + 40)
|
|
}
|
|
|
|
typeChar()
|
|
setInterval(() => (showCursor.value = !showCursor.value), 500)
|
|
}
|
|
|
|
onMounted(() => {
|
|
const observer = new IntersectionObserver(([entry]) => {
|
|
if (entry.isIntersecting) {
|
|
observer.disconnect()
|
|
startTyping()
|
|
}
|
|
}, { threshold: 0.3 })
|
|
|
|
const el = document.querySelector('.schema-typer')
|
|
if (el) observer.observe(el)
|
|
})
|
|
</script>
|
|
|
|
|
|
<style lang="sass">
|
|
.schema-typer
|
|
background-color: #1e1e1e
|
|
padding: 1.5rem
|
|
margin: 2rem
|
|
border-radius: 0.5rem
|
|
font-family: monospace
|
|
font-size: 0.7rem
|
|
color: #ffffff
|
|
overflow-x: auto
|
|
overflow-y: auto
|
|
line-height: 1.4
|
|
height: auto
|
|
min-height: 450px
|
|
width: 400px
|
|
transform: rotate(6deg)
|
|
box-shadow: -3px 3px 6px 4px grey
|
|
|
|
span
|
|
margin: 0
|
|
padding: 0
|
|
|
|
.code-line
|
|
display: block
|
|
line-height: 1.2
|
|
|
|
.token-tag
|
|
color: #569cd6
|
|
|
|
.token-tag-name
|
|
color: #4ec9b0
|
|
|
|
.token-attr
|
|
color: #c586c0
|
|
|
|
.token-key
|
|
color: #9cdcfe
|
|
margin-left: 1rem
|
|
|
|
.token-string
|
|
color: #ce9178
|
|
|
|
.token-number
|
|
color: #b5cea8
|
|
|
|
.token-boolean
|
|
color: #dcdcaa
|
|
|
|
.token-text
|
|
color: #d4d4d4
|
|
margin-left: .2rem
|
|
|
|
.cursor
|
|
display: inline-block
|
|
width: 1ch
|
|
background-color: white
|
|
animation: blink 1s steps(2, start) infinite
|
|
|
|
@keyframes blink
|
|
to
|
|
visibility: hidden
|
|
</style>
|
|
|