Downloaded theme
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<div class="md-alert md-alert-note">
|
||||
<div class="md-mermaid">
|
||||
<pre class="mermaid">
|
||||
{{ .Text | safeHTML }}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
{{- /* Check if this is a GitHub-style alert */ -}}
|
||||
{{- if eq .Type "alert" -}}
|
||||
<div class="md-alert md-alert-{{ .AlertType }}">
|
||||
{{ .Text | safeHTML }}
|
||||
</div>
|
||||
{{- else -}}
|
||||
<blockquote class="md-blockquote">
|
||||
{{ .Text | safeHTML }}
|
||||
</blockquote>
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="md-mermaid">
|
||||
<pre class="mermaid">
|
||||
{{ .Inner | safeHTML }}
|
||||
</pre>
|
||||
</div>
|
||||
@@ -0,0 +1,337 @@
|
||||
{{- $lang := .Type | default "text" -}}
|
||||
{{- $filename := .Attributes.filename | default "" -}}
|
||||
{{- $label := cond (ne $filename "") $filename ($lang | upper) -}}
|
||||
{{- $id := printf "cb-%s" (printf "%d" .Ordinal | sha256 | truncate 8 "") -}}
|
||||
|
||||
{{- $collapseEnabled := site.Params.codeblock.collapse.enabled | default true -}}
|
||||
{{- $defaultState := site.Params.codeblock.collapse.defaultState | default "expanded" -}}
|
||||
{{- $collapsedHeight := site.Params.codeblock.collapse.collapsedHeight | default 200 -}}
|
||||
|
||||
{{- $highlighted := transform.HighlightCodeBlock . -}}
|
||||
|
||||
<div class="mb-codeblock" id="{{ $id }}" data-lang="{{ $lang | lower }}">
|
||||
<!-- Header with language badge and actions -->
|
||||
<div class="mb-codeblock-header">
|
||||
<div class="mb-codeblock-left">
|
||||
<span class="mb-codeblock-badge">
|
||||
{{ $lang | upper }}
|
||||
</span>
|
||||
{{- if ne $filename "" -}}
|
||||
<span class="mb-codeblock-filename">
|
||||
<i class="fas fa-file-code" style="font-size: 0.65rem;"></i>
|
||||
{{ $filename }}
|
||||
</span>
|
||||
{{- end -}}
|
||||
</div>
|
||||
|
||||
<div class="mb-codeblock-actions">
|
||||
{{- if $collapseEnabled -}}
|
||||
<button class="mb-action-btn mb-collapse-btn" data-collapsed="false" aria-label="Collapse code">
|
||||
<i class="fas fa-compress-alt"></i>
|
||||
<span>Collapse</span>
|
||||
</button>
|
||||
{{- end -}}
|
||||
<button class="mb-action-btn mb-copy-btn" aria-label="Copy code">
|
||||
<i class="fas fa-copy"></i>
|
||||
<span>Copy</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Code content -->
|
||||
<div
|
||||
class="mb-codeblock-content"
|
||||
data-state="{{ $defaultState }}"
|
||||
{{- if eq $defaultState "collapsed" }}
|
||||
style="max-height: {{ $collapsedHeight }}px; overflow: hidden;"
|
||||
{{- end }}
|
||||
>
|
||||
{{ $highlighted.Wrapped }}
|
||||
|
||||
{{- if $collapseEnabled -}}
|
||||
<div class="mb-collapse-overlay">
|
||||
<button class="mb-expand-trigger">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
<span>Click to expand</span>
|
||||
</button>
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const codeblock = document.getElementById('{{ $id }}');
|
||||
if (!codeblock) return;
|
||||
|
||||
const copyBtn = codeblock.querySelector('.mb-copy-btn');
|
||||
const collapseBtn = codeblock.querySelector('.mb-collapse-btn');
|
||||
const content = codeblock.querySelector('.mb-codeblock-content');
|
||||
const overlay = codeblock.querySelector('.mb-collapse-overlay');
|
||||
const expandTrigger = overlay?.querySelector('.mb-expand-trigger');
|
||||
|
||||
// ==================
|
||||
// COPY FUNCTIONALITY
|
||||
// ==================
|
||||
if (copyBtn) {
|
||||
copyBtn.addEventListener('click', async function() {
|
||||
let codeText = '';
|
||||
|
||||
// Handle line-numbered code (Hugo's table format)
|
||||
const codeCell = codeblock.querySelector('.lntd:last-child code');
|
||||
if (codeCell) {
|
||||
codeText = codeCell.textContent || codeCell.innerText || '';
|
||||
} else {
|
||||
// Regular code block
|
||||
const codeEl = codeblock.querySelector('pre code');
|
||||
codeText = codeEl ? (codeEl.textContent || codeEl.innerText || '') : '';
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(codeText.trim());
|
||||
|
||||
// Success feedback
|
||||
const originalHTML = copyBtn.innerHTML;
|
||||
copyBtn.innerHTML = '<i class="fas fa-check"></i><span>Copied!</span>';
|
||||
copyBtn.classList.add('mb-btn-success');
|
||||
|
||||
setTimeout(() => {
|
||||
copyBtn.innerHTML = originalHTML;
|
||||
copyBtn.classList.remove('mb-btn-success');
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy code:', err);
|
||||
|
||||
// Fallback for older browsers
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = codeText.trim();
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.opacity = '0';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
copyBtn.innerHTML = '<i class="fas fa-check"></i><span>Copied!</span>';
|
||||
setTimeout(() => {
|
||||
copyBtn.innerHTML = '<i class="fas fa-copy"></i><span>Copy</span>';
|
||||
}, 2000);
|
||||
} catch (fallbackErr) {
|
||||
console.error('Fallback copy failed:', fallbackErr);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// =======================
|
||||
// COLLAPSE FUNCTIONALITY
|
||||
// =======================
|
||||
if (collapseBtn && content && overlay) {
|
||||
const collapsedHeight = {{ $collapsedHeight }};
|
||||
|
||||
function toggleCollapse() {
|
||||
const isCollapsed = content.dataset.state === 'collapsed';
|
||||
|
||||
if (isCollapsed) {
|
||||
// EXPAND
|
||||
content.style.maxHeight = '';
|
||||
content.style.overflow = '';
|
||||
content.dataset.state = 'expanded';
|
||||
overlay.style.display = 'none';
|
||||
collapseBtn.innerHTML = '<i class="fas fa-compress-alt"></i><span>Collapse</span>';
|
||||
collapseBtn.dataset.collapsed = 'false';
|
||||
} else {
|
||||
// COLLAPSE
|
||||
content.style.maxHeight = collapsedHeight + 'px';
|
||||
content.style.overflow = 'hidden';
|
||||
content.dataset.state = 'collapsed';
|
||||
overlay.style.display = 'flex';
|
||||
collapseBtn.innerHTML = '<i class="fas fa-expand-alt"></i><span>Expand</span>';
|
||||
collapseBtn.dataset.collapsed = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize if default state is collapsed
|
||||
if ('{{ $defaultState }}' === 'collapsed') {
|
||||
content.dataset.state = 'collapsed';
|
||||
overlay.style.display = 'flex';
|
||||
collapseBtn.innerHTML = '<i class="fas fa-expand-alt"></i><span>Expand</span>';
|
||||
collapseBtn.dataset.collapsed = 'true';
|
||||
}
|
||||
|
||||
collapseBtn.addEventListener('click', toggleCollapse);
|
||||
|
||||
if (expandTrigger) {
|
||||
expandTrigger.addEventListener('click', toggleCollapse);
|
||||
}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Additional styles for improved codeblock - add to your main.css */
|
||||
|
||||
.mb-codeblock-filename {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
color: var(--color-text);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 0.35rem;
|
||||
background: color-mix(in srgb, var(--color-bg) 40%, transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent);
|
||||
}
|
||||
|
||||
.mb-action-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
background: transparent;
|
||||
border: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent);
|
||||
color: var(--color-text-muted);
|
||||
cursor: pointer;
|
||||
font-size: 0.7rem;
|
||||
padding: 0.35rem 0.65rem;
|
||||
border-radius: 0.4rem;
|
||||
transition: all 0.15s ease-out;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.mb-action-btn:hover {
|
||||
color: var(--color-accent);
|
||||
background: color-mix(in srgb, var(--color-accent) 12%, transparent);
|
||||
border-color: var(--color-accent);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.mb-action-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.mb-action-btn i {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.mb-btn-success {
|
||||
color: #22c55e !important;
|
||||
border-color: #22c55e !important;
|
||||
background: color-mix(in srgb, #22c55e 12%, transparent) !important;
|
||||
}
|
||||
|
||||
.mb-collapse-overlay {
|
||||
display: none;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
rgba(0, 0, 0, 0.3) 40%,
|
||||
rgba(0, 0, 0, 0.85) 100%
|
||||
);
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
padding-bottom: 1rem;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.mb-expand-trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--color-accent);
|
||||
background: color-mix(in srgb, var(--color-accent) 20%, transparent);
|
||||
color: var(--color-accent);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease-out;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.mb-expand-trigger:hover {
|
||||
background: color-mix(in srgb, var(--color-accent) 30%, transparent);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(168, 85, 247, 0.3);
|
||||
}
|
||||
|
||||
.mb-expand-trigger i {
|
||||
font-size: 0.7rem;
|
||||
animation: bounce 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(3px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Language-specific badge colors */
|
||||
.mb-codeblock[data-lang="javascript"] .mb-codeblock-badge,
|
||||
.mb-codeblock[data-lang="js"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #f7df1e 25%, transparent);
|
||||
color: #f7df1e;
|
||||
}
|
||||
|
||||
.mb-codeblock[data-lang="typescript"] .mb-codeblock-badge,
|
||||
.mb-codeblock[data-lang="ts"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #3178c6 25%, transparent);
|
||||
color: #3178c6;
|
||||
}
|
||||
|
||||
.mb-codeblock[data-lang="python"] .mb-codeblock-badge,
|
||||
.mb-codeblock[data-lang="py"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #3776ab 25%, transparent);
|
||||
color: #3776ab;
|
||||
}
|
||||
|
||||
.mb-codeblock[data-lang="go"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #00add8 25%, transparent);
|
||||
color: #00add8;
|
||||
}
|
||||
|
||||
.mb-codeblock[data-lang="rust"] .mb-codeblock-badge,
|
||||
.mb-codeblock[data-lang="rs"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #ce422b 25%, transparent);
|
||||
color: #ce422b;
|
||||
}
|
||||
|
||||
.mb-codeblock[data-lang="html"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #e34c26 25%, transparent);
|
||||
color: #e34c26;
|
||||
}
|
||||
|
||||
.mb-codeblock[data-lang="css"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #264de4 25%, transparent);
|
||||
color: #264de4;
|
||||
}
|
||||
|
||||
.mb-codeblock[data-lang="bash"] .mb-codeblock-badge,
|
||||
.mb-codeblock[data-lang="sh"] .mb-codeblock-badge,
|
||||
.mb-codeblock[data-lang="shell"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #4eaa25 25%, transparent);
|
||||
color: #4eaa25;
|
||||
}
|
||||
|
||||
.mb-codeblock[data-lang="json"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #000000 25%, transparent);
|
||||
color: #dddddd;
|
||||
}
|
||||
|
||||
.mb-codeblock[data-lang="yaml"] .mb-codeblock-badge,
|
||||
.mb-codeblock[data-lang="yml"] .mb-codeblock-badge {
|
||||
background: color-mix(in srgb, #cb171e 25%, transparent);
|
||||
color: #cb171e;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,5 @@
|
||||
<h{{ .Level }} id="{{ .Anchor }}">
|
||||
<a href="#{{ .Anchor }}" class="md-heading-anchor">
|
||||
{{ .Text | safeHTML }}
|
||||
</a>
|
||||
</h{{ .Level }}>
|
||||
@@ -0,0 +1,12 @@
|
||||
<figure class="md-image">
|
||||
<a href="{{ .Destination | safeURL }}" class="glightbox">
|
||||
<img
|
||||
src="{{ .Destination | safeURL }}"
|
||||
alt="{{ .Text }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
</a>
|
||||
{{ with .Title }}
|
||||
<figcaption>{{ . }}</figcaption>
|
||||
{{ end }}
|
||||
</figure>
|
||||
@@ -0,0 +1,7 @@
|
||||
<a
|
||||
href="{{ .Destination | safeURL }}"
|
||||
{{ if strings.HasPrefix .Destination "http" }}target="_blank" rel="noopener"{{ end }}
|
||||
class="md-link"
|
||||
>
|
||||
{{ .Text | safeHTML }}
|
||||
</a>
|
||||
@@ -0,0 +1,39 @@
|
||||
<div class="table-wrap">
|
||||
<table
|
||||
{{- range $k, $v := .Attributes }}
|
||||
{{- if $v }}
|
||||
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
|
||||
{{- end }}
|
||||
{{- end }}>
|
||||
<thead>
|
||||
{{- range .THead }}
|
||||
<tr>
|
||||
{{- range . }}
|
||||
<th
|
||||
{{- with .Alignment }}
|
||||
{{- printf " style=%q" (printf "text-align: %s" .) | safeHTMLAttr }}
|
||||
{{- end -}}
|
||||
>
|
||||
{{- .Text -}}
|
||||
</th>
|
||||
{{- end }}
|
||||
</tr>
|
||||
{{- end }}
|
||||
</thead>
|
||||
<tbody>
|
||||
{{- range .TBody }}
|
||||
<tr>
|
||||
{{- range . }}
|
||||
<td
|
||||
{{- with .Alignment }}
|
||||
{{- printf " style=%q" (printf "text-align: %s" .) | safeHTMLAttr }}
|
||||
{{- end -}}
|
||||
>
|
||||
{{- .Text -}}
|
||||
</td>
|
||||
{{- end }}
|
||||
</tr>
|
||||
{{- end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
{{- $type := .Get "type" | default "note" -}}
|
||||
<div class="md-alert md-alert-{{ $type }}">
|
||||
{{ .Inner | markdownify }}
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
{{- $images := split (.Get "images") "," -}}
|
||||
{{- $captions := split (default "" (.Get "captions")) "," -}}
|
||||
|
||||
<div class="gallery-container" data-jg>
|
||||
{{- if .Inner -}}
|
||||
{{- /* If Inner content is provided, convert markdown images to lightbox-ready anchors */ -}}
|
||||
{{- $content := .Inner -}}
|
||||
{{- $content = replaceRE `!\[([^\]]*)\]\(([^\)]+)\)` `<a href="$2" class="glightbox" data-glightbox="description: $1"><img src="$2" alt="$1" loading="lazy"></a>` $content -}}
|
||||
{{ $content | safeHTML }}
|
||||
{{- else if $images -}}
|
||||
{{- /* Otherwise, generate from images parameter */ -}}
|
||||
{{- range $index, $image := $images -}}
|
||||
{{- $imagePath := trim $image " " -}}
|
||||
{{- $caption := "" -}}
|
||||
{{- if $captions -}}
|
||||
{{- $caption = index $captions $index | default "" | trim " " -}}
|
||||
{{- end -}}
|
||||
{{- if $imagePath -}}
|
||||
<a href="{{ $imagePath | relURL }}" class="glightbox" data-glightbox="{{ if $caption }}description: {{ $caption }}{{ end }}">
|
||||
<img src="{{ $imagePath | relURL }}" alt="{{ $caption | default $imagePath }}" loading="lazy">
|
||||
</a>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- /* Fallback: try to get images from page resources */ -}}
|
||||
{{- $resources := .Page.Resources.Match "images/*" -}}
|
||||
{{- if $resources -}}
|
||||
{{- range $resources -}}
|
||||
<a href="{{ .RelPermalink }}" class="glightbox">
|
||||
<img src="{{ .Resize "300x" }}" alt="{{ .Name }}" loading="lazy">
|
||||
</a>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
</div>
|
||||
156
themes/minimal-black/layouts/_default/about-alternative.html
Normal file
156
themes/minimal-black/layouts/_default/about-alternative.html
Normal file
@@ -0,0 +1,156 @@
|
||||
{{ define "main" }}
|
||||
<section class="layout-page">
|
||||
<article class="about-alt-page">
|
||||
|
||||
<div class="about-alt-layout">
|
||||
<!-- Left Sidebar - Profile Card -->
|
||||
<aside class="about-alt-sidebar">
|
||||
<div class="about-alt-profile-card">
|
||||
{{ with .Site.Params.hero.avatar }}
|
||||
<div class="about-alt-avatar">
|
||||
<img src="{{ . | relURL }}" alt="{{ $.Site.Params.brand }}" />
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="about-alt-avatar-placeholder">
|
||||
<i class="fas fa-code"></i>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<h1 class="about-alt-name">{{ $.Site.Params.brand }}</h1>
|
||||
|
||||
{{ with .Params.subtitle }}
|
||||
<p class="about-alt-role">{{ . }}</p>
|
||||
{{ end }}
|
||||
|
||||
{{ with .Site.Params.hero.location }}
|
||||
<div class="about-alt-meta">
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<span>{{ . }}</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<!-- Quick Stats -->
|
||||
{{ with $.Site.Params.about.alt.stats }}
|
||||
<div class="about-alt-stats">
|
||||
{{ range . }}
|
||||
<div class="about-alt-stat">
|
||||
<div class="about-alt-stat-value">{{ .value }}</div>
|
||||
<div class="about-alt-stat-label">{{ .label }}</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<!-- Social Links -->
|
||||
{{ with .Site.Params.social }}
|
||||
<div class="about-alt-social">
|
||||
{{ range . }}
|
||||
<a
|
||||
href="{{ .url }}"
|
||||
class="about-alt-social-icon"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="{{ .label }}"
|
||||
>
|
||||
<i class="{{ .icon }}"></i>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Right Content Area -->
|
||||
<div class="about-alt-content">
|
||||
<!-- Introduction -->
|
||||
<div class="about-alt-section">
|
||||
<div class="markdown-body">
|
||||
{{ $content := .Content }}
|
||||
{{ $content = replace $content "<hr>" "|||SPLIT|||" }}
|
||||
{{ $content = replace $content "<hr />" "|||SPLIT|||" }}
|
||||
{{ $content = replace $content "<hr/>" "|||SPLIT|||" }}
|
||||
{{ $parts := split $content "|||SPLIT|||" }}
|
||||
|
||||
{{ index $parts 0 | safeHTML }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Experience Cards -->
|
||||
{{ if gt (len $parts) 1 }}
|
||||
{{- $experienceParts := slice -}}
|
||||
{{- $additionalContent := "" -}}
|
||||
{{- $foundNonExperience := false -}}
|
||||
|
||||
{{- range $index, $part := after 1 $parts -}}
|
||||
{{- $trimmed := trim $part " \n\t" -}}
|
||||
{{- if $trimmed -}}
|
||||
{{- /* Check if this looks like an experience card (starts with <p><strong>) or is additional content (starts with <h) */ -}}
|
||||
{{- if or (hasPrefix $trimmed "<p><strong>") (hasPrefix $trimmed "<p><strong ") (hasPrefix $trimmed "<p>\n<strong") (hasPrefix $trimmed "<p> <strong") -}}
|
||||
{{- if not $foundNonExperience -}}
|
||||
{{- $experienceParts = $experienceParts | append $part -}}
|
||||
{{- else -}}
|
||||
{{- $additionalContent = printf "%s|||SPLIT|||%s" $additionalContent $part -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- $foundNonExperience = true -}}
|
||||
{{- $additionalContent = printf "%s|||SPLIT|||%s" $additionalContent $part -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if gt (len $experienceParts) 0 -}}
|
||||
<div class="about-alt-section">
|
||||
<h2 class="about-alt-section-title">
|
||||
<i class="fas fa-briefcase"></i>
|
||||
<span>Experience</span>
|
||||
</h2>
|
||||
|
||||
<div class="about-alt-experience-grid">
|
||||
{{- range $experienceParts -}}
|
||||
<div class="about-alt-experience-card">
|
||||
{{ . | safeHTML }}
|
||||
</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Render additional content sections */ -}}
|
||||
{{- if $additionalContent -}}
|
||||
{{- $additionalParts := split $additionalContent "|||SPLIT|||" -}}
|
||||
{{- range $additionalParts -}}
|
||||
{{- $trimmed := trim . " \n\t" -}}
|
||||
{{- if $trimmed -}}
|
||||
<div class="about-alt-section">
|
||||
<div class="markdown-body">
|
||||
{{ . | safeHTML }}
|
||||
</div>
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
<!-- Skills Badge Cloud -->
|
||||
{{ with $.Site.Params.about.alt.skills }}
|
||||
<div class="about-alt-section">
|
||||
<h2 class="about-alt-section-title">
|
||||
<i class="fas fa-code"></i>
|
||||
<span>Tech Stack</span>
|
||||
</h2>
|
||||
<div class="about-alt-skills">
|
||||
{{ range . }}
|
||||
<span class="about-alt-skill">
|
||||
{{ with .icon }}<i class="{{ . }}"></i>{{ end }}
|
||||
{{ .label }}
|
||||
</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</article>
|
||||
</section>
|
||||
{{ end }}
|
||||
77
themes/minimal-black/layouts/_default/about.html
Normal file
77
themes/minimal-black/layouts/_default/about.html
Normal file
@@ -0,0 +1,77 @@
|
||||
{{ define "main" }}
|
||||
<section class="layout-page">
|
||||
<article class="about-page">
|
||||
<!-- Hero Section -->
|
||||
<header class="about-hero">
|
||||
<div class="about-hero-content">
|
||||
{{ with .Site.Params.hero.avatar }}
|
||||
<div class="about-avatar">
|
||||
<img src="{{ . | relURL }}" alt="{{ $.Site.Params.brand }}" />
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<h1 class="about-title">{{ .Title }}</h1>
|
||||
|
||||
{{ with .Params.subtitle }}
|
||||
<p class="about-subtitle">{{ . }}</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="about-content">
|
||||
<div class="card card-pad markdown-body">
|
||||
{{ $content := .Content }}
|
||||
{{ $content = replace $content "<hr>" "|||SPLIT|||" }}
|
||||
{{ $content = replace $content "<hr />" "|||SPLIT|||" }}
|
||||
{{ $content = replace $content "<hr/>" "|||SPLIT|||" }}
|
||||
{{ $parts := split $content "|||SPLIT|||" }}
|
||||
|
||||
{{ if eq (len $parts) 1 }}
|
||||
<!-- No timeline, render normally -->
|
||||
{{ .Content }}
|
||||
{{ else }}
|
||||
<!-- Render intro section (everything before first hr) -->
|
||||
{{ index $parts 0 | safeHTML }}
|
||||
|
||||
<!-- Render timeline -->
|
||||
<div class="timeline">
|
||||
{{ range after 1 $parts }}
|
||||
{{ $trimmed := trim . " \n\t" }}
|
||||
{{ if $trimmed }}
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-marker"></div>
|
||||
<div class="timeline-content">
|
||||
{{ . | safeHTML }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Social Links Footer -->
|
||||
{{ with .Site.Params.social }}
|
||||
<div class="about-social">
|
||||
<h3 class="about-social-title">Let's Connect</h3>
|
||||
<div class="about-social-links">
|
||||
{{ range . }}
|
||||
<a
|
||||
href="{{ .url }}"
|
||||
class="about-social-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="{{ .label }}"
|
||||
>
|
||||
<i class="{{ .icon }}"></i>
|
||||
<span>{{ .label }}</span>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</article>
|
||||
</section>
|
||||
{{ end }}
|
||||
30
themes/minimal-black/layouts/_default/baseof.html
Normal file
30
themes/minimal-black/layouts/_default/baseof.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.Language.Lang }}"
|
||||
data-cb-collapse-enabled="{{ .Site.Params.features.codeblock.collapse.enabled }}"
|
||||
data-cb-collapse-default="{{ .Site.Params.features.codeblock.collapse.defaultState }}"
|
||||
data-cb-collapse-lines="{{ .Site.Params.features.codeblock.collapse.autoCollapseLines }}"
|
||||
data-cb-collapse-auto-height="{{ .Site.Params.features.codeblock.collapse.autoCollapseHeight }}"
|
||||
data-cb-collapse-collapsed-height="{{ .Site.Params.features.codeblock.collapse.collapsedHeight }}">
|
||||
<head>
|
||||
{{ partial "head.html" . }}
|
||||
</head>
|
||||
<body class="min-h-screen bg-bg text-text antialiased">
|
||||
<div class="flex min-h-screen flex-col">
|
||||
{{ partial "header.html" . }}
|
||||
|
||||
<main class="flex-1">
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
|
||||
{{ partial "footer.html" . }}
|
||||
</div>
|
||||
{{ partial "search-overlay.html" . }}
|
||||
{{ partial "dock.html" . }}
|
||||
<script src="{{ "js/main.js" | relURL }}" defer></script>
|
||||
<script src="{{ "js/search.js" | relURL }}" defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js" defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/justified-gallery@3.8.2/dist/js/justifiedGallery.min.js" defer></script>
|
||||
<script src="{{ "js/lightbox.js" | relURL }}" defer></script>
|
||||
<script src="{{ "js/gallery.js" | relURL }}" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
30
themes/minimal-black/layouts/_default/list.html
Normal file
30
themes/minimal-black/layouts/_default/list.html
Normal file
@@ -0,0 +1,30 @@
|
||||
{{ define "main" }}
|
||||
<section class="layout-page-tight">
|
||||
<div class="page-int">
|
||||
<header class="mb-8">
|
||||
<h1 class="heading-page text-2xl sm:text-3xl">{{ .Title }}</h1>
|
||||
{{ with .Description }}
|
||||
<p class="mt-2 text-sm text-muted">{{ . }}</p>
|
||||
{{ end }}
|
||||
</header>
|
||||
|
||||
<div class="space-y-6">
|
||||
{{ range .Pages.ByDate.Reverse }}
|
||||
<article class="border-b border-border pb-6 last:border-0">
|
||||
<a href="{{ .RelPermalink }}" class="group block">
|
||||
<h2 class="text-lg font-medium tracking-tight group-hover:text-accent">
|
||||
{{ .Title }}
|
||||
</h2>
|
||||
{{ with .Params.description }}
|
||||
<p class="mt-1 text-sm text-muted">{{ . }}</p>
|
||||
{{ end }} {{ with .Date }}
|
||||
<p class="mt-2 text-xs text-muted">{{ .Format "January 2, 2006" }}</p>
|
||||
{{ end }}
|
||||
</a>
|
||||
</article>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
154
themes/minimal-black/layouts/_default/single.html
Normal file
154
themes/minimal-black/layouts/_default/single.html
Normal file
@@ -0,0 +1,154 @@
|
||||
{{ define "main" }}
|
||||
<section class="layout-page section-stack">
|
||||
<div class="article-layout">
|
||||
<article class="article-main space-y-4">
|
||||
<header class="space-y-2">
|
||||
<h1 class="heading-page text-2xl sm:text-3xl">{{ .Title }}</h1>
|
||||
|
||||
<div class="text-[0.75rem] text-muted">
|
||||
{{ with .Date }}
|
||||
<span>{{ .Format "02 Jan 2006" }}</span>
|
||||
{{ end }}
|
||||
{{ with .ReadingTime }}
|
||||
<span> • {{ . }} min read</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{ with .Params.description }}
|
||||
<p class="max-w-xl text-sm text-muted">
|
||||
{{ . }}
|
||||
</p>
|
||||
{{ end }}
|
||||
</header>
|
||||
|
||||
<div class="card card-pad">
|
||||
<div class="markdown-body">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{{ if .TableOfContents }}
|
||||
<aside class="article-toc">
|
||||
<div class="toc-wrapper">
|
||||
<div class="toc-header">
|
||||
<h3 class="toc-title">
|
||||
<i class="fas fa-list-ul"></i>
|
||||
<span>Table of Contents</span>
|
||||
</h3>
|
||||
<button class="toc-toggle" aria-label="Toggle table of contents">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
<nav class="toc-nav">
|
||||
{{ .TableOfContents }}
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const tocWrapper = document.querySelector('.toc-wrapper');
|
||||
const tocToggle = document.querySelector('.toc-toggle');
|
||||
const tocNav = document.querySelector('.toc-nav');
|
||||
const tocLinks = document.querySelectorAll('.toc-nav a');
|
||||
|
||||
if (!tocWrapper || !tocNav) return;
|
||||
|
||||
// =====================
|
||||
// MOBILE TOGGLE
|
||||
// =====================
|
||||
if (tocToggle) {
|
||||
tocToggle.addEventListener('click', function() {
|
||||
tocWrapper.classList.toggle('collapsed');
|
||||
});
|
||||
}
|
||||
|
||||
// =====================
|
||||
// SMOOTH SCROLLING
|
||||
// =====================
|
||||
tocLinks.forEach(link => {
|
||||
link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const targetId = this.getAttribute('href');
|
||||
const targetElement = document.querySelector(targetId);
|
||||
|
||||
if (targetElement) {
|
||||
const yOffset = -80; // offset for sticky header
|
||||
const y = targetElement.getBoundingClientRect().top + window.pageYOffset + yOffset;
|
||||
|
||||
window.scrollTo({
|
||||
top: y,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
// Update URL without scrolling
|
||||
history.pushState(null, null, targetId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// =====================
|
||||
// ACTIVE LINK HIGHLIGHT
|
||||
// =====================
|
||||
const headings = document.querySelectorAll('.markdown-body h1[id], .markdown-body h2[id], .markdown-body h3[id], .markdown-body h4[id], .markdown-body h5[id], .markdown-body h6[id]');
|
||||
|
||||
let activeHeading = null;
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
// Find all currently intersecting headings
|
||||
const intersectingHeadings = [];
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
intersectingHeadings.push({
|
||||
element: entry.target,
|
||||
top: entry.boundingClientRect.top
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// If we have intersecting headings, find the one closest to the top
|
||||
if (intersectingHeadings.length > 0) {
|
||||
// Sort by distance from top (smallest positive value or largest negative)
|
||||
intersectingHeadings.sort((a, b) => Math.abs(a.top) - Math.abs(b.top));
|
||||
|
||||
const closestHeading = intersectingHeadings[0].element;
|
||||
const id = closestHeading.getAttribute('id');
|
||||
|
||||
if (id && id !== activeHeading) {
|
||||
activeHeading = id;
|
||||
const tocLink = document.querySelector(`.toc-nav a[href="#${id}"]`);
|
||||
|
||||
if (tocLink) {
|
||||
// Remove active from all links
|
||||
tocLinks.forEach(link => link.classList.remove('active'));
|
||||
// Add active to current link
|
||||
tocLink.classList.add('active');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: '-80px 0px -80%',
|
||||
threshold: [0, 0.25, 0.5, 0.75, 1]
|
||||
}
|
||||
);
|
||||
|
||||
// Observe all headings
|
||||
headings.forEach((heading) => {
|
||||
observer.observe(heading);
|
||||
});
|
||||
|
||||
// Highlight first item by default
|
||||
if (tocLinks.length > 0) {
|
||||
tocLinks[0].classList.add('active');
|
||||
activeHeading = tocLinks[0].getAttribute('href').substring(1);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
Reference in New Issue
Block a user