diff --git a/assets/global-shortcuts.js b/assets/global-shortcuts.js
index a6ff36a..0720fce 100644
--- a/assets/global-shortcuts.js
+++ b/assets/global-shortcuts.js
@@ -1,3 +1,10 @@
+function newPage() {
+ const name = prompt('New page name:');
+ if (!name || !name.trim()) return;
+ const slug = name.trim().replace(/\s+/g, '-');
+ window.location.href = window.location.pathname + slug + '/?edit';
+}
+
(function () {
document.addEventListener('keydown', function (e) {
if (!e.altKey || !e.shiftKey) return;
@@ -5,7 +12,11 @@
case 'E':
e.preventDefault();
window.location.href = window.location.pathname + '?edit';
- break;
+ break;
+ case 'N':
+ e.preventDefault();
+ newPage();
+ break;
}
});
})();
diff --git a/assets/page.html b/assets/page.html
index 18cb76c..541e09a 100644
--- a/assets/page.html
+++ b/assets/page.html
@@ -19,7 +19,8 @@
CANCEL
{{else if .CanEdit}}
- EDIT
+
+ EDIT
{{end}}
diff --git a/assets/style.css b/assets/style.css
index 85aba9f..bc3e427 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -102,6 +102,25 @@ header {
color: #ffd54f;
}
+.new-btn {
+ background: none;
+ border: none;
+ color: #ffb300;
+ font: inherit;
+ cursor: pointer;
+ padding: 0;
+ white-space: nowrap;
+}
+.new-btn::before {
+ content: "[";
+}
+.new-btn::after {
+ content: "]";
+}
+.new-btn:hover {
+ color: #ffd54f;
+}
+
/* === Main === */
main {
max-width: 860px;
@@ -313,6 +332,7 @@ textarea:focus {
color: #ffb300;
font: inherit;
cursor: pointer;
+ text-shadow: inherit;
padding: 0;
}
.btn-save::before {
@@ -330,6 +350,7 @@ textarea:focus {
border: none;
color: #ffb300;
font: inherit;
+ text-shadow: inherit;
cursor: pointer;
padding: 0;
text-decoration: none;
diff --git a/main.go b/main.go
index 3ea5363..200b363 100644
--- a/main.go
+++ b/main.go
@@ -86,7 +86,15 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
info, err := os.Stat(fsPath)
if err != nil {
- if os.IsNotExist(err) && strings.HasSuffix(r.URL.Path, "/") {
+ if os.IsNotExist(err) {
+ // Non-existent path: redirect GETs to the canonical slash form so
+ // the browser URL is consistent, then serve an empty folder page.
+ // POSTs must not be redirected — the form action has no trailing
+ // slash (path.Clean strips it) and the content would be lost.
+ if !strings.HasSuffix(r.URL.Path, "/") && r.Method != http.MethodPost {
+ http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently)
+ return
+ }
h.serveDir(w, r, urlPath, fsPath)
return
}