Reorganize assets folder

This commit is contained in:
2026-05-08 08:34:11 +02:00
parent 892e4860b4
commit 69037a77ed
18 changed files with 16 additions and 16 deletions
+129
View File
@@ -0,0 +1,129 @@
function encodePickedPath(p) {
if (p === '/' || p === '') return '/';
return '/' + p.replace(/^\/+/, '').split('/').map(encodeURIComponent).join('/');
}
function promptPageName(title, initial, confirmLabel, onName) {
var input = document.createElement('input');
input.type = 'text';
input.className = 'modal-input';
input.placeholder = 'Page name';
if (initial) input.value = initial;
openModal({
title: title,
body: input,
confirm: {
label: confirmLabel,
onConfirm: function () {
var name = input.value.trim();
if (!name) return;
onName(name);
}
}
});
}
function newPage() {
var current = decodeURIComponent(window.location.pathname).replace(/\/+$/, '') || '/';
openTreePicker({
title: 'New page — where?',
mode: 'folder',
initialPath: current,
preselect: current,
hideFiles: true,
confirmLabel: 'NEXT',
onSelect: function (parentPath) {
promptPageName('New page — name?', '', 'CREATE', function (name) {
var base = parentPath === '/' ? '/' : encodePickedPath(parentPath) + '/';
window.location.href = base + encodeURIComponent(name) + '/?edit';
});
}
});
}
function movePage() {
var current = decodeURIComponent(window.location.pathname).replace(/\/+$/, '');
if (!current) return;
var segs = current.split('/').filter(Boolean);
var currentName = segs[segs.length - 1] || '';
var parent = '/' + segs.slice(0, -1).join('/');
if (parent === '/') parent = '/';
openTreePicker({
title: 'Move — new parent?',
mode: 'folder',
initialPath: parent,
preselect: parent,
hideFiles: true,
confirmLabel: 'NEXT',
onSelect: function (newParent) {
var input = document.createElement('input');
input.type = 'text';
input.className = 'modal-input';
input.placeholder = 'Page name';
input.value = currentName;
var linksCheckbox = document.createElement('input');
linksCheckbox.type = 'checkbox';
linksCheckbox.id = 'move-update-links';
var linksLabel = document.createElement('label');
linksLabel.htmlFor = linksCheckbox.id;
linksLabel.className = 'modal-checkbox';
linksLabel.appendChild(linksCheckbox);
linksLabel.appendChild(document.createTextNode('Update links'));
var body = document.createDocumentFragment();
body.appendChild(input);
body.appendChild(linksLabel);
openModal({
title: 'Move — new name?',
body: body,
confirm: {
label: 'MOVE',
onConfirm: function () {
var name = input.value.trim();
if (!name) return;
var dest = (newParent === '/' ? '' : newParent) + '/' + name;
var action = window.location.pathname + '?move=' +
encodeURIComponent(dest);
if (linksCheckbox.checked) action += '&links=1';
var form = document.createElement('form');
form.method = 'POST';
form.action = action;
document.body.appendChild(form);
form.submit();
}
}
});
}
});
}
function deletePage() {
var decodedPath = decodeURIComponent(window.location.pathname);
openModal({
title: 'Delete page',
body: 'Delete ' + decodedPath + ' and everything inside it?',
confirm: {
label: 'DELETE',
danger: true,
enterConfirms: false,
onConfirm: function () {
var form = document.createElement('form');
form.method = 'POST';
form.action = window.location.pathname + '?delete=1';
document.body.appendChild(form);
form.submit();
}
},
cancel: { autofocus: true },
swapButtons: true
});
}
document.addEventListener('DOMContentLoaded', function () {
wireDropdown(document.querySelector('[data-action="actions-drop"]'));
});
+17
View File
@@ -0,0 +1,17 @@
(function () {
var content = document.querySelector("main");
if (!content) return;
var headings = content.querySelectorAll("h2, h3, h4");
if (!headings) return
headings.forEach(function (h) {
if (!h.id) return;
var a = document.createElement('a');
a.href = '#' + h.id;
a.className = 'heading-anchor';
a.setAttribute('aria-label', 'Link to this section');
a.textContent = '#';
h.insertBefore(a, h.firstChild);
});
}());
+12
View File
@@ -0,0 +1,12 @@
(function () {
document.querySelectorAll('.content a[href^="http"]').forEach(function (a) {
var hostname = new URL(a.href).hostname;
var img = document.createElement('img');
img.src = 'https://icons.duckduckgo.com/ip3/' + hostname + '.ico';
img.width = 16;
img.height = 16;
img.style.verticalAlign = 'middle';
img.style.marginRight = '3px';
a.prepend(img);
});
})();
+52
View File
@@ -0,0 +1,52 @@
{{define "headScripts"}}<script src="/_/page/actions.js"></script>{{end}}
{{define "content"}}
{{if .Content}}
<div class="content">{{.Content}}</div>
{{end}}
{{if .SpecialContent}}
<div class="content">{{.SpecialContent}}</div>
{{end}}
{{if .Entries}}
<h2 id="files">Files</h2>
<div class="listing">
{{range .Entries}}
<div class="listing-item">
<span class="icon">{{.Icon}}</span>
<a href="{{.URL}}">{{.Name}}</a>
<span class="meta">{{.Meta}}</span>
</div>
{{end}}
</div>
{{else if not .Content}}
{{if not .SpecialContent}}
<p class="empty">Empty folder — <a href="?edit">[CREATE]</a></p>
{{end}}
{{end}}
{{if or .Content .SpecialContent}}
<script src="/_/page/content.js"></script>
<script src="/_/page/anchors.js"></script>
<script src="/_/page/toc.js"></script>
<script src="/_/page/tasks.js"></script>
{{end}}
{{if .Content}}
<script src="/_/page/sections.js"></script>
{{end}}
{{end}}
{{define "extras"}}
{{if .SidebarWidget}}{{.SidebarWidget}}{{end}}
{{if .CanEdit}}
<div class="fab dropdown">
<button class="btn btn-fab" data-action="actions-drop" title="Actions" aria-label="Actions"></button>
<div class="dropdown-menu align-right open-up">
<button class="btn dropdown-item" onclick="newPage()" title="New page (N)">NEW</button>
<a class="btn dropdown-item" href="?edit" title="Edit page (E)">EDIT</a>
{{if not .IsRoot}}
<button class="btn dropdown-item" onclick="movePage()" title="Move page (M)">MOVE</button>
<button class="btn dropdown-item danger" onclick="deletePage()" title="Delete page">DELETE</button>
{{end}}
</div>
</div>
{{end}}
{{end}}
+17
View File
@@ -0,0 +1,17 @@
(function () {
var content = document.querySelector('.content');
if (!content) return;
var headings = content.querySelectorAll('h1, h2, h3, h4, h5, h6');
if (!headings.length) return;
// Section 0 is pre-heading content, editable via full-page edit.
// Sections 1..N each start at a heading; that is the index sent to the server.
headings.forEach(function (h, i) {
var a = document.createElement('a');
a.href = '?edit&section=' + (i + 1);
a.className = 'btn btn-small';
a.textContent = 'edit';
h.appendChild(document.createTextNode(' '))
h.appendChild(a);
});
}());
+24
View File
@@ -0,0 +1,24 @@
(function () {
document.querySelectorAll('input.task-checkbox[data-task-index]').forEach(function (cb) {
cb.addEventListener('change', function () {
var idx = cb.dataset.taskIndex;
var checked = cb.checked;
cb.disabled = true;
fetch(window.location.pathname + '?toggle=' + idx, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'checked=' + checked
}).then(function (res) {
if (!res.ok) {
cb.checked = !checked;
alert('Failed to save task state (' + res.status + ')');
}
}).catch(function () {
cb.checked = !checked;
alert('Failed to save task state');
}).finally(function () {
cb.disabled = false;
});
});
});
})();
+59
View File
@@ -0,0 +1,59 @@
(function () {
var content = document.querySelector("main");
if (!content) return;
var headings = content.querySelectorAll("h2, h3, h4");
if (headings.length < 2) return;
var nav = document.createElement("nav");
nav.className = "toc";
var header = document.createElement("div");
header.className = "panel-header";
header.textContent = "Contents";
nav.appendChild(header);
var list = document.createElement("ul");
headings.forEach(function (h) {
if (!h.id) return;
var li = document.createElement("li");
li.className = "toc-" + h.tagName.toLowerCase();
var a = document.createElement("a");
a.href = "#" + h.id;
var clone = h.cloneNode(true);
clone.querySelectorAll(".btn, .muted, .heading-anchor").forEach(function (el) { el.remove(); });
a.textContent = clone.textContent.trim();
li.appendChild(a);
list.appendChild(li);
});
nav.appendChild(list);
var toggle = document.createElement("button");
toggle.type = "button";
toggle.className = "panel-toggle";
toggle.textContent = "Contents";
toggle.setAttribute("aria-expanded", "false");
toggle.addEventListener("click", function () {
var open = nav.classList.toggle("is-open");
toggle.setAttribute("aria-expanded", open ? "true" : "false");
});
var main = document.querySelector("main");
if (main) {
main.parentNode.insertBefore(toggle, main);
main.parentNode.insertBefore(nav, main);
} else {
document.body.appendChild(toggle);
document.body.appendChild(nav);
}
var pageHeader = document.querySelector("header");
function updateTop() {
if (!pageHeader || getComputedStyle(nav).position !== "fixed") return;
var rect = pageHeader.getBoundingClientRect();
nav.style.top = Math.max(8, rect.bottom + 8) + "px";
}
window.addEventListener("scroll", updateTop, { passive: true });
window.addEventListener("resize", updateTop);
updateTop();
})();