Refactor Layout and improve search

This commit is contained in:
2026-04-29 19:26:01 +02:00
parent c688761e89
commit 86f2b7c34f
8 changed files with 286 additions and 309 deletions
+39
View File
@@ -0,0 +1,39 @@
{{define "headerActions"}}
<a class="btn" href="{{.PostURL}}">CANCEL</a>
<button class="btn" type="submit" form="edit-form" data-action="save" data-key="S" title="Save (S)">SAVE</button>
{{end}}
{{define "content"}}
<form id="edit-form" class="edit-form" method="POST" action="{{.PostURL}}">
{{if ge .SectionIndex 0}}<input type="hidden" name="section" value="{{.SectionIndex}}">{{end}}
<div class="editor-toolbar">
<button type="button" class="btn btn-tool" data-action="bold" data-key="B" title="Bold (B)">**</button>
<button type="button" class="btn btn-tool" data-action="italic" data-key="I" title="Italic (I)">*</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool" data-action="h1" data-key="1" title="Heading 1 (1)">#</button>
<button type="button" class="btn btn-tool" data-action="h2" data-key="2" title="Heading 2 (2)">##</button>
<button type="button" class="btn btn-tool" data-action="h3" data-key="3" title="Heading 3 (3)">###</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool" data-action="code" data-key="C" title="Inline code (C)">`</button>
<button type="button" class="btn btn-tool" data-action="codeblock" data-key="K" title="Code block (K)">```</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool" data-action="link" data-key="L" title="Link (L)">[]</button>
<button type="button" class="btn btn-tool" data-action="wikilink" data-key="P" title="Insert wiki link (P)">[[]]</button>
<button type="button" class="btn btn-tool" data-action="quote" data-key="Q" title="Blockquote (Q)">&gt;</button>
<button type="button" class="btn btn-tool" data-action="ul" data-key="U" title="Unordered list (U)">-</button>
<button type="button" class="btn btn-tool" data-action="ol" data-key="O" title="Ordered list (O)">1.</button>
<button type="button" class="btn btn-tool" data-action="hr" data-key="R" title="Horizontal rule (R)">---</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool dropdown" data-action="tbldrop" title="Table (T)">T▾</button>
<button type="button" class="btn btn-tool dropdown" data-action="datedrop" title="Insert date (D/W)">D▾</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool" data-action="movie" data-key="V" title="Import movie (V)">MV</button>
</div>
<textarea name="content" id="editor" autofocus>{{.RawContent}}</textarea>
</form>
<script src="/_/editor/lists.js"></script>
<script src="/_/editor/tables.js"></script>
<script src="/_/editor/dates.js"></script>
<script src="/_/editor/movie.js"></script>
<script src="/_/editor.js"></script>
{{end}}
+7
View File
@@ -14,6 +14,13 @@
e.preventDefault();
if (window.location.pathname !== '/' && typeof movePage === 'function') movePage();
break;
case 'F':
var input = document.querySelector('.search-input');
if (!input) return;
e.preventDefault();
input.focus();
input.select();
break;
}
});
})();
+37
View File
@@ -0,0 +1,37 @@
{{define "layout"}}<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{.Title}}</title>
<link rel="icon" href="/_/favicon.ico" />
<link rel="preload" href="/_/fonts/IosevkaEtoile.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/_/fonts/IosevkaSlab.woff2" as="font" type="font/woff2" crossorigin />
<link rel="stylesheet" href="/_/style.css" />
<script src="/_/modal.js"></script>
<script src="/_/global-shortcuts.js"></script>
<script src="/_/tree-picker.js"></script>
{{block "headScripts" .}}{{end}}
</head>
<body>
<header>
<nav class="breadcrumb">
<a href="/"><svg class="logo" viewBox="0 0 26.052269 26.052269" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="currentColor" stroke-linejoin="miter" transform="matrix(0.05463483,8.1519706e-6,-8.1519706e-6,0.05463483,-64.560546,-24.6949)"><rect x="1188.537" y="457.92056" width="461.87488" height="462.15189" stroke-width="20.2288"/><path d="m1348.9955 456.59572.046 309.36839" stroke-width="19.6849"/><path d="m1200.3996 765.80237 441.8362-.0659" stroke-width="19.6849"/><path d="m1648.2897 620.244-299.2012.0446" stroke-width="20.5676"/><path d="m1491.6148 909.24806-.021-136.93117" stroke-width="19.6849"/><rect x="1191.6504" y="461.66092" width="457.09634" height="457.09634" stroke-width="19.6761"/></g></svg></a>
{{range .Crumbs}}
<span class="sep">/</span><a href="{{.URL}}">{{.Name}}</a>
{{end}}
</nav>
{{if not .EditMode}}
<form class="search-form" action="/" method="get">
<input class="search-input" type="search" name="q" value="{{block "searchQuery" .}}{{end}}" placeholder="Search…" title="Search (F)" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
</form>
{{end}}
{{block "headerActions" .}}{{end}}
</header>
<main>
{{block "content" .}}{{end}}
</main>
{{block "extras" .}}{{end}}
</body>
</html>
{{end}}
+52 -115
View File
@@ -1,115 +1,52 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{.Title}}</title>
<link rel="icon" href="/_/favicon.ico" />
<link rel="preload" href="/_/fonts/IosevkaEtoile.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/_/fonts/IosevkaSlab.woff2" as="font" type="font/woff2" crossorigin />
<link rel="stylesheet" href="/_/style.css" />
<script src="/_/modal.js"></script>
<script src="/_/global-shortcuts.js"></script>
<script src="/_/tree-picker.js"></script>
<script src="/_/page-actions.js"></script>
</head>
<body>
<header>
<nav class="breadcrumb">
<a href="/"><svg class="logo" viewBox="0 0 26.052269 26.052269" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="currentColor" stroke-linejoin="miter" transform="matrix(0.05463483,8.1519706e-6,-8.1519706e-6,0.05463483,-64.560546,-24.6949)"><rect x="1188.537" y="457.92056" width="461.87488" height="462.15189" stroke-width="20.2288"/><path d="m1348.9955 456.59572.046 309.36839" stroke-width="19.6849"/><path d="m1200.3996 765.80237 441.8362-.0659" stroke-width="19.6849"/><path d="m1648.2897 620.244-299.2012.0446" stroke-width="20.5676"/><path d="m1491.6148 909.24806-.021-136.93117" stroke-width="19.6849"/><rect x="1191.6504" y="461.66092" width="457.09634" height="457.09634" stroke-width="19.6761"/></g></svg></a>
{{range .Crumbs}}
<span class="sep">/</span><a href="{{.URL}}">{{.Name}}</a>
{{end}}
</nav>
{{if not .EditMode}}
<form class="search-form" action="/" method="get">
<input class="search-input" type="search" name="q" placeholder="Search…" />
</form>
{{end}}
{{if .EditMode}}
<a class="btn" href="{{.PostURL}}">CANCEL</a>
<button class="btn" type="submit" form="edit-form" data-action="save" data-key="S" title="Save (S)">SAVE</button>
{{else if .CanEdit}}
<div class="dropdown">
<button class="btn" data-action="actions-drop" title="Actions">ACTIONS ▾</button>
<div class="dropdown-menu align-right">
<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}}
</header>
<main>
{{if .EditMode}}
<form id="edit-form" class="edit-form" method="POST" action="{{.PostURL}}">
{{if ge .SectionIndex 0}}<input type="hidden" name="section" value="{{.SectionIndex}}">{{end}}
<div class="editor-toolbar">
<button type="button" class="btn btn-tool" data-action="bold" data-key="B" title="Bold (B)">**</button>
<button type="button" class="btn btn-tool" data-action="italic" data-key="I" title="Italic (I)">*</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool" data-action="h1" data-key="1" title="Heading 1 (1)">#</button>
<button type="button" class="btn btn-tool" data-action="h2" data-key="2" title="Heading 2 (2)">##</button>
<button type="button" class="btn btn-tool" data-action="h3" data-key="3" title="Heading 3 (3)">###</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool" data-action="code" data-key="C" title="Inline code (C)">`</button>
<button type="button" class="btn btn-tool" data-action="codeblock" data-key="K" title="Code block (K)">```</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool" data-action="link" data-key="L" title="Link (L)">[]</button>
<button type="button" class="btn btn-tool" data-action="wikilink" data-key="P" title="Insert wiki link (P)">[[]]</button>
<button type="button" class="btn btn-tool" data-action="quote" data-key="Q" title="Blockquote (Q)">&gt;</button>
<button type="button" class="btn btn-tool" data-action="ul" data-key="U" title="Unordered list (U)">-</button>
<button type="button" class="btn btn-tool" data-action="ol" data-key="O" title="Ordered list (O)">1.</button>
<button type="button" class="btn btn-tool" data-action="hr" data-key="R" title="Horizontal rule (R)">---</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool dropdown" data-action="tbldrop" title="Table (T)">T▾</button>
<button type="button" class="btn btn-tool dropdown" data-action="datedrop" title="Insert date (D/W)">D▾</button>
<span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool" data-action="movie" data-key="V" title="Import movie (V)">MV</button>
</div>
<textarea name="content" id="editor" autofocus>{{.RawContent}}</textarea>
</form>
<script src="/_/editor/lists.js"></script>
<script src="/_/editor/tables.js"></script>
<script src="/_/editor/dates.js"></script>
<script src="/_/editor/movie.js"></script>
<script src="/_/editor.js"></script>
{{else}}
{{if .Content}}
<div class="content">{{.Content}}</div>
{{end}}
{{if .SpecialContent}}
<div class="content">{{.SpecialContent}}</div>
{{end}}
{{if or .Content .SpecialContent}}
<script src="/_/content.js"></script>
<script src="/_/anchors.js"></script>
<script src="/_/toc.js"></script>
{{end}}
{{if .Content}}
<script src="/_/sections.js"></script>
{{end}}
{{if .Entries}}
<div class="listing">
<div class="listing-header">Contents</div>
{{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}}
{{end}}
</main>
{{if .SidebarWidget}}{{.SidebarWidget}}{{end}}
</body>
</html>
{{define "headScripts"}}<script src="/_/page-actions.js"></script>{{end}}
{{define "headerActions"}}
{{if .CanEdit}}
<div class="dropdown">
<button class="btn" data-action="actions-drop" title="Actions">ACTIONS ▾</button>
<div class="dropdown-menu align-right">
<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}}
{{define "content"}}
{{if .Content}}
<div class="content">{{.Content}}</div>
{{end}}
{{if .SpecialContent}}
<div class="content">{{.SpecialContent}}</div>
{{end}}
{{if or .Content .SpecialContent}}
<script src="/_/content.js"></script>
<script src="/_/anchors.js"></script>
<script src="/_/toc.js"></script>
{{end}}
{{if .Content}}
<script src="/_/sections.js"></script>
{{end}}
{{if .Entries}}
<div class="listing">
<div class="listing-header">Contents</div>
{{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}}
{{end}}
{{define "extras"}}{{if .SidebarWidget}}{{.SidebarWidget}}{{end}}{{end}}
+22 -52
View File
@@ -1,52 +1,22 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Search{{if .Query}}: {{.Query}}{{end}}</title>
<link rel="icon" href="/_/favicon.ico" />
<link rel="preload" href="/_/fonts/IosevkaEtoile.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/_/fonts/IosevkaSlab.woff2" as="font" type="font/woff2" crossorigin />
<link rel="stylesheet" href="/_/style.css" />
</head>
<body>
<header>
<nav class="breadcrumb">
<a href="/"><svg class="logo" viewBox="0 0 26.052269 26.052269" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="currentColor" stroke-linejoin="miter" transform="matrix(0.05463483,8.1519706e-6,-8.1519706e-6,0.05463483,-64.560546,-24.6949)"><rect x="1188.537" y="457.92056" width="461.87488" height="462.15189" stroke-width="20.2288"/><path d="m1348.9955 456.59572.046 309.36839" stroke-width="19.6849"/><path d="m1200.3996 765.80237 441.8362-.0659" stroke-width="19.6849"/><path d="m1648.2897 620.244-299.2012.0446" stroke-width="20.5676"/><path d="m1491.6148 909.24806-.021-136.93117" stroke-width="19.6849"/><rect x="1191.6504" y="461.66092" width="457.09634" height="457.09634" stroke-width="19.6761"/></g></svg></a>
<span class="sep">/</span><span>search</span>
</nav>
<form class="search-form" action="/" method="get">
<input class="search-input" type="search" name="q" value="{{.Query}}" placeholder="Search folders…" autofocus />
<label class="search-toggle muted" title="Also search page contents">
<input type="checkbox" name="full" value="1" {{if .Full}}checked{{end}} />
full-text
</label>
<button class="btn" type="submit">GO</button>
</form>
</header>
<main>
{{if .Query}}
{{if .Results}}
<div class="listing">
<div class="listing-header">{{len .Results}} match{{if ne (len .Results) 1}}es{{end}} for &ldquo;{{.Query}}&rdquo;</div>
{{range .Results}}
<div class="listing-item">
<div class="search-result">
<div class="search-result-row">
<a href="{{.URL}}">{{.Name}}</a>
<span class="meta">{{.Path}}</span>
</div>
{{if .Snippet}}<div class="search-snippet muted">{{.Snippet}}</div>{{end}}
</div>
</div>
{{end}}
</div>
{{else}}
<p class="empty">No folders match &ldquo;{{.Query}}&rdquo;.</p>
{{end}}
{{else}}
<p class="empty">Enter a query above.</p>
{{end}}
</main>
</body>
</html>
{{define "searchQuery"}}{{.Query}}{{end}}
{{define "content"}}
{{if .Query}}
{{if .Results}}
<h2 class="muted search-summary">{{len .Results}} match{{if ne (len .Results) 1}}es{{end}} for &ldquo;{{.Query}}&rdquo;</h2>
<div class="search-results">
{{range .Results}}
<article class="search-card">
<a class="search-card-name" href="{{.URL}}">{{.Name}}</a>
<div class="search-card-path muted">/{{.Path}}</div>
{{if .Snippet}}<div class="search-card-snippet">{{.Snippet}}</div>{{end}}
</article>
{{end}}
</div>
{{else}}
<p class="empty">No matches for &ldquo;{{.Query}}&rdquo;.</p>
{{end}}
{{else}}
<p class="empty">Enter a query above.</p>
{{end}}
{{end}}
+27 -24
View File
@@ -379,37 +379,40 @@ textarea {
.search-input:focus {
border-color: var(--primary-hover);
}
.search-toggle {
display: flex;
align-items: center;
gap: 0.25rem;
white-space: nowrap;
.search-summary {
margin-bottom: 1rem;
}
.search-result {
.search-results {
display: flex;
flex-direction: column;
gap: 0.2rem;
flex: 1;
min-width: 0;
gap: 1rem;
}
.search-result-row {
.search-card {
display: flex;
align-items: center;
gap: 0.75rem;
min-width: 0;
flex-direction: column;
gap: 0.25rem;
padding-bottom: 1rem;
border-bottom: 1px dashed var(--secondary);
}
.search-result-row a {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.search-card:last-child {
border-bottom: none;
}
.search-snippet {
font-size: 0.8rem;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.search-card-name {
color: var(--link);
font-size: 1.1rem;
word-break: break-word;
}
.search-card-name:hover {
color: var(--link-hover);
}
.search-card-path {
word-break: break-all;
}
.search-card-snippet {
font-size: 0.9rem;
line-height: 1.5;
color: var(--text-muted);
margin-top: 0.25rem;
}
/* === Muted text === */