Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Make the palette the only search surface

1265ea552fb7346628c1e742a63c758c819379c5 · Keenan Nemetz · 10d ago · parent 6c03e6d3

Make the palette the only search surface

The full /search page repeated the palette's query and visibility rule, so
one of the two had to become the source of truth. Keep the palette: it
shows the first 30 matches, says when a query found more, and is reachable
from every page and with the keyboard shortcut.

The top bar no longer carries a Search destination or a form fallback; its
launcher is a plain button, and the palette dialog never navigates to a
page behind it.

server/web/templates/chrome.html +33−9

@@ -29,16 +29,15 @@
29 29 <nav class="topnav" aria-label="Global">
30 30 <a class="{{if eq .Section "home"}}active{{end}}" href="/"{{if eq .Section "home"}} aria-current="page"{{end}}>{{icon "house"}}Home</a>
31 31 <a class="{{if eq .Section "activity"}}active{{end}}" href="/activity"{{if eq .Section "activity"}} aria-current="page"{{end}}>{{icon "activity"}}Activity</a>
32 <a class="{{if eq .Section "search"}}active{{end}}" href="/search"{{if eq .Section "search"}} aria-current="page"{{end}}>{{icon "search"}}Search</a>
33 32 {{if .InstanceAdmin}}<a class="{{if eq .Section "users"}}active{{end}}" href="/users"{{if eq .Section "users"}} aria-current="page"{{end}}>{{icon "users"}}Accounts</a>{{end}}
34 33 </nav>
35 <form class="topsearch" method="get" action="/search" role="search">
36 <button type="submit" class="topsearch-box" data-palette-open aria-haspopup="dialog" aria-controls="search-palette" aria-keyshortcuts="Control+K Meta+K">
34 <div class="topsearch" role="search">
35 <button type="button" class="topsearch-box" data-palette-open aria-haspopup="dialog" aria-controls="search-palette" aria-keyshortcuts="Control+K Meta+K">
37 36 {{icon "search"}}
38 <span class="topsearch-label">Search issues, changes, comments</span>
37 <span class="topsearch-label">Search issues and changes</span>
39 38 <kbd class="topsearch-key" data-palette-key aria-hidden="true">Ctrl K</kbd>
40 39 </button>
41 </form>
40 </div>
42 41 <div class="topright">
43 42 {{if .LoggedIn}}
44 43 <details class="account-menu">
@@ -60,11 +59,11 @@
60 59 {{end}}
61 60 </div>
62 61 </header>
63 {{/* The global search is also a command palette: one field over every issue,
64 change request and comment the reader can see. Without JavaScript the top
65 bar's form still submits to /search; the dialog is the enhancement. */}}
62 {{/* The command palette is the whole search surface: one field over every
63 issue, change request and comment the reader can see. The launcher in the
64 global bar opens it; the dialog itself never navigates to a page. */}}
66 65 <dialog class="palette" id="search-palette" aria-label="Search issues, changes and comments">
67 <form class="palette-search" method="get" action="/search" role="search"
66 <form class="palette-search" role="search"
68 67 hx-get="/search/palette" hx-trigger="input changed delay:250ms, search"
69 68 hx-target="#palette-results" hx-swap="innerHTML" hx-sync="this:replace">
70 69 <span aria-hidden="true">{{icon "search"}}</span>
@@ -148,3 +147,28 @@
148 147 {{define "notfound"}}{{template "errorpage" .}}{{end}}
149 148
150 149 {{define "servererror"}}{{template "errorpage" .}}{{end}}
150
151 {{/* The command palette's initial state and the rows HTMX swaps in. The dialog
152 shell above carries both, so they live beside the chrome that owns them. */}}
153 {{define "palette_hint"}}
154 <p class="palette-empty" role="option" aria-disabled="true">Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.</p>
155 {{end}}
156
157 {{define "palette_results"}}
158 {{if .Message}}
159 <p class="palette-empty" role="option" aria-disabled="true">{{.Message}}</p>
160 {{else if not .Query}}
161 {{template "palette_hint"}}
162 {{else if .Results}}
163 {{range .Results}}
164 <a class="palette-row" data-palette-row role="option" aria-selected="false" href="{{.URL}}">
165 <span class="palette-row-title">{{.Title}}</span>
166 <span class="palette-row-where">{{.KindLabel}} · {{.Repo}}{{if .Number}} · <span class="sha">{{num .Number}}</span>{{end}}</span>
167 {{if .Snippet}}<span class="search-snippet">{{.Snippet}}</span>{{end}}
168 </a>
169 {{end}}
170 {{if .Limited}}<p class="palette-empty" role="option" aria-disabled="true">Showing the first results. Add a word to narrow the search.</p>{{end}}
171 {{else}}
172 <p class="palette-empty" role="option" aria-disabled="true">Nothing matches “{{.Query}}”.</p>
173 {{end}}
174 {{end}}