Add editor interface

This commit is contained in:
2026-04-10 09:53:02 +02:00
parent 5c577b3578
commit 06a3da1d54
4 changed files with 117 additions and 4 deletions

50
assets/editor.js Normal file
View File

@@ -0,0 +1,50 @@
(function () {
var textarea = document.getElementById('editor');
if (!textarea) return;
function wrap(before, after, placeholder) {
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
var selected = textarea.value.slice(start, end) || placeholder;
var replacement = before + selected + after;
textarea.value = textarea.value.slice(0, start) + replacement + textarea.value.slice(end);
if (selected === placeholder) {
textarea.selectionStart = start + before.length;
textarea.selectionEnd = start + before.length + placeholder.length;
} else {
textarea.selectionStart = start + replacement.length;
textarea.selectionEnd = start + replacement.length;
}
textarea.focus();
}
function linePrefix(prefix) {
var start = textarea.selectionStart;
var lineStart = textarea.value.lastIndexOf('\n', start - 1) + 1;
textarea.value = textarea.value.slice(0, lineStart) + prefix + textarea.value.slice(lineStart);
textarea.selectionStart = textarea.selectionEnd = start + prefix.length;
textarea.focus();
}
var actions = {
bold: function () { wrap('**', '**', 'bold text'); },
italic: function () { wrap('*', '*', 'italic text'); },
h1: function () { linePrefix('# '); },
h2: function () { linePrefix('## '); },
h3: function () { linePrefix('### '); },
code: function () { wrap('`', '`', 'code'); },
codeblock: function () { wrap('```\n', '\n```', 'code'); },
quote: function () { linePrefix('> '); },
link: function () { wrap('[', '](url)', 'link text'); },
ul: function () { linePrefix('- '); },
ol: function () { linePrefix('1. '); },
hr: function () { wrap('\n\n---\n\n', '', ''); },
};
document.querySelectorAll('[data-action]').forEach(function (btn) {
btn.addEventListener('click', function () {
var action = actions[btn.dataset.action];
if (action) action();
});
});
})();

View File

@@ -18,12 +18,30 @@
<main>
{{if .EditMode}}
<form class="edit-form" method="POST" action="{{.PostURL}}">
<textarea name="content" autofocus>{{.RawContent}}</textarea>
<div class="editor-toolbar">
<button type="button" class="btn-tool" data-action="bold" title="Bold">**</button>
<button type="button" class="btn-tool" data-action="italic" title="Italic">*</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn-tool" data-action="h1" title="Heading 1">#</button>
<button type="button" class="btn-tool" data-action="h2" title="Heading 2">##</button>
<button type="button" class="btn-tool" data-action="h3" title="Heading 3">###</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn-tool" data-action="code" title="Inline code">`</button>
<button type="button" class="btn-tool" data-action="codeblock" title="Code block">```</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn-tool" data-action="link" title="Link">[]</button>
<button type="button" class="btn-tool" data-action="quote" title="Blockquote">&gt;</button>
<button type="button" class="btn-tool" data-action="ul" title="Unordered list">-</button>
<button type="button" class="btn-tool" data-action="ol" title="Ordered list">1.</button>
<button type="button" class="btn-tool" data-action="hr" title="Horizontal rule">---</button>
</div>
<textarea name="content" id="editor" autofocus>{{.RawContent}}</textarea>
<div class="form-actions">
<a class="btn-cancel" href="{{.PostURL}}">CANCEL</a>
<button class="btn-save" type="submit">SAVE</button>
</div>
</form>
<script src="/_/editor.js"></script>
{{else}} {{if .Content}}
<div class="content">{{.Content}}</div>
{{end}} {{if .Entries}}

View File

@@ -233,11 +233,52 @@ main {
text-shadow: none;
}
/* === Editor toolbar === */
.editor-toolbar {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
border: 1px solid #0a0;
border-bottom: none;
padding: 0.4rem 0.6rem;
background: #001a00;
}
.btn-tool {
background: none;
border: none;
color: #0f0;
font: inherit;
font-size: 0.85rem;
cursor: pointer;
padding: 0 0.15rem;
text-shadow: 0 0 4px #0a0;
white-space: nowrap;
}
.btn-tool::before {
content: "[";
color: #060;
}
.btn-tool::after {
content: "]";
color: #060;
}
.btn-tool:hover {
color: #fff;
text-shadow: 0 0 6px #0f0;
}
.toolbar-sep {
width: 1px;
background: #060;
margin: 0 0.2rem;
align-self: stretch;
}
/* === Edit form === */
.edit-form {
display: flex;
flex-direction: column;
gap: 1rem;
}
textarea {
@@ -245,7 +286,10 @@ textarea {
min-height: 60vh;
background: #000;
border: 1px solid #0a0;
color: white;
border-top: none;
color: #0f0;
caret-color: #0f0;
text-shadow: 0 0 4px #0a0;
font: inherit;
font-size: 0.9rem;
line-height: 1.6;