HUGE rework of the site to not generate static content into the main
page. Page now loads windows and blog posts at runtime, reducing page size dramatically.
This commit is contained in:
parent
4ac4bbb2f7
commit
ddc223846e
17 changed files with 211 additions and 239 deletions
|
|
@ -8,12 +8,12 @@ module.exports = function (eleventyConfig) {
|
|||
|
||||
// Collection: about window content
|
||||
eleventyConfig.addCollection("aboutContent", (api) =>
|
||||
api.getFilteredByGlob("src/about.md")
|
||||
api.getFilteredByGlob("src/content/about.md")
|
||||
);
|
||||
|
||||
// Collection: contact window content
|
||||
eleventyConfig.addCollection("contactContent", (api) =>
|
||||
api.getFilteredByGlob("src/contact.md")
|
||||
api.getFilteredByGlob("src/content/contact.md")
|
||||
);
|
||||
|
||||
// Collection: projects, sorted alphabetically by title
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@ deploy.sh
|
|||
node_modules/
|
||||
_site/
|
||||
.claude
|
||||
CLAUDE.md
|
||||
|
|
|
|||
14
src/_includes/fragment.njk
Normal file
14
src/_includes/fragment.njk
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{% if title %}
|
||||
<h2>{{ title }}</h2>
|
||||
{% if date or tags %}
|
||||
<p class="post-meta">
|
||||
{%- if date %}{{ date | readableDate }}{% if tags %} · {% endif %}{% endif -%}
|
||||
{%- if tags %}{{ tags | join(', ') }}{% endif %}
|
||||
</p>
|
||||
<hr class="post-rule">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{{ content | safe }}
|
||||
{%- if boringUrl %}
|
||||
<p><a href="{{ boringUrl }}">→ plaintext version</a></p>
|
||||
{% endif %}
|
||||
|
|
@ -1,98 +1,80 @@
|
|||
(function () {
|
||||
var listing = document.getElementById('blog-listing');
|
||||
var content = document.getElementById('blog-window-content');
|
||||
var panels = document.querySelectorAll('.blog-post-panel');
|
||||
var tagBtns = document.querySelectorAll('.blog-tag');
|
||||
var items = document.querySelectorAll('.post-list-item');
|
||||
"use strict";
|
||||
|
||||
// Tag filtering
|
||||
tagBtns.forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
tagBtns.forEach(function (b) { b.classList.remove('active'); });
|
||||
btn.classList.add('active');
|
||||
var tag = btn.dataset.tag;
|
||||
items.forEach(function (li) {
|
||||
var tags = li.dataset.tags ? li.dataset.tags.split(' ') : [];
|
||||
li.style.display = (tag === '*' || tags.indexOf(tag) !== -1) ? '' : 'none';
|
||||
});
|
||||
function syncWindow(container) {
|
||||
container.scrollTop = 0;
|
||||
var win = container.closest(".window");
|
||||
if (win && win._syncMore) win._syncMore();
|
||||
}
|
||||
|
||||
function openSubPage(url, container, backUrl) {
|
||||
WM.loadFragment(url, container, function () {
|
||||
var back = document.createElement("button");
|
||||
back.className = "blog-back";
|
||||
back.dataset.backUrl = backUrl;
|
||||
back.textContent = "\u2190 back";
|
||||
container.insertBefore(back, container.firstChild);
|
||||
syncWindow(container);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Open a post
|
||||
document.querySelectorAll('.blog-open-post').forEach(function (link) {
|
||||
link.addEventListener('click', function (e) {
|
||||
document.addEventListener("click", function (e) {
|
||||
|
||||
// Open a blog post
|
||||
var postLink = e.target.closest(".blog-open-post");
|
||||
if (postLink) {
|
||||
e.preventDefault();
|
||||
listing.classList.add('hidden');
|
||||
panels.forEach(function (p) { p.classList.add('hidden'); });
|
||||
var panel = document.getElementById('blog-post-' + link.dataset.postIdx);
|
||||
if (panel) {
|
||||
panel.classList.remove('hidden');
|
||||
content.scrollTop = 0;
|
||||
var win = content.closest('.window');
|
||||
if (win && win._syncMore) win._syncMore();
|
||||
}
|
||||
});
|
||||
});
|
||||
var container = postLink.closest(".window-content");
|
||||
openSubPage(postLink.dataset.postUrl, container, "/fragments/blog/");
|
||||
return;
|
||||
}
|
||||
|
||||
// Open a project
|
||||
var projLink = e.target.closest(".project-open-item");
|
||||
if (projLink) {
|
||||
e.preventDefault();
|
||||
var container = projLink.closest(".window-content");
|
||||
openSubPage(projLink.dataset.projectUrl, container, "/fragments/projects/");
|
||||
return;
|
||||
}
|
||||
|
||||
// Back button (shared by blog and projects)
|
||||
var backBtn = e.target.closest(".blog-back");
|
||||
if (backBtn) {
|
||||
var container = backBtn.closest(".window-content");
|
||||
WM.loadFragment(backBtn.dataset.backUrl, container, function () {
|
||||
syncWindow(container);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Blog tag filtering
|
||||
var blogTag = e.target.closest(".blog-tag");
|
||||
if (blogTag) {
|
||||
var content = blogTag.closest(".window-content");
|
||||
content.querySelectorAll(".blog-tag").forEach(function (b) { b.classList.remove("active"); });
|
||||
blogTag.classList.add("active");
|
||||
var tag = blogTag.dataset.tag;
|
||||
content.querySelectorAll(".post-list-item").forEach(function (li) {
|
||||
var tags = li.dataset.tags ? li.dataset.tags.split(" ") : [];
|
||||
li.style.display = (tag === "*" || tags.indexOf(tag) !== -1) ? "" : "none";
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Project tag filtering
|
||||
var projTag = e.target.closest(".project-tag");
|
||||
if (projTag) {
|
||||
var content = projTag.closest(".window-content");
|
||||
content.querySelectorAll(".project-tag").forEach(function (b) { b.classList.remove("active"); });
|
||||
projTag.classList.add("active");
|
||||
var tag = projTag.dataset.tag;
|
||||
content.querySelectorAll(".project-list-item").forEach(function (li) {
|
||||
var tags = li.dataset.tags ? li.dataset.tags.split(" ") : [];
|
||||
li.style.display = (tag === "*" || tags.indexOf(tag) !== -1) ? "" : "none";
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Back to listing
|
||||
document.querySelectorAll('.blog-back').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
panels.forEach(function (p) { p.classList.add('hidden'); });
|
||||
listing.classList.remove('hidden');
|
||||
content.scrollTop = 0;
|
||||
var win = content.closest('.window');
|
||||
if (win && win._syncMore) win._syncMore();
|
||||
});
|
||||
});
|
||||
}());
|
||||
|
||||
// Projects panel
|
||||
(function () {
|
||||
var listing = document.getElementById('projects-listing');
|
||||
var content = document.getElementById('projects-window-content');
|
||||
var panels = document.querySelectorAll('.project-detail-panel');
|
||||
var tagBtns = document.querySelectorAll('.project-tag');
|
||||
var items = document.querySelectorAll('.project-list-item');
|
||||
|
||||
if (!listing) return;
|
||||
|
||||
// Tag filtering
|
||||
tagBtns.forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
tagBtns.forEach(function (b) { b.classList.remove('active'); });
|
||||
btn.classList.add('active');
|
||||
var tag = btn.dataset.tag;
|
||||
items.forEach(function (li) {
|
||||
var tags = li.dataset.tags ? li.dataset.tags.split(' ') : [];
|
||||
li.style.display = (tag === '*' || tags.indexOf(tag) !== -1) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Open a project
|
||||
document.querySelectorAll('.project-open-item').forEach(function (link) {
|
||||
link.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
listing.classList.add('hidden');
|
||||
panels.forEach(function (p) { p.classList.add('hidden'); });
|
||||
var panel = document.getElementById('project-item-' + link.dataset.projectIdx);
|
||||
if (panel) {
|
||||
panel.classList.remove('hidden');
|
||||
content.scrollTop = 0;
|
||||
var win = content.closest('.window');
|
||||
if (win && win._syncMore) win._syncMore();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Back to listing
|
||||
document.querySelectorAll('.project-back').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
panels.forEach(function (p) { p.classList.add('hidden'); });
|
||||
listing.classList.remove('hidden');
|
||||
content.scrollTop = 0;
|
||||
var win = content.closest('.window');
|
||||
if (win && win._syncMore) win._syncMore();
|
||||
});
|
||||
});
|
||||
}());
|
||||
|
|
|
|||
|
|
@ -6,10 +6,33 @@ const WM = (() => {
|
|||
"use strict";
|
||||
|
||||
let topZ = 100;
|
||||
const state = new Map(); // wid → { el, hidden, maximized, savedStyle }
|
||||
const state = new Map(); // wid → { el, hidden, maximized, savedStyle, contentLoaded }
|
||||
let drag = null;
|
||||
let resize = null;
|
||||
|
||||
/* Fragment loader */
|
||||
|
||||
const fragmentCache = {};
|
||||
|
||||
function loadFragment(url, container, onDone) {
|
||||
if (fragmentCache[url]) {
|
||||
container.innerHTML = fragmentCache[url];
|
||||
if (onDone) onDone();
|
||||
return;
|
||||
}
|
||||
container.innerHTML = "<p style=\"color:var(--p-dim);\">Loading\u2026</p>";
|
||||
fetch(url)
|
||||
.then(r => r.ok ? r.text() : Promise.reject(r.status))
|
||||
.then(html => {
|
||||
fragmentCache[url] = html;
|
||||
container.innerHTML = html;
|
||||
if (onDone) onDone();
|
||||
})
|
||||
.catch(() => {
|
||||
container.innerHTML = "<p style=\"color:var(--p-dim);\">Failed to load.</p>";
|
||||
});
|
||||
}
|
||||
|
||||
const mobile = () => window.matchMedia("(pointer: coarse)").matches;
|
||||
|
||||
/* Init */
|
||||
|
|
@ -90,7 +113,7 @@ const WM = (() => {
|
|||
buildChrome(win);
|
||||
|
||||
const hidden = win.classList.contains("hidden");
|
||||
state.set(id, { el: win, hidden, maximized: false, savedStyle: null });
|
||||
state.set(id, { el: win, hidden, maximized: false, savedStyle: null, contentLoaded: false });
|
||||
|
||||
// Draggable title bar (desktop only)
|
||||
const bar = win.querySelector(".window-titlebar");
|
||||
|
|
@ -156,6 +179,13 @@ const WM = (() => {
|
|||
s.el.classList.remove("hidden");
|
||||
s.hidden = false;
|
||||
focusEl(s.el);
|
||||
if (s.el.dataset.src && !s.contentLoaded) {
|
||||
s.contentLoaded = true;
|
||||
const contentEl = s.el.querySelector(".window-content");
|
||||
if (contentEl) loadFragment(s.el.dataset.src, contentEl, () => {
|
||||
if (s.el._syncMore) s.el._syncMore();
|
||||
});
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
clampToArea(s.el);
|
||||
if (s.el._syncMore) s.el._syncMore();
|
||||
|
|
@ -272,5 +302,5 @@ const WM = (() => {
|
|||
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
|
||||
return { show, hide, toggle, focus };
|
||||
return { show, hide, toggle, focus, loadFragment };
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
---
|
||||
permalink: false
|
||||
title: "Over The Wire - Behemoth retrospective"
|
||||
date: 2026-03-30
|
||||
tags:
|
||||
|
|
|
|||
4
src/blog/posts/posts.11tydata.js
Normal file
4
src/blog/posts/posts.11tydata.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
layout: "fragment.njk",
|
||||
permalink: data => `/fragments/blog/${data.page.fileSlug}/`
|
||||
};
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"layout": "post-plain.njk"
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
---
|
||||
permalink: false
|
||||
layout: fragment.njk
|
||||
permalink: /fragments/about/
|
||||
boringUrl: /boring/about/
|
||||
---
|
||||
|
||||
## $whoami
|
||||
|
|
@ -15,4 +17,4 @@ Reach out if you want to chat about exploit dev, vulnerability research, or retr
|
|||
- Anything \*NIX. Practically, mostly Linux. Ask me about old UNIX or BSD.
|
||||
- Systems programming, reverse engineering, exploitation
|
||||
- Network services and architecture
|
||||
- Rather familiar with Windows internals as well
|
||||
- Rather familiar with Windows internals as well
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
---
|
||||
permalink: false
|
||||
layout: fragment.njk
|
||||
permalink: /fragments/contact/
|
||||
boringUrl: /boring/contact/
|
||||
---
|
||||
|
||||
## Jake "**lordtet**" Holtham
|
||||
33
src/fragments/blog.njk
Normal file
33
src/fragments/blog.njk
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
permalink: /fragments/blog/
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
{% set tagCounts = collections.posts | tagCounts %}
|
||||
{% if tagCounts.length %}
|
||||
<div class="blog-tags">
|
||||
<button class="blog-tag active" data-tag="*">all <span class="tag-count">{{ collections.posts.length }}</span></button>
|
||||
{% for tc in tagCounts %}
|
||||
<button class="blog-tag" data-tag="{{ tc.tag }}">{{ tc.tag }} <span class="tag-count">{{ tc.count }}</span></button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if collections.posts.length %}
|
||||
<ul class="post-list" id="blog-post-list">
|
||||
{% for post in collections.posts %}
|
||||
<li class="post-list-item"
|
||||
data-tags="{{ post.data.tags | join(' ') if post.data.tags else '' }}">
|
||||
<a href="#"
|
||||
class="blog-open-post"
|
||||
data-post-url="/fragments/blog/{{ post.fileSlug }}/">{{ post.data.title }}</a>
|
||||
<span class="post-date">{{ post.date | readableDate }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p style="color:var(--p-dim);">No posts yet — check back soon.</p>
|
||||
{% endif %}
|
||||
|
||||
<p style="margin-top:14px; border-top:1px solid var(--p-dim); padding-top:10px;">
|
||||
<a href="/boring/blog/">→ plaintext version</a>
|
||||
</p>
|
||||
32
src/fragments/projects.njk
Normal file
32
src/fragments/projects.njk
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
permalink: /fragments/projects/
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
{% set projectTagCounts = collections.projects | tagCounts %}
|
||||
{% if projectTagCounts.length %}
|
||||
<div class="blog-tags">
|
||||
<button class="project-tag active" data-tag="*">all <span class="tag-count">{{ collections.projects.length }}</span></button>
|
||||
{% for tc in projectTagCounts %}
|
||||
<button class="project-tag" data-tag="{{ tc.tag }}">{{ tc.tag }} <span class="tag-count">{{ tc.count }}</span></button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if collections.projects.length %}
|
||||
<ul class="post-list" id="projects-list">
|
||||
{% for project in collections.projects %}
|
||||
<li class="project-list-item"
|
||||
data-tags="{{ project.data.tags | join(' ') if project.data.tags else '' }}">
|
||||
<a href="#"
|
||||
class="project-open-item"
|
||||
data-project-url="/fragments/projects/{{ project.fileSlug }}/">{{ project.data.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p style="color:var(--p-dim);">No projects yet.</p>
|
||||
{% endif %}
|
||||
|
||||
<p style="margin-top:14px; border-top:1px solid var(--p-dim); padding-top:10px;">
|
||||
<a href="/boring/projects/">→ plaintext version</a>
|
||||
</p>
|
||||
141
src/index.njk
141
src/index.njk
|
|
@ -24,163 +24,40 @@ permalink: /
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{# ════════════════════════════════════════════════ #}
|
||||
{# ABOUT WINDOW #}
|
||||
{# ════════════════════════════════════════════════ #}
|
||||
<div class="window hidden"
|
||||
id="win-about"
|
||||
data-wid="win-about"
|
||||
data-title="$whoami"
|
||||
data-src="/fragments/about/"
|
||||
style="top:50px; left:120px; width:540px;">
|
||||
|
||||
<div class="window-content">
|
||||
{{ collections.aboutContent[0].templateContent | safe }}
|
||||
<p><a href="/boring/about/">→ plaintext version</a></p>
|
||||
</div>
|
||||
|
||||
<div class="window-content"></div>
|
||||
</div>
|
||||
|
||||
{# ════════════════════════════════════════════════ #}
|
||||
{# BLOG WINDOW #}
|
||||
{# ════════════════════════════════════════════════ #}
|
||||
<div class="window hidden"
|
||||
id="win-blog"
|
||||
data-wid="win-blog"
|
||||
data-title="blog/"
|
||||
data-src="/fragments/blog/"
|
||||
style="top:60px; left:400px; width:500px; height:460px;">
|
||||
|
||||
<div class="window-content" id="blog-window-content">
|
||||
|
||||
{# ── Listing view ─────────────────────────── #}
|
||||
<div id="blog-listing">
|
||||
|
||||
{# Tag filters #}
|
||||
{% set tagCounts = collections.posts | tagCounts %}
|
||||
{% if tagCounts.length %}
|
||||
<div class="blog-tags">
|
||||
<button class="blog-tag active" data-tag="*">all <span class="tag-count">{{ collections.posts.length }}</span></button>
|
||||
{% for tc in tagCounts %}
|
||||
<button class="blog-tag" data-tag="{{ tc.tag }}">{{ tc.tag }} <span class="tag-count">{{ tc.count }}</span></button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Post list #}
|
||||
{% if collections.posts.length %}
|
||||
<ul class="post-list" id="blog-post-list">
|
||||
{% for post in collections.posts %}
|
||||
<li class="post-list-item"
|
||||
data-tags="{{ post.data.tags | join(' ') if post.data.tags else '' }}">
|
||||
<a href="#"
|
||||
class="blog-open-post"
|
||||
data-post-idx="{{ loop.index0 }}">{{ post.data.title }}</a>
|
||||
<span class="post-date">{{ post.date | readableDate }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p style="color:var(--p-dim);">No posts yet — check back soon.</p>
|
||||
{% endif %}
|
||||
|
||||
<p style="margin-top:14px; border-top:1px solid var(--p-dim); padding-top:10px;">
|
||||
<a href="/boring/blog/">→ plaintext version</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{# ── Per-post views (built at compile time, hidden by default) ── #}
|
||||
{% for post in collections.posts %}
|
||||
<div class="blog-post-panel hidden" id="blog-post-{{ loop.index0 }}">
|
||||
<button class="blog-back">← back</button>
|
||||
<h2 style="margin-top:10px;">{{ post.data.title }}</h2>
|
||||
<p class="post-meta">
|
||||
{{ post.date | readableDate }}
|
||||
{% if post.data.tags %}· {{ post.data.tags | join(', ') }}{% endif %}
|
||||
</p>
|
||||
<hr class="post-rule">
|
||||
{{ post.templateContent | safe }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="window-content" id="blog-window-content"></div>
|
||||
</div>
|
||||
|
||||
{# ════════════════════════════════════════════════ #}
|
||||
{# PROJECTS WINDOW #}
|
||||
{# ════════════════════════════════════════════════ #}
|
||||
<div class="window hidden"
|
||||
id="win-projects"
|
||||
data-wid="win-projects"
|
||||
data-title="projects/"
|
||||
data-src="/fragments/projects/"
|
||||
style="top:140px; left:120px; width:560px; height:380px;">
|
||||
|
||||
<div class="window-content" id="projects-window-content">
|
||||
|
||||
{# ── Listing view ─────────────────────────── #}
|
||||
<div id="projects-listing">
|
||||
|
||||
{# Tag filters #}
|
||||
{% set projectTagCounts = collections.projects | tagCounts %}
|
||||
{% if projectTagCounts.length %}
|
||||
<div class="blog-tags">
|
||||
<button class="project-tag active" data-tag="*">all <span class="tag-count">{{ collections.projects.length }}</span></button>
|
||||
{% for tc in projectTagCounts %}
|
||||
<button class="project-tag" data-tag="{{ tc.tag }}">{{ tc.tag }} <span class="tag-count">{{ tc.count }}</span></button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Project list #}
|
||||
{% if collections.projects.length %}
|
||||
<ul class="post-list" id="projects-list">
|
||||
{% for project in collections.projects %}
|
||||
<li class="project-list-item"
|
||||
data-tags="{{ project.data.tags | join(' ') if project.data.tags else '' }}">
|
||||
<a href="#"
|
||||
class="project-open-item"
|
||||
data-project-idx="{{ loop.index0 }}">{{ project.data.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p style="color:var(--p-dim);">No projects yet.</p>
|
||||
{% endif %}
|
||||
|
||||
<p style="margin-top:14px; border-top:1px solid var(--p-dim); padding-top:10px;">
|
||||
<a href="/boring/projects/">→ plaintext version</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{# ── Per-project detail panels (built at compile time, hidden by default) ── #}
|
||||
{% for project in collections.projects %}
|
||||
<div class="project-detail-panel hidden" id="project-item-{{ loop.index0 }}">
|
||||
<button class="project-back">← back</button>
|
||||
<h2 style="margin-top:10px;">{{ project.data.title }}</h2>
|
||||
{% if project.data.tags %}
|
||||
<p class="post-meta">{{ project.data.tags | join(' · ') }}</p>
|
||||
{% endif %}
|
||||
<hr class="post-rule">
|
||||
{{ project.templateContent | safe }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="window-content" id="projects-window-content"></div>
|
||||
</div>
|
||||
|
||||
{# ════════════════════════════════════════════════ #}
|
||||
{# CONTACT WINDOW #}
|
||||
{# ════════════════════════════════════════════════ #}
|
||||
<div class="window hidden"
|
||||
id="win-contact"
|
||||
data-wid="win-contact"
|
||||
data-title="contact"
|
||||
data-src="/fragments/contact/"
|
||||
style="top:20px; left:240px; width:480px;">
|
||||
|
||||
<div class="window-content">
|
||||
{{ collections.contactContent[0].templateContent | safe }}
|
||||
<p style="margin-top:14px"><a href="/boring/contact/">→ plaintext version</a></p>
|
||||
</div>
|
||||
|
||||
<div class="window-content"></div>
|
||||
</div>
|
||||
|
||||
<script src="/assets/js/blog.js"></script>
|
||||
|
|
|
|||
4
src/projects/projects.11tydata.js
Normal file
4
src/projects/projects.11tydata.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
layout: "fragment.njk",
|
||||
permalink: data => `/fragments/projects/${data.page.fileSlug}/`
|
||||
};
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"permalink": false
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
---
|
||||
permalink: false
|
||||
title: "Test Project 1"
|
||||
tags:
|
||||
- x86
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
---
|
||||
permalink: false
|
||||
title: "Test Project 2"
|
||||
tags:
|
||||
- arm
|
||||
|
|
|
|||
Loading…
Reference in a new issue