main 5041353

read_systemview_config.go RawHistoryFolder

1package usecase
2
3import (
4	"context"
5	"errors"
6
7	"github.com/bnema/dumber/internal/application/dto"
8	"github.com/bnema/dumber/internal/application/port"
9	"github.com/bnema/dumber/internal/domain/entity"
10)
11
12// ReadSystemviewConfigUseCase exposes read-side systemview config payloads.
13type ReadSystemviewConfigUseCase struct {
14	reader             port.SystemviewConfigReader
15	resolvedAppearance *systemviewResolvedAppearanceResolver
16}
17
18type systemviewResolvedAppearanceResolver struct {
19	externalTheme port.ExternalThemeSource
20	colorResolver port.ColorSchemeResolver
21	prefersDark   func() bool
22}
23
24// ReadSystemviewConfigOption customizes ReadSystemviewConfigUseCase behavior.
25type ReadSystemviewConfigOption func(*ReadSystemviewConfigUseCase)
26
27// WithSystemviewResolvedAppearance overlays the current config payload with a display-only
28// resolved appearance snapshot. It uses an isolated theme resolver per read so opening
29// dumb://config cannot mutate the live browser theme resolver's last-good cache.
30func WithSystemviewResolvedAppearance(
31	externalTheme port.ExternalThemeSource,
32	colorResolver port.ColorSchemeResolver,
33	prefersDark func() bool,
34) ReadSystemviewConfigOption {
35	return func(uc *ReadSystemviewConfigUseCase) {
36		uc.resolvedAppearance = &systemviewResolvedAppearanceResolver{
37			externalTheme: externalTheme,
38			colorResolver: colorResolver,
39			prefersDark:   prefersDark,
40		}
41	}
42}
43
44// NewReadSystemviewConfigUseCase creates a new read systemview config use case.
45func NewReadSystemviewConfigUseCase(reader port.SystemviewConfigReader, opts ...ReadSystemviewConfigOption) *ReadSystemviewConfigUseCase {
46	if reader == nil {
47		panic("NewReadSystemviewConfigUseCase: reader is nil")
48	}
49	uc := &ReadSystemviewConfigUseCase{reader: reader}
50	for _, opt := range opts {
51		if opt != nil {
52			opt(uc)
53		}
54	}
55	return uc
56}
57
58// ErrNilSystemviewConfigReader is returned when the config reader dependency is nil.
59var ErrNilSystemviewConfigReader = errors.New("systemview config reader is nil")
60
61// Current returns the current systemview config payload.
62func (uc *ReadSystemviewConfigUseCase) Current(ctx context.Context) (dto.SystemviewConfigPayload, error) {
63	if uc == nil || uc.reader == nil {
64		return dto.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader
65	}
66	payload, err := uc.reader.Current(ctx)
67	if err != nil {
68		return dto.SystemviewConfigPayload{}, err
69	}
70	return uc.withResolvedAppearance(ctx, payload), nil
71}
72
73// Default returns the default systemview config payload.
74func (uc *ReadSystemviewConfigUseCase) Default(ctx context.Context) (dto.SystemviewConfigPayload, error) {
75	if uc == nil || uc.reader == nil {
76		return dto.SystemviewConfigPayload{}, ErrNilSystemviewConfigReader
77	}
78	return uc.reader.Default(ctx)
79}
80
81func (uc *ReadSystemviewConfigUseCase) withResolvedAppearance(
82	ctx context.Context,
83	payload dto.SystemviewConfigPayload,
84) dto.SystemviewConfigPayload {
85	if uc == nil || uc.resolvedAppearance == nil {
86		return payload
87	}
88	resolved, ok := uc.resolvedAppearance.resolve(ctx, payload)
89	if !ok {
90		return payload
91	}
92	appearance := dto.WebUIAppearanceWithResolvedTheme(payload.Appearance, resolved)
93	payload.ResolvedAppearance = &appearance
94	return payload
95}
96
97func (r *systemviewResolvedAppearanceResolver) resolve(
98	ctx context.Context,
99	payload dto.SystemviewConfigPayload,
100) (entity.ResolvedTheme, bool) {
101	if r == nil {
102		return entity.ResolvedTheme{}, false
103	}
104	resolver := NewResolveThemeUseCase(r.externalTheme)
105	out, err := resolver.Execute(ctx, ResolveThemeInputFromConfig(
106		&payload.Appearance,
107		payload.DefaultUIScale,
108		nil,
109		r.preference(payload.Appearance.ColorScheme),
110	))
111	if err != nil {
112		return entity.ResolvedTheme{}, false
113	}
114	return out.Theme, true
115}
116
117func (r *systemviewResolvedAppearanceResolver) preference(colorScheme string) port.ColorSchemePreference {
118	fallback := port.ColorSchemePreference{PrefersDark: true, Source: "systemviews"}
119	if r.colorResolver != nil {
120		fallback = r.colorResolver.Resolve()
121	} else if r.prefersDark != nil {
122		fallback.PrefersDark = r.prefersDark()
123	}
124	return ColorSchemePreferenceFromConfig(colorScheme, fallback)
125}

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