config.templ RawHistoryFolder

1package systemviews
2
3import (
4	"github.com/bnema/dumber/internal/application/dto"
5	"github.com/bnema/dumber/internal/application/port"
6)
7
8templ ConfigView(data configRenderData) {
9	@Alert("success", data.Notice)
10	@Alert("error", data.Error)
11	@Section("sv-config-appearance", "Appearance") {
12		@AppearanceForm(data.Config)
13	}
14	@Section("sv-config-search", "Search") {
15		@SearchSettings(data.Config)
16	}
17	@Section("sv-config-performance", "Performance") {
18		@PerformanceSettings(data.Config)
19	}
20	@Section("sv-config-keybindings", "Keybindings") {
21		@Meta(configKeybindingSummary(data.Keybindings))
22		@ConfigKeybindings(data.Keybindings)
23	}
24	@Section("sv-config-values", "Current payload") {
25		@KV(flattenValues(data.Config))
26	}
27}
28
29templ AppearanceForm(config dto.SystemviewConfigPayload) {
30	<form class="sv-card-form sv-config-form" data-sv-action="config.appearance.save">
31		<label><span>Sans font</span><input name="sans_font" type="text" value={ config.Appearance.SansFont } required/></label>
32		<label><span>Serif font</span><input name="serif_font" type="text" value={ config.Appearance.SerifFont } required/></label>
33		<label><span>Monospace font</span><input name="monospace_font" type="text" value={ config.Appearance.MonospaceFont } required/></label>
34		<label><span>GTK UI font</span><input name="gtk_font" type="text" value={ config.Appearance.GtkFont } required/></label>
35		<label><span>Default font size</span><input name="default_font_size" type="number" min="1" max="72" value={ formatConfigInt(config.Appearance.DefaultFontSize) } required/></label>
36		<label><span>UI scale</span><input name="default_ui_scale" type="number" min="0.5" max="3" step="0.05" value={ formatConfigFloat(config.DefaultUIScale) } required/></label>
37		<label><span>Color scheme</span><select name="color_scheme">
38			<option value="default" selected?={ colorSchemeSelected(config.Appearance.ColorScheme, "default") }>System default</option>
39			<option value="prefer-light" selected?={ colorSchemeSelected(config.Appearance.ColorScheme, "prefer-light") }>Prefer light</option>
40			<option value="prefer-dark" selected?={ colorSchemeSelected(config.Appearance.ColorScheme, "prefer-dark") }>Prefer dark</option>
41		</select></label>
42		<fieldset class="sv-fieldset">
43			<legend>External theme</legend>
44			<p class="sv-meta">Provider noctalia supports colors-json (default Noctalia colors.json) and dumber-json (advanced user template).</p>
45			<label><span>Enable external theme</span><input name="external_theme_enabled" type="checkbox" checked?={ config.Appearance.ExternalTheme.Enabled }/></label>
46			<label><span>Provider</span><input name="external_theme_provider" type="text" value={ config.Appearance.ExternalTheme.Provider } placeholder="noctalia"/></label>
47			<label><span>Format</span><input name="external_theme_format" type="text" value={ config.Appearance.ExternalTheme.Format } placeholder="colors-json"/></label>
48			<label><span>Path</span><input name="external_theme_path" type="text" value={ config.Appearance.ExternalTheme.Path } placeholder="~/.config/noctalia/colors.json"/></label>
49		</fieldset>
50		@PaletteFields("Light palette", "light", config.Appearance.LightPalette)
51		@PaletteFields("Dark palette", "dark", config.Appearance.DarkPalette)
52		<div class="sv-button-row sv-form-actions">
53			<button class="sv-button" type="submit">@ButtonIconLabel(iconSave, "Save appearance")</button>
54			<button class="sv-button sv-button-secondary" type="button" data-sv-action="config.appearance.reset" data-sv-confirm="Reset appearance settings to defaults?">@ButtonIconLabel(iconRotateCCW, "Reset appearance")</button>
55		</div>
56	</form>
57}
58
59templ PaletteFields(title string, prefix string, palette dto.ColorPalette) {
60	<fieldset class="sv-fieldset">
61		<legend>{ title }</legend>
62		<label><span>Background</span><input name={ prefix + "_background" } type="text" value={ palette.Background } required/></label>
63		<label><span>Surface</span><input name={ prefix + "_surface" } type="text" value={ palette.Surface } required/></label>
64		<label><span>Surface variant</span><input name={ prefix + "_surface_variant" } type="text" value={ palette.SurfaceVariant } required/></label>
65		<label><span>Text</span><input name={ prefix + "_text" } type="text" value={ palette.Text } required/></label>
66		<label><span>Muted</span><input name={ prefix + "_muted" } type="text" value={ palette.Muted } required/></label>
67		<label><span>Accent</span><input name={ prefix + "_accent" } type="text" value={ palette.Accent } required/></label>
68		<label><span>Border</span><input name={ prefix + "_border" } type="text" value={ palette.Border } required/></label>
69	</fieldset>
70}
71
72templ SearchSettings(config dto.SystemviewConfigPayload) {
73	<form class="sv-card-form" data-sv-action="config.search.save">
74		<label><span>Default search engine</span><input name="default_search_engine" type="text" value={ config.DefaultSearchEngine } placeholder="https://duckduckgo.com/?q=%s" required/></label>
75		<button class="sv-button" type="submit">@ButtonIconLabel(iconSave, "Save search default")</button>
76	</form>
77	<div class="sv-group">
78		<div class="sv-group-header"><h4>Search shortcuts</h4></div>
79		<p class="sv-meta">Shortcut URLs must include the %s placeholder for the search query.</p>
80		<form class="sv-inline-form" data-sv-action="config.searchShortcut.create">
81			<label><span>Key</span><input name="key" type="text" placeholder="ddg" required/></label>
82			<label><span>URL</span><input name="url" type="text" placeholder="https://duckduckgo.com/?q=%s" pattern=".*%s.*" required/></label>
83			<label><span>Description</span><input name="description" type="text" placeholder="DuckDuckGo"/></label>
84			<button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconPlus, "Add shortcut")</button>
85		</form>
86		{{ shortcuts := searchShortcutRows(config.SearchShortcuts) }}
87		if len(shortcuts) == 0 {
88			@EmptyState("No search shortcuts configured")
89		} else {
90			<ul class="sv-list">
91				for _, shortcut := range shortcuts {
92					@SearchShortcutItem(shortcut)
93				}
94			</ul>
95		}
96	</div>
97}
98
99templ SearchShortcutItem(shortcut searchShortcutRow) {
100	<li class="sv-list-row">
101		<form class="sv-inline-form" data-sv-action="config.searchShortcut.update">
102			<input type="hidden" name="key" value={ shortcut.Key }/>
103			<label><span>Key</span><input name="new_key" type="text" value={ shortcut.Key } required/></label>
104			<label><span>URL</span><input name="url" type="text" value={ shortcut.URL } pattern=".*%s.*" required/></label>
105			<label><span>Description</span><input name="description" type="text" value={ shortcut.Description }/></label>
106			<button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconSave, "Save")</button>
107			<button class="sv-button sv-button-secondary sv-button-danger sv-button-icon-only" type="button" data-sv-action="config.searchShortcut.delete" data-key={ shortcut.Key } data-sv-confirm="Delete this search shortcut?" aria-label={ "Delete search shortcut " + shortcut.Key }>@IconOnly(iconTrash, "Delete search shortcut " + shortcut.Key)</button>
108		</form>
109	</li>
110}
111
112templ PerformanceSettings(config dto.SystemviewConfigPayload) {
113	{{ customProfile := performanceProfileSelected(config.Performance.Profile, "custom") }}
114	<form class="sv-card-form sv-config-form" data-sv-action="config.performance.save" data-sv-performance-form>
115		<label><span>Profile</span><select name="profile" data-sv-performance-profile>
116			<option value="default" selected?={ performanceProfileSelected(config.Performance.Profile, "default") }>Default</option>
117			<option value="balanced" selected?={ performanceProfileSelected(config.Performance.Profile, "balanced") }>Balanced</option>
118			<option value="lite" selected?={ performanceProfileSelected(config.Performance.Profile, "lite") }>Lite</option>
119			<option value="max" selected?={ performanceProfileSelected(config.Performance.Profile, "max") }>Max</option>
120			<option value="custom" selected?={ performanceProfileSelected(config.Performance.Profile, "custom") }>Custom</option>
121		</select></label>
122		<p class="sv-meta">Choose Custom to edit individual performance values.</p>
123		<label><span>Skia CPU threads</span><input name="skia_cpu_threads" type="number" min="0" max="8" value={ formatConfigInt(config.Performance.Custom.SkiaCPUThreads) } disabled?={ !customProfile } data-sv-performance-custom/></label>
124		<label><span>Skia GPU threads</span><input name="skia_gpu_threads" type="number" min="-1" max="8" value={ formatConfigInt(config.Performance.Custom.SkiaGPUThreads) } disabled?={ !customProfile } data-sv-performance-custom/></label>
125		<label><span>Web process memory MB</span><input name="web_process_memory_mb" type="number" min="0" max="16384" value={ formatConfigInt(config.Performance.Custom.WebProcessMemoryMB) } disabled?={ !customProfile } data-sv-performance-custom/></label>
126		<label><span>Network process memory MB</span><input name="network_process_memory_mb" type="number" min="0" max="4096" value={ formatConfigInt(config.Performance.Custom.NetworkProcessMemoryMB) } disabled?={ !customProfile } data-sv-performance-custom/></label>
127		<label><span>WebView pool prewarm</span><input name="webview_pool_prewarm" type="number" min="0" max="20" value={ formatConfigInt(config.Performance.Custom.WebViewPoolPrewarm) } disabled?={ !customProfile } data-sv-performance-custom/></label>
128		<button class="sv-button" type="submit">@ButtonIconLabel(iconSave, "Save performance")</button>
129	</form>
130	<div class="sv-stat-grid">
131		<div class="sv-stat-card"><span class="sv-stat-value">{ formatConfigInt(config.Performance.Hardware.CPUThreads) }</span><span class="sv-stat-label">CPU threads</span></div>
132		<div class="sv-stat-card"><span class="sv-stat-value">{ formatConfigInt(config.Performance.Hardware.TotalRAMMB) }</span><span class="sv-stat-label">RAM MB</span></div>
133		<div class="sv-stat-card"><span class="sv-stat-value">{ config.Performance.Hardware.GPUName }</span><span class="sv-stat-label">GPU</span></div>
134		<div class="sv-stat-card"><span class="sv-stat-value">{ config.EngineType }</span><span class="sv-stat-label">Engine</span></div>
135	</div>
136	if !engineIsWebKit(config.EngineType) {
137		<p class="sv-meta">Performance tuning applies only to the WebKit engine.</p>
138	} else {
139		<p class="sv-meta">Performance profile changes may require restarting the browser to affect existing engine processes.</p>
140	}
141}
142
143templ ConfigKeybindings(keybindings port.KeybindingsConfig) {
144	{{ groups := configKeybindingGroups(keybindings) }}
145	if groups == nil {
146		@EmptyState("Keybindings unavailable")
147	} else if len(groups) == 0 {
148		@EmptyState("No keybindings configured")
149	} else {
150		<div class="sv-button-row">
151			<button class="sv-button sv-button-secondary sv-button-danger" type="button" data-sv-action="config.keybinding.resetAll" data-sv-confirm="Reset all keybindings to defaults?">@ButtonIconLabel(iconRotateCCW, "Reset all keybindings")</button>
152		</div>
153		for _, group := range groups {
154			@ConfigKeybindingGroup(group)
155		}
156	}
157}
158
159templ ConfigKeybindingGroup(group renderedKeybindingGroup) {
160	<div class="sv-group">
161		<div class="sv-group-header">
162			<div>
163				<h4>{ keybindingGroupTitle(group) }</h4>
164				if keybindingGroupMeta(group) != "" {
165					<p class="sv-meta">{ keybindingGroupMeta(group) }</p>
166				}
167			</div>
168		</div>
169		<div class="sv-group-body">
170			if len(group.Bindings) == 0 {
171				@EmptyState("No bindings in this group")
172			} else {
173				<ul class="sv-list">
174					for _, binding := range group.Bindings {
175						@ConfigKeybinding(group, binding)
176					}
177				</ul>
178			}
179		</div>
180	</div>
181}
182
183templ ConfigKeybinding(group renderedKeybindingGroup, binding renderedKeybinding) {
184	<li class="sv-list-row">
185		<div class="sv-keybinding-row">
186			<div>
187				<strong>{ keybindingDescription(binding) }</strong>
188				<p class="sv-meta">{ keybindingAction(binding) }</p>
189				<div class="sv-binding-keys">
190					@KeyList(binding.Keys)
191					<span class="sv-badge">{ keybindingStatus(binding) }</span>
192					if keybindingIsCustom(binding) && len(binding.DefaultKeys) > 0 {
193						<span class="sv-meta">default:</span>
194						@KeyList(binding.DefaultKeys)
195					}
196				</div>
197			</div>
198			<form class="sv-inline-form" data-sv-action="config.keybinding.set">
199				<input type="hidden" name="mode" value={ group.Mode }/>
200				<input type="hidden" name="action" value={ binding.Action }/>
201				<label><span>Keys</span><input name="keys" type="text" value={ keybindingKeysValue(binding.Keys) } placeholder="ctrl+h, ctrl+shift+h" required/></label>
202				<button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconSave, "Save")</button>
203				<button class="sv-button sv-button-secondary" type="button" data-sv-action="config.keybinding.reset" data-mode={ group.Mode } data-action={ binding.Action }>@ButtonIconLabel(iconRotateCCW, "Reset")</button>
204			</form>
205		</div>
206	</li>
207}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close