Commit a8a1af8
authored
* feat(#92): unified HeaderSearch + slide-down command palette
Foundation for issue #92: replace the centered Cmd+K modal and the
homepage DuckDuckGo box with a single viewport-centered HeaderSearch
input that drives three behaviours via one keystroke source.
Layout
------
- Header moves out of #main-content and spans the full viewport
(fixed top-0 inset-x-0). Sidebar now starts below the header
(top:55px). The Gearbox logo + sidebar-collapse toggle relocate
into the header so all chrome lives in a single bar across the
top of the page.
- Header is a 3-col CSS grid (1fr | auto | 1fr): chip+breadcrumb
on the left, HeaderSearch in the auto-sized middle column
(visually centered on the viewport rather than the content area),
per-page toolbar (#header-page-content) on the right.
HeaderSearch
------------
- New @HeaderSearch templ in base.templ + static/js/common/header-search.js.
- One input drives:
- search/filter mode (default): each keystroke fires the active
filter callback registered via window.gearbox.filter.register.
Enter submits; with no registered onSubmit it falls back to a
DuckDuckGo search in a new tab (preserves the old home-page
behaviour).
- palette mode (leading `>`): slide-down panel anchored to the
input lists boxes / gears / settings / global actions / per-gear
commands. Backspace from `>` returns to search mode.
- Cmd/Ctrl+K focuses the input and inserts `>`; `/` focuses the
input from anywhere; Esc exits palette / clears / blurs in turn.
- Magnifying glass ↔ chevron icon swap signals current mode.
- Narrow viewports collapse to a magnifying-glass button.
Command palette
---------------
- Rewrite command-palette.js to consume HeaderSearch instead of its
own input + centered overlay. Panel is fixed beneath the input,
no backdrop blur, no full-viewport dim — page remains interactive
(VS Code pattern).
- Per-gear commands surface via window.gearbox.commands.register
({id, label, group, run}); the palette groups them by `group`.
Help overlay
------------
- Full-window smoked-glass redesign: no panel/border/card, shortcuts
render in a centered two-column grid directly on the blurred
backdrop. Clicking anywhere on the backdrop closes; Esc and `?`
still work.
- Shortcut list updated for the new keys (`>` enters palette mode,
`/` focuses the search bar, etc.). The legacy filter-candidate
list in shortcut-help.js is gone — HeaderSearch owns `/`.
Registry API
------------
- New static/js/common/gear-commands.js exposes:
window.gearbox.commands.{register,clear,list,onChange,run}
window.gearbox.filter.{register,clear,current,onChange}
- HeaderSearch + command-palette subscribe to both via onChange so
they re-render the moment a gear's page script registers.
Refs #92
* feat(#92): migrate every gear's filter + toolbar to HeaderSearch/palette
Each gear's hand-rolled filter input is removed from its templ and
replaced with a window.gearbox.filter.register call in the page
script. Toolbar buttons + dropdowns gain palette entries via
window.gearbox.commands.register so they're reachable via Cmd+K → ">".
Home gear
- Drop the inline DuckDuckGo form (#home-search). Tile filtering
runs through the global HeaderSearch; non-matching tiles fade
(no GridStack re-layout). Enter opens the first matching tile,
or falls back to DuckDuckGo when nothing matches.
- Palette: "Add tile", "Toggle edit mode".
Logs gear (the issue's worked example)
- #filter-input + its X clear button removed from the templ;
filterLogs() reads __logFilterQuery instead.
- Palette: every log source option (16 sources on this install),
every line-count, every severity, plus Refresh / Copy /
Download / Toggle fullscreen — the same controls the keyboard-
averse user reaches by clicking.
Alerts, Services, HAProxy Overview
- Filter inputs removed from the templ; per-page apply* functions
read from a JS-managed query updated by the filter callback.
- Palette includes each status / health / disabled dropdown
option plus a refresh entry.
OS Updates, Bx (Fleet)
- #pkg-search and #bx-search become hidden inputs so the
Tabulator-backed createDataGrid({searchInput: '#…'}) keeps
latching onto them; the filter callback writes .value and
dispatches `input` so the existing datagrid pipeline is
untouched.
- Palette adds the view-filter dropdowns plus "Check for updates"
/ "Install package…" (OS Updates) and "Add box…" (Bx).
Traffic Visualization
- No text filter on this page; the view-mode dropdown stays and
each option becomes a palette entry, plus reset / toggle
physics / refresh.
Metrics, Certificates
- No filters to migrate. Palette gains "Toggle edit mode" +
"Refresh metrics" for Metrics and "Refresh certificate list"
for Certificates.
Refs #92
* fix(#92): HeaderSearch — drop gear placeholder, lock box width, remove chevron
Three feedback-driven tweaks after the first cut landed:
1. The per-gear placeholder ("Filter backends…", "Filter log lines…",
etc.) is gone. The box reads identically on every page: magnifier
icon + "/ to search · Cmd K for palette" hint chips. The hint
chips are the affordance — the box doesn't need a label that
changes per gear too. filter.placeholder is still accepted on the
API but no longer rendered; keeping the field so callers don't
break.
2. The header's grid template was `1fr auto 1fr`, which sized the
center column to its intrinsic content. Entering palette mode hid
the hint chips, shrank the intrinsic width, and the box visibly
narrowed. Switched to `1fr minmax(320px, 640px) 1fr` so the center
column has a guaranteed width regardless of inner content. Also
dropped the `focus-within:ring-2 focus-within:border-blue-500` on
the row — the focus ring added 2px of perceived width on focus
and the user wants the box style identical at all times.
3. The chevron-right icon I'd added for palette mode is gone. The
`>` character in the input is already the affordance; the extra
glyph was redundant. Now in palette mode the input shows JUST the
`>` text the user typed, no leading icon at all. Magnifier comes
back when palette mode exits.
Refs #92
* fix(#92): HeaderSearch — content-sized box, stable across modes, responsive
The box was a fixed 640px with a min of 320px in the grid template,
which was much too wide; the user asked for it to default to the size
needed to fit its contents and shrink on narrow viewports.
Three changes that together get the right behaviour:
1. Center grid column dropped back to `auto` (sizes to intrinsic
content) instead of `minmax(320px, 640px)`. The natural width at
wide viewports is now ~445px — icon + input + hint chips + clear
button + padding — exactly what the user wanted.
2. Width-preserving hide for the icon, hint chips, and clear button.
They use `invisible` (visibility:hidden) instead of `hidden`
(display:none), so they keep their layout slot when not rendered.
This was the missing piece behind every "box shrinks when I do X"
complaint:
- Idle vs typing: the clear button used to appear from display:none
on first keystroke and widen the row by ~24px.
- Search mode vs palette mode: the magnifier + chips used to leave
the flow entirely, shrinking the grid auto-column.
All three states now measure 445.36px at vw≥1024px and don't budge.
3. Hint chips responsive breakpoint moved from `sm:flex` to `lg:flex`.
At <1024px the chips drop out via real display:none, the grid
column shrinks to ~229px, and the user gets the "shrink some on
narrow viewports" behaviour they asked for. Below md (768px) the
row collapses entirely to a magnifying-glass button as before.
Also fixed a long-standing bug in the mobile expand/collapse flow:
the resize handler was unconditionally adding `flex` to the row at
md+, which overrode the responsive `hidden md:flex` rule and stuck
the row visible even after the viewport shrank back below md. Now
the row's display is owned entirely by `hidden md:flex`, and a
single .header-search-expanded marker class (with a matching CSS
rule in base.templ) drives the compact-overlay state on phones.
Refs #92
* fix(#92): HeaderSearch — hint chips overlay the input, not a separate slot
Issue: the hint chips were a flex sibling of the input, so the input
itself was only ~156px wide and the hint area was unusable for typing.
Even when focused, the user couldn't type into the right ~200px of the
box because that space belonged to the hint chips, not the input.
Fix: wrap the input in a `relative` div and absolutely position the
hint chips at the right edge of THAT wrapper, with
`pointer-events-none`. The input is now `w-full` inside the wrapper
and gets every pixel of horizontal space. The chips are an overlay
that fades in/out without affecting layout, GitHub-search style.
Visibility is now driven entirely by Tailwind peer modifiers on the
overlay (the JS hint-toggle is gone):
opacity-0 peer-placeholder-shown:opacity-100 peer-focus:opacity-0
- Idle (empty, not focused): chips visible.
- Focused (empty or not): chips fade out.
- Has content: chips fade out — including palette mode, where the
`>` character counts as content.
Also shrank the default box. The input wrapper uses
`min-w-[8rem] lg:min-w-[13rem]`, which sizes the row's intrinsic
content to ~280px at lg+ (down from ~445px) and ~200px at md.
The gap between the magnifier and the `/` chip is now ~10px instead
of ~80px, matching the user's "get rid of most of that" feedback.
Refs #92
* fix(#92): header grid — right column gets priority over left's slack
The grid was `1fr auto 1fr`, which gave the left and right columns
an equal share of remaining space regardless of content. On medium-
sized viewports that left wide empty space on the LEFT while the
RIGHT toolbar's per-page controls (selects + buttons) got clipped
because their column couldn't grow past its equal share.
Changed the template to:
minmax(0, 1fr) auto minmax(max-content, 1fr)
- LEFT → `minmax(0, 1fr)`: can absorb all slack OR shrink to 0.
The chip name + breadcrumb may clip on narrow viewports.
- CENTER → `auto`: the search box at its content width, unchanged.
- RIGHT → `minmax(max-content, 1fr)`: its full natural width is the
floor, so it never clips; beyond that it shares free space evenly
with the left so the search stays visually viewport-centered when
there's room.
Behaviour at three sample viewport widths (HAProxy gear, full
toolbar):
3000px left=1332 search=281 right=1332 → search 4px off center
1750px left=633 search=281 right=781 → right at max-content,
left absorbs ~250px slack
1280px left=163 search=281 right=781 → left clips
(chip+breadcrumb truncate)
right stays fully visible
Refs #92
* fix(#92): command palette — independent width on narrow viewports
When the viewport shrinks below md, the HeaderSearch row collapses
to a 34px magnifying-glass compact button. The palette positioner
was clamping its width to the search wrap, so on narrow viewports
the slide-down panel became an unusable ~34px vertical strip with
one-letter labels.
Decouple palette width from the search wrap:
width = clamp(360px, searchWrap.width, min(640px, viewport - 16))
Then re-anchor centered on the search input and clamp the resulting
left position so the panel stays inside the viewport with an 8px
gutter on each side.
At vw=875 the panel is 360px wide (MIN_W) and left-aligned near
the viewport edge; at vw=1500 it's its natural ~445px under the
search box; at vw≥1880 it caps at 640px MAX_W. The list rows now
read cleanly across the full responsive range.
Refs #92
* address Copilot review on #109
Six findings (4 inline + 2 in the review body, all valid):
1. `clear()` in header-search.js fired BOTH `filter.onInput('')` AND
`filter.onClear()`. Most gears were re-filtering twice; any side-
effectful onClear would run on top of the empty-input path. Pick
one path: prefer onClear if registered, otherwise dispatch empty
onInput. Removed the now-unused dispatchClear helper.
2. toggleSidebar() / applySidebarState() only updated the hidden
`#sidebar-icon-{collapse,expand}` shim left behind in the sidebar
for legacy code. The VISIBLE icons live in the new header toggle
(`#sidebar-icon-{collapse,expand}-header`) and were left untouched,
so clicking the toggle moved the sidebar but the icon never
swapped. Mirror the state to both pairs.
3. The `?` shortcut overlay is `role="dialog" aria-modal="true"` but
open() just removed `hidden` — focus stayed wherever it was, so
the dialog wasn't announced and keyboard focus could land outside
it. Make the overlay focusable on open and move focus into it;
restore focus to the prior element on close. No formal focus
trap is needed since the overlay has no interactive controls
(clicking anywhere closes).
4. The Overview gear restored the saved text filter by writing
__backendFilterQuery directly THEN calling HeaderSearch.setValue,
BEFORE registering the filter — so the bridge would never carry
restored state through the registered onInput. Swap the order:
register first, then setValue, and let the dispatch update the
query variable through the same code path keystrokes use.
5. The outside-click handler that collapses the compact mobile
search row checked `row.classList.contains('hidden')` to bail
when closed — but the row's templ markup is `hidden md:flex`, so
it ALWAYS carries `hidden` regardless of overlay state. The check
effectively disabled the feature. Use the .header-search-expanded
marker class instead, which the overlay state actually toggles.
6. The HeaderSearch templ doc-comment still described the original
"registered placeholder switches the input's placeholder" plan;
we dropped that earlier so the box reads the same on every page.
Updated the comment to match.
Verified in the browser: toggling the sidebar swaps the visible
header icon; clear() with both callbacks registered fires onClear
exactly once and onInput zero times; opening the help overlay moves
focus onto it.
Refs #92
1 parent 5837316 commit a8a1af8
18 files changed
Lines changed: 1738 additions & 667 deletions
File tree
- gearbox
- internal
- framework/templates
- layouts
- pages
- gears
- bx
- home
- static/js
- bx
- common
- gears
- os-updates
- traffic
Lines changed: 317 additions & 137 deletions
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
23 | 18 | | |
24 | 19 | | |
25 | 20 | | |
| |||
354 | 349 | | |
355 | 350 | | |
356 | 351 | | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
357 | 385 | | |
358 | 386 | | |
359 | 387 | | |
| |||
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
532 | 532 | | |
533 | 533 | | |
534 | 534 | | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
535 | 545 | | |
536 | 546 | | |
537 | 547 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
15 | 22 | | |
16 | 23 | | |
17 | 24 | | |
| |||
39 | 46 | | |
40 | 47 | | |
41 | 48 | | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
| 49 | + | |
| 50 | + | |
62 | 51 | | |
63 | 52 | | |
64 | 53 | | |
| |||
457 | 446 | | |
458 | 447 | | |
459 | 448 | | |
460 | | - | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
461 | 455 | | |
462 | | - | |
463 | | - | |
| 456 | + | |
464 | 457 | | |
465 | 458 | | |
466 | 459 | | |
| |||
752 | 745 | | |
753 | 746 | | |
754 | 747 | | |
755 | | - | |
756 | | - | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
757 | 753 | | |
758 | 754 | | |
759 | 755 | | |
760 | | - | |
761 | | - | |
762 | | - | |
763 | | - | |
764 | | - | |
765 | | - | |
766 | | - | |
767 | | - | |
768 | 756 | | |
769 | 757 | | |
770 | 758 | | |
| |||
786 | 774 | | |
787 | 775 | | |
788 | 776 | | |
789 | | - | |
790 | | - | |
791 | | - | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
792 | 781 | | |
793 | | - | |
794 | 782 | | |
795 | 783 | | |
796 | 784 | | |
| |||
1053 | 1041 | | |
1054 | 1042 | | |
1055 | 1043 | | |
| 1044 | + | |
| 1045 | + | |
| 1046 | + | |
| 1047 | + | |
| 1048 | + | |
| 1049 | + | |
| 1050 | + | |
| 1051 | + | |
| 1052 | + | |
| 1053 | + | |
| 1054 | + | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
| 1060 | + | |
| 1061 | + | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + | |
| 1065 | + | |
| 1066 | + | |
| 1067 | + | |
| 1068 | + | |
| 1069 | + | |
| 1070 | + | |
| 1071 | + | |
| 1072 | + | |
| 1073 | + | |
| 1074 | + | |
| 1075 | + | |
| 1076 | + | |
| 1077 | + | |
| 1078 | + | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
| 1083 | + | |
| 1084 | + | |
| 1085 | + | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
| 1105 | + | |
| 1106 | + | |
| 1107 | + | |
| 1108 | + | |
| 1109 | + | |
| 1110 | + | |
| 1111 | + | |
| 1112 | + | |
| 1113 | + | |
| 1114 | + | |
| 1115 | + | |
| 1116 | + | |
| 1117 | + | |
| 1118 | + | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
| 1122 | + | |
| 1123 | + | |
| 1124 | + | |
1056 | 1125 | | |
1057 | 1126 | | |
1058 | 1127 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2822 | 2822 | | |
2823 | 2823 | | |
2824 | 2824 | | |
| 2825 | + | |
| 2826 | + | |
| 2827 | + | |
| 2828 | + | |
| 2829 | + | |
| 2830 | + | |
| 2831 | + | |
| 2832 | + | |
| 2833 | + | |
| 2834 | + | |
| 2835 | + | |
| 2836 | + | |
| 2837 | + | |
| 2838 | + | |
| 2839 | + | |
| 2840 | + | |
| 2841 | + | |
| 2842 | + | |
| 2843 | + | |
| 2844 | + | |
| 2845 | + | |
2825 | 2846 | | |
2826 | 2847 | | |
2827 | 2848 | | |
| |||
Lines changed: 9 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
200 | 200 | | |
201 | 201 | | |
202 | 202 | | |
203 | | - | |
204 | | - | |
205 | | - | |
206 | | - | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
215 | 212 | | |
216 | 213 | | |
217 | 214 | | |
| |||
0 commit comments