556 lines
28 KiB
PHP
556 lines
28 KiB
PHP
@extends('layouts.site')
|
||
|
||
@section('content')
|
||
<section class="wrapper !bg-[#edf2fc]">
|
||
<div class="container pt-10 pb-36 xl:pt-[4.5rem] lg:pt-[4.5rem] md:pt-[4.5rem] xl:pb-40 lg:pb-40 md:pb-40 !text-center">
|
||
<div class="flex flex-wrap mx-[-15px]">
|
||
<div class="md:w-10/12 lg:w-10/12 xl:w-8/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto">
|
||
<div class="post-header !mb-[.9rem]">
|
||
@if($post->category)
|
||
<div class="inline-flex uppercase !tracking-[0.02rem] text-[0.7rem] font-bold !text-[#aab0bc] !mb-[0.4rem] text-line relative align-top !pl-[1.4rem] before:content-[''] before:absolute before:inline-block before:translate-y-[-60%] before:w-3 before:h-[0.05rem] before:left-0 before:top-2/4 before:bg-[#e31e24]">
|
||
<a href="{{ route('blog.index', ['category' => $post->category->slug]) }}" class="hover !text-[#e31e24]" rel="category">{{ $post->category->name }}</a>
|
||
</div>
|
||
@endif
|
||
<h1 class="!text-[calc(1.365rem_+_1.38vw)] font-bold !leading-[1.2] xl:!text-[2.4rem] !mb-4">
|
||
{{ method_exists($post, 'translate') ? $post->translate('title') : $post->title }}
|
||
</h1>
|
||
<ul class="!text-[0.8rem] !text-[#aab0bc] m-0 p-0 list-none !mb-5">
|
||
<li class="post-date inline-block">
|
||
<i class="uil uil-calendar-alt pr-[0.2rem] align-[-.05rem] before:content-['\e9ba']"></i>
|
||
<span>{{ $post->published_at ? $post->published_at->format('d M Y') : $post->created_at->format('d M Y') }}</span>
|
||
</li>
|
||
<li class="post-author inline-block before:content-[''] before:inline-block before:w-[0.2rem] before:h-[0.2rem] before:opacity-50 before:m-[0_.6rem_0_.4rem] before:rounded-[100%] before:align-[.15rem] before:bg-[#aab0bc]">
|
||
<span class="!text-[0.8rem] !text-[#aab0bc]">
|
||
<i class="uil uil-user pr-[0.2rem] align-[-.05rem] before:content-['\ed6f']"></i>
|
||
<span>{{ $post->author ? $post->author->name : 'Trunçgil' }}</span>
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="wrapper !bg-[#ffffff]">
|
||
<div class="container !pb-[4.5rem] xl:!pb-24 lg:!pb-24 md:!pb-24">
|
||
<div class="flex flex-wrap mx-[-15px]">
|
||
<div class="xl:w-10/12 lg:w-10/12 w-full flex-[0_0_auto] !px-[15px] max-w-full !mx-auto">
|
||
<div class="blog single !mt-[-7rem]">
|
||
<div class="card !shadow-[0_0.25rem_1.75rem_rgba(30,34,40,0.07)]">
|
||
@if($post->featured_image || $post->featured_image_url)
|
||
<figure class="card-img-top">
|
||
<img src="{{ $post->featured_image ? asset('storage/' . $post->featured_image) : $post->featured_image_url }}" alt="{{ $post->title }}" style="width: 100%; max-height: 520px; object-fit: cover;" loading="lazy">
|
||
</figure>
|
||
@endif
|
||
<div class="card-body flex-[1_1_auto] p-[40px] xl:!p-[2.8rem_3rem_2.8rem] lg:!p-[2.8rem_3rem_2.8rem] md:!p-[2.8rem_3rem_2.8rem]">
|
||
<div class="classic-view">
|
||
<article class="post !mb-8">
|
||
<div class="relative !mb-5 post-content">
|
||
{!! method_exists($post, 'translate') ? $post->translate('content') : ($post->content ?? '') !!}
|
||
</div>
|
||
|
||
<div class="post-footer xl:!flex xl:!flex-row xl:!justify-between lg:!flex lg:!flex-row lg:!justify-between md:!flex md:!flex-row md:!justify-between !items-center !mt-8">
|
||
@if($post->tags && count($post->tags) > 0)
|
||
<div>
|
||
<ul class="pl-0 list-none tag-list !mb-0">
|
||
@foreach($post->tags as $tag)
|
||
<li class="!mt-0 !mb-[0.45rem] !mr-[0.2rem] inline-block">
|
||
<a href="{{ route('blog.index', ['tag' => $tag]) }}" class="btn btn-soft-ash btn-sm !rounded-[50rem] flex items-center hover:translate-y-[-0.15rem] hover:shadow-[0_0.25rem_0.75rem_rgba(30,34,40,.05)] before:not-italic before:content-['#'] before:font-normal before:!pr-[0.2rem]">{{ $tag }}</a>
|
||
</li>
|
||
@endforeach
|
||
</ul>
|
||
</div>
|
||
@endif
|
||
|
||
<div class="!mb-0 xl:!mb-2 lg:!mb-2 md:!mb-2">
|
||
<div class="dropdown share-dropdown btn-group">
|
||
<button class="btn btn-sm btn-red !text-white !bg-[#e31e24] border-[#e31e24] hover:text-white hover:bg-[#e31e24] hover:!border-[#e31e24] active:text-white active:bg-[#e31e24] active:border-[#e31e24] disabled:text-white disabled:bg-[#e31e24] disabled:border-[#e31e24] !rounded-[50rem] btn-icon btn-icon-start dropdown-toggle !mb-0 !mr-0 hover:translate-y-[-0.15rem] hover:shadow-[0_0.25rem_0.75rem_rgba(30,34,40,0.15)]" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||
<i class="uil uil-share-alt !mr-[0.3rem] text-[0.8rem] before:content-['\ecb0']"></i> Paylaş </button>
|
||
<div class="dropdown-menu !shadow-[rgba(30,34,40,0.06)_0px_0px_25px_0px]">
|
||
<a class="dropdown-item text-[0.7rem] !p-[.25rem_1.15rem]" href="https://twitter.com/intent/tweet?url={{ urlencode(request()->fullUrl()) }}&text={{ urlencode($post->title) }}" target="_blank"><i class="uil uil-twitter w-4 text-[0.8rem] pr-[0.4rem] align-[-.1rem] before:content-['\ed59']"></i>Twitter</a>
|
||
<a class="dropdown-item text-[0.7rem] !p-[.25rem_1.15rem]" href="https://www.facebook.com/sharer/sharer.php?u={{ urlencode(request()->fullUrl()) }}" target="_blank"><i class="uil uil-facebook-f w-4 text-[0.8rem] pr-[0.4rem] align-[-.1rem] before:content-['\eae2']"></i>Facebook</a>
|
||
<a class="dropdown-item text-[0.7rem] !p-[.25rem_1.15rem]" href="https://www.linkedin.com/sharing/share-offsite/?url={{ urlencode(request()->fullUrl()) }}" target="_blank"><i class="uil uil-linkedin w-4 text-[0.8rem] pr-[0.4rem] align-[-.1rem] before:content-['\ebd1']"></i>Linkedin</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
|
||
<hr>
|
||
|
||
<!-- Author Bio Widget -->
|
||
<div class="author-info xl:!flex lg:!flex md:!flex items-center !mb-3">
|
||
<div class="flex items-center">
|
||
<figure class="w-12 h-12 !relative !mr-4 rounded-[100%]">
|
||
<img class="rounded-[50%]" alt="image" src="{{ $post->author && $post->author->avatar ? asset('storage/' . $post->author->avatar) : asset('assets/img/avatars/u5.webp') }}" onerror="this.src='{{ asset('assets/img/avatars/u5.webp') }}'" loading="lazy">
|
||
</figure>
|
||
<div>
|
||
<h6><a href="#" class="!text-[#343f52] hover:!text-[#e31e24]">{{ $post->author ? $post->author->name : 'Trunçgil' }}</a></h6>
|
||
<span class="!text-[0.75rem] !text-[#aab0bc] m-0 p-0 list-none">{{ $post->author && $post->author->role ? $post->author->role : 'Trunçgil Teknoloji Editörü' }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="!mt-3 xl:!mt-0 lg:!mt-0 md:!mt-0 !ml-auto">
|
||
<a href="{{ route('blog.index', ['author' => $post->author_id]) }}" class="btn btn-sm btn-soft-ash !rounded-[50rem] btn-icon btn-icon-start !mb-0 hover:translate-y-[-0.15rem] hover:shadow-[0_0.25rem_0.75rem_rgba(30,34,40,0.15)]"><i class="uil uil-file-alt !mr-[0.3rem] before:content-['\eaec'] text-[.8rem]"></i> Tüm Yazıları</a>
|
||
</div>
|
||
</div>
|
||
<p>Trunçgil Teknoloji editörleri tarafından kaleme alınmış bu makalede teknoloji, yazılım geliştirme ve dijital dönüşüm konularına dair güncel gelişmeleri incelediniz. Destek ve sorularınız için bizimle iletişime geçebilirsiniz.</p>
|
||
|
||
<!-- Social Links -->
|
||
<nav class="nav social !mt-4">
|
||
<a class="text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="https://twitter.com/truncgil" target="_blank"><i class="text-[1rem] !text-[#5daed5] before:content-['\ed59'] uil uil-twitter"></i></a>
|
||
<a class="text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="https://facebook.com/truncgil" target="_blank"><i class="text-[1rem] !text-[#4470cf] before:content-['\eae2'] uil uil-facebook-f"></i></a>
|
||
<a class="text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="https://instagram.com/truncgil" target="_blank"><i class="text-[1rem] !text-[#d53581] before:content-['\eb9c'] uil uil-instagram"></i></a>
|
||
<a class="text-[1rem] transition-all duration-[0.2s] ease-in-out translate-y-0 motion-reduce:transition-none hover:translate-y-[-0.15rem] m-[0_.7rem_0_0]" href="https://youtube.com/truncgil" target="_blank"><i class="text-[1rem] !text-[#c8312b] before:content-['\edb5'] uil uil-youtube"></i></a>
|
||
</nav>
|
||
|
||
<!-- Related Posts Section (You Might Also Like) -->
|
||
@if($relatedPosts && $relatedPosts->count() > 0)
|
||
<hr>
|
||
<h3 class="!mb-6">Bunları da Beğenebilirsiniz</h3>
|
||
<div class="swiper-container blog grid-view !mb-24 relative z-10" data-margin="30" data-dots="true" data-items-md="2" data-items-xs="1">
|
||
<div class="swiper">
|
||
<div class="swiper-wrapper">
|
||
@foreach($relatedPosts as $rPost)
|
||
@php
|
||
$rTitle = method_exists($rPost, 'translate') ? $rPost->translate('title') : $rPost->title;
|
||
$rCategoryName = $rPost->category ? $rPost->category->name : '';
|
||
$rImageUrl = $rPost->featured_image ? asset('storage/' . $rPost->featured_image) : asset('assets/img/photos/tb4.jpg');
|
||
$rUrl = route('blog.show', $rPost->slug);
|
||
$rDate = $rPost->published_at ? $rPost->published_at->format('d M Y') : $rPost->created_at->format('d M Y');
|
||
@endphp
|
||
<div class="swiper-slide">
|
||
<article>
|
||
<figure class="overlay overlay-1 hover-scale group rounded !mb-5">
|
||
<a href="{{ $rUrl }}">
|
||
<img class="!transition-all !duration-[0.35s] !ease-in-out group-hover:scale-105" src="{{ $rImageUrl }}" alt="{{ $rTitle }}" style="height: 220px; object-fit: cover; width: 100%;" loading="lazy">
|
||
</a>
|
||
<figcaption class="group-hover:opacity-100 absolute w-full h-full opacity-0 text-center px-4 py-3 inset-0 z-[5] pointer-events-none p-2">
|
||
<h5 class="from-top !mb-0 absolute w-full translate-y-[-80%] p-[.75rem_1rem] left-0 top-2/4 !text-white">Devamını Oku</h5>
|
||
</figcaption>
|
||
</figure>
|
||
<div class="post-header !mb-[.9rem]">
|
||
@if($rPost->category)
|
||
<div class="inline-flex !mb-[.4rem] uppercase !tracking-[0.02rem] text-[0.7rem] font-bold !text-[#aab0bc] relative align-top !pl-[1.4rem] before:content-[''] before:absolute before:inline-block before:translate-y-[-60%] before:w-3 before:h-[0.05rem] before:left-0 before:top-2/4 before:bg-[#e31e24]">
|
||
<a href="{{ route('blog.index', ['category' => $rPost->category->slug]) }}" class="hover !text-[#e31e24]" rel="category">{{ $rCategoryName }}</a>
|
||
</div>
|
||
@endif
|
||
<h2 class="post-title h3 !mt-1 !mb-3">
|
||
<a class="!text-[#343f52] hover:!text-[#e31e24]" href="{{ $rUrl }}">{{ \Illuminate\Support\Str::limit($rTitle, 50) }}</a>
|
||
</h2>
|
||
</div>
|
||
<div class="post-footer">
|
||
<ul class="!text-[0.7rem] !text-[#aab0bc] m-0 p-0 list-none !mb-0">
|
||
<li class="post-date inline-block"><i class="uil uil-calendar-alt pr-[0.2rem] align-[-.05rem] before:content-['\e9ba']"></i><span>{{ $rDate }}</span></li>
|
||
</ul>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
<div class="swiper-controls">
|
||
<div class="swiper-pagination swiper-pagination-clickable swiper-pagination-bullets swiper-pagination-horizontal"></div>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
@endsection
|
||
|
||
@push('structured-data')
|
||
@php
|
||
$postTitle = method_exists($post, 'translate') ? $post->translate('title') : $post->title;
|
||
$postExcerpt = method_exists($post, 'translate') ? $post->translate('excerpt') : ($post->excerpt ?? '');
|
||
$postContent = method_exists($post, 'translate') ? $post->translate('content') : ($post->content ?? '');
|
||
$postExcerptText = trim(strip_tags($postExcerpt));
|
||
if (empty($postExcerptText)) {
|
||
$postExcerptText = \Illuminate\Support\Str::limit(trim(strip_tags($postContent)), 160);
|
||
}
|
||
|
||
$postUrl = route('blog.show', $post->slug);
|
||
$imageUrl = $post->featured_image ? asset('storage/' . $post->featured_image) : ($post->featured_image_url ?? asset('assets/img/photos/tb10.jpg'));
|
||
|
||
$publishedDate = $post->published_at ? $post->published_at->toIso8601String() : $post->created_at->toIso8601String();
|
||
$modifiedDate = $post->updated_at ? $post->updated_at->toIso8601String() : $publishedDate;
|
||
|
||
$authorName = $post->author ? $post->author->name : 'Trunçgil';
|
||
$authorRole = $post->author && $post->author->role ? $post->author->role : 'Trunçgil Teknoloji Editörü';
|
||
$authorUrl = route('blog.index', ['author' => $post->author_id ?? 1]);
|
||
|
||
$siteName = setting('site_name', 'Trunçgil Teknoloji');
|
||
$siteLogo = setting('site_logo') ? (str_starts_with(setting('site_logo'), 'http') ? setting('site_logo') : asset('storage/' . ltrim(setting('site_logo'), '/'))) : asset('assets/img/logo.png');
|
||
$sitePhone = setting('contact_phone', '+902244431620');
|
||
$siteEmail = setting('contact_email', 'info@truncgil.com');
|
||
$siteAddress = setting('contact_address', 'Gaziantep Teknopark, Şahinbey, Gaziantep');
|
||
|
||
$socialLinksRaw = setting('social_links');
|
||
$socialLinks = [];
|
||
if ($socialLinksRaw) {
|
||
$decoded = json_decode($socialLinksRaw, true);
|
||
if (is_array($decoded)) {
|
||
$socialLinks = array_values(array_filter($decoded));
|
||
} elseif (is_string($socialLinksRaw)) {
|
||
$socialLinks = [$socialLinksRaw];
|
||
}
|
||
}
|
||
if (empty($socialLinks)) {
|
||
$socialLinks = [
|
||
'https://twitter.com/truncgil',
|
||
'https://facebook.com/truncgil',
|
||
'https://instagram.com/truncgil',
|
||
'https://youtube.com/truncgil',
|
||
'https://linkedin.com/company/truncgil'
|
||
];
|
||
}
|
||
|
||
// 1. Organization & LocalBusiness combined
|
||
$publisherData = [
|
||
"@context" => "https://schema.org",
|
||
"@type" => "ProfessionalService",
|
||
"@id" => url('/') . "#organization",
|
||
"name" => $siteName,
|
||
"url" => url('/'),
|
||
"logo" => [
|
||
"@type" => "ImageObject",
|
||
"url" => $siteLogo
|
||
],
|
||
"image" => $siteLogo,
|
||
"telephone" => $sitePhone,
|
||
"email" => $siteEmail,
|
||
"address" => [
|
||
"@type" => "PostalAddress",
|
||
"streetAddress" => $siteAddress,
|
||
"addressLocality" => "Şahinbey",
|
||
"addressRegion" => "Gaziantep",
|
||
"postalCode" => "27010",
|
||
"addressCountry" => "TR"
|
||
],
|
||
"geo" => [
|
||
"@type" => "GeoCoordinates",
|
||
"latitude" => 37.0274446,
|
||
"longitude" => 37.3074315
|
||
],
|
||
"hasMap" => "https://maps.google.com/?q=37.0274446,37.3074315",
|
||
"sameAs" => $socialLinks,
|
||
"areaServed" => [
|
||
["@type" => "Country", "name" => "TR"],
|
||
["@type" => "Country", "name" => "DE"],
|
||
["@type" => "Country", "name" => "RU"],
|
||
["@type" => "Country", "name" => "US"],
|
||
["@type" => "AdministrativeArea", "name" => "Gaziantep"],
|
||
["@type" => "AdministrativeArea", "name" => "Bursa"]
|
||
],
|
||
"priceRange" => "$$"
|
||
];
|
||
|
||
// 2. BreadcrumbList
|
||
$categoryName = $post->category ? $post->category->name : 'Genel';
|
||
$categorySlug = $post->category ? $post->category->slug : 'genel';
|
||
$categoryUrl = route('blog.index', ['category' => $categorySlug]);
|
||
|
||
$breadcrumbs = [
|
||
"@context" => "https://schema.org",
|
||
"@type" => "BreadcrumbList",
|
||
"itemListElement" => [
|
||
[
|
||
"@type" => "ListItem",
|
||
"position" => 1,
|
||
"name" => "Anasayfa",
|
||
"item" => url('/')
|
||
],
|
||
[
|
||
"@type" => "ListItem",
|
||
"position" => 2,
|
||
"name" => "Blog",
|
||
"item" => route('blog.index')
|
||
],
|
||
[
|
||
"@type" => "ListItem",
|
||
"position" => 3,
|
||
"name" => $categoryName,
|
||
"item" => $categoryUrl
|
||
],
|
||
[
|
||
"@type" => "ListItem",
|
||
"position" => 4,
|
||
"name" => $postTitle,
|
||
"item" => $postUrl
|
||
]
|
||
]
|
||
];
|
||
|
||
// 3. BlogPosting
|
||
$blogPosting = [
|
||
"@context" => "https://schema.org",
|
||
"@type" => "BlogPosting",
|
||
"@id" => $postUrl . "#blogposting",
|
||
"mainEntityOfPage" => [
|
||
"@type" => "WebPage",
|
||
"@id" => $postUrl
|
||
],
|
||
"headline" => $postTitle,
|
||
"description" => $postExcerptText,
|
||
"image" => $imageUrl,
|
||
"datePublished" => $publishedDate,
|
||
"dateModified" => $modifiedDate,
|
||
"author" => [
|
||
"@type" => "Person",
|
||
"name" => $authorName,
|
||
"jobTitle" => $authorRole,
|
||
"url" => $authorUrl
|
||
],
|
||
"publisher" => [
|
||
"@type" => "Organization",
|
||
"name" => $siteName,
|
||
"logo" => [
|
||
"@type" => "ImageObject",
|
||
"url" => $siteLogo
|
||
]
|
||
],
|
||
"articleSection" => $categoryName,
|
||
"keywords" => is_array($post->tags) ? implode(', ', $post->tags) : ($post->tags ?? '')
|
||
];
|
||
@endphp
|
||
<script type="application/ld+json">
|
||
{!! json_encode($publisherData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) !!}
|
||
</script>
|
||
<script type="application/ld+json">
|
||
{!! json_encode($breadcrumbs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) !!}
|
||
</script>
|
||
<script type="application/ld+json">
|
||
{!! json_encode($blogPosting, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) !!}
|
||
</script>
|
||
@endpush
|
||
|
||
@push('styles')
|
||
<!-- CodeMirror Styles -->
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.css" integrity="sha384-zaeBlB/vwYsDRSlFajnDd7OydJ0cWk+c2OWybl3eSUf6hW2EbhlCsQPqKr3gkznT" crossorigin="anonymous">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/theme/material-darker.min.css" integrity="sha384-eZTPTN0EvJdn23s24UDYJmUM2T7C2ZFa3qFLypeBruJv8mZeTusKUAO/j5zPAQ6l" crossorigin="anonymous">
|
||
<!-- Google Fonts JetBrains Mono -->
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300..800;1,300..800&display=swap" rel="stylesheet">
|
||
|
||
<style>
|
||
/* Custom modern code block window styling */
|
||
.code-window {
|
||
background-color: #212529;
|
||
border-radius: 12px;
|
||
box-shadow: 0 12px 24px rgba(30, 34, 40, 0.08), 0 4px 12px rgba(30, 34, 40, 0.04);
|
||
margin: 2rem 0;
|
||
overflow: hidden;
|
||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.code-window-header {
|
||
background-color: #1a1d20;
|
||
padding: 0.8rem 1.2rem;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
.code-window-dots {
|
||
display: flex;
|
||
gap: 7px;
|
||
align-items: center;
|
||
}
|
||
|
||
.code-window-dot {
|
||
width: 11px;
|
||
height: 11px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.dot-red { background-color: #ff5f56; }
|
||
.dot-yellow { background-color: #ffbd2e; }
|
||
.dot-green { background-color: #27c93f; }
|
||
|
||
.code-window-lang {
|
||
font-size: 0.75rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
color: #aab0bc;
|
||
background-color: rgba(255, 255, 255, 0.08);
|
||
padding: 3px 10px;
|
||
border-radius: 6px;
|
||
font-family: 'Space Grotesk', system-ui, sans-serif;
|
||
}
|
||
|
||
/* Customizing CodeMirror specifically */
|
||
.CodeMirror {
|
||
font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', Courier, monospace !important;
|
||
font-size: 0.85rem !important;
|
||
line-height: 1.65 !important;
|
||
height: auto !important;
|
||
background: #212529 !important;
|
||
padding: 0.8rem 0 !important;
|
||
}
|
||
|
||
.CodeMirror-scroll {
|
||
max-height: 550px !important;
|
||
}
|
||
|
||
.CodeMirror-gutters {
|
||
background-color: #212529 !important;
|
||
border-right: 1px solid rgba(255, 255, 255, 0.05) !important;
|
||
width: 48px !important;
|
||
}
|
||
|
||
.CodeMirror-linenumber {
|
||
color: #4a505e !important;
|
||
padding-left: 12px !important;
|
||
}
|
||
|
||
.CodeMirror-cursor {
|
||
border-left: 1px solid transparent !important; /* Hide cursor for read-only blocks */
|
||
}
|
||
|
||
/* Styling selection */
|
||
.CodeMirror-selected {
|
||
background: rgba(255, 255, 255, 0.1) !important;
|
||
}
|
||
.CodeMirror-focused .CodeMirror-selected {
|
||
background: rgba(255, 255, 255, 0.15) !important;
|
||
}
|
||
</style>
|
||
@endpush
|
||
|
||
@push('scripts')
|
||
<!-- CodeMirror Core JS -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.js" integrity="sha384-uTzi9PU3axyLeKbgCPqQdNFT5Q23BRgFbmtrolYC7ROlbWVv7IggV+3r0ALSQd/s" crossorigin="anonymous"></script>
|
||
|
||
<!-- CodeMirror Modes -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/xml/xml.min.js" integrity="sha384-xPpkMo5nDgD98fIcuRVYhxkZV6/9Y4L8s3p0J5c4MxgJkyKJ8BJr+xfRkq7kn6Tw" crossorigin="anonymous"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/javascript/javascript.min.js" integrity="sha384-g0o+WW9mdIxA7LaaCKTkRm0M5TVT+Bb4s9eocxPsI2G0Xm0POG9iD6G6qP1IIsfS" crossorigin="anonymous"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/css/css.min.js" integrity="sha384-fpeIC2FZuPmw7mIsTvgB5BNc8QVxQC/nWg2W+CgPYOAiBiYVuHe2E8HiTWHBMIJQ" crossorigin="anonymous"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/clike/clike.min.js" integrity="sha384-bZx++XMDqT0QRVoq6eVZnkq04LmwWZ0eLo0tFdSGkOJgH8qiqeMIIFJ+mRaM0wfb" crossorigin="anonymous"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/php/php.min.js" integrity="sha384-1FUwPY2kaZKXw258/9CYBSS+zcc3CPggxE1zLjmYYiOdkcOw3KcXH5VNJWWbjw2U" crossorigin="anonymous"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/python/python.min.js" integrity="sha384-Xy+2exU6lBoT4OpUOtnQb+cUpn+nlJQEHvRobWVtwz6wIsw4oNoO7xyd/l8rYgMy" crossorigin="anonymous"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/sql/sql.min.js" integrity="sha384-HxXmA1hLc56V6Ja4yfcCwAprmbnS4tuvKYS0qKG3t6oxOFMflcnYq5fOnt6wVCda" crossorigin="anonymous"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/shell/shell.min.js" integrity="sha384-dmPRq54XNtYnlnAo7QrwJnZNa5r0JP5gU8z4H0686Cgz37qrR2jmNaDHUXyMI4wn" crossorigin="anonymous"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/yaml/yaml.min.js" integrity="sha384-LekyoEGHzBMGUx26EEtC5G6HD6iyIr0msuy3LP3F8V98vVS5ZoNfIyiirXsoqAt/" crossorigin="anonymous"></script>
|
||
|
||
<!-- Initialization Script -->
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const codeBlocks = document.querySelectorAll('.post-content pre code');
|
||
if (codeBlocks.length === 0) return;
|
||
|
||
codeBlocks.forEach(function(codeEl) {
|
||
// Extract raw code text securely (safely handled by textContent)
|
||
let rawCode = codeEl.textContent || codeEl.innerText;
|
||
|
||
// Fix double-escaped newlines if the code contains '\n' sequences
|
||
if (rawCode.includes('\\n')) {
|
||
const literalCount = (rawCode.match(/\\n/g) || []).length;
|
||
const actualCount = (rawCode.match(/\n/g) || []).length;
|
||
if (literalCount > actualCount) {
|
||
rawCode = rawCode.replace(/\\n/g, '\n');
|
||
}
|
||
}
|
||
|
||
// Retrieve and parse language from class
|
||
let lang = 'code';
|
||
|
||
const classes = codeEl.className.split(' ');
|
||
for (const cls of classes) {
|
||
if (cls.startsWith('language-') || cls.startsWith('lang-')) {
|
||
lang = cls.replace('language-', '').replace('lang-', '');
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Map languages to CodeMirror modes
|
||
const modeMap = {
|
||
'js': 'javascript',
|
||
'javascript': 'javascript',
|
||
'ts': 'javascript',
|
||
'typescript': 'javascript',
|
||
'json': 'javascript',
|
||
'html': 'xml',
|
||
'xml': 'xml',
|
||
'svg': 'xml',
|
||
'css': 'css',
|
||
'php': 'php',
|
||
'python': 'python',
|
||
'py': 'python',
|
||
'sql': 'sql',
|
||
'bash': 'shell',
|
||
'sh': 'shell',
|
||
'shell': 'shell',
|
||
'yaml': 'yaml',
|
||
'yml': 'yaml'
|
||
};
|
||
|
||
const resolvedMode = modeMap[lang.toLowerCase()] || 'javascript';
|
||
|
||
// Display nice label for the language badge
|
||
const displayLang = lang === 'code' ? 'Code' : lang;
|
||
|
||
// Build premium macOS-style window wrapping structure strictly using secure DOM methods (no innerHTML)
|
||
const preEl = codeEl.parentNode;
|
||
if (!preEl || preEl.tagName !== 'PRE') return;
|
||
|
||
const codeWindow = document.createElement('div');
|
||
codeWindow.className = 'code-window';
|
||
|
||
const header = document.createElement('div');
|
||
header.className = 'code-window-header';
|
||
|
||
const dots = document.createElement('div');
|
||
dots.className = 'code-window-dots';
|
||
|
||
const dotRed = document.createElement('div');
|
||
dotRed.className = 'code-window-dot dot-red';
|
||
const dotYellow = document.createElement('div');
|
||
dotYellow.className = 'code-window-dot dot-yellow';
|
||
const dotGreen = document.createElement('div');
|
||
dotGreen.className = 'code-window-dot dot-green';
|
||
|
||
dots.appendChild(dotRed);
|
||
dots.appendChild(dotYellow);
|
||
dots.appendChild(dotGreen);
|
||
|
||
const langBadge = document.createElement('div');
|
||
langBadge.className = 'code-window-lang';
|
||
langBadge.textContent = displayLang; // Secure text content insertion
|
||
|
||
header.appendChild(dots);
|
||
header.appendChild(langBadge);
|
||
|
||
const editorContainer = document.createElement('div');
|
||
editorContainer.className = 'code-editor-container';
|
||
|
||
codeWindow.appendChild(header);
|
||
codeWindow.appendChild(editorContainer);
|
||
|
||
// Replace the legacy pre tag with our beautiful new code window
|
||
preEl.parentNode.replaceChild(codeWindow, preEl);
|
||
|
||
// Instantiate CodeMirror inside the new container
|
||
CodeMirror(editorContainer, {
|
||
value: rawCode.trim(),
|
||
mode: resolvedMode,
|
||
theme: 'material-darker',
|
||
lineNumbers: true,
|
||
lineWrapping: true,
|
||
readOnly: 'nocursor',
|
||
viewportMargin: Infinity
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
@endpush
|
||
|