mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-13 09:06:27 -04:00
Add gui.shrinkSidePanelsToContent option (#5754)
Some checks are pending
Continuous Integration / ci - ${{matrix.os}} (~/.cache/go-build, ubuntu-latest) (push) Waiting to run
Continuous Integration / ci - ${{matrix.os}} (~\AppData\Local\go-build, windows-latest) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}} (2.32.0) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}} (2.38.2) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}} (2.44.0) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}} (latest) (push) Waiting to run
Continuous Integration / build (push) Waiting to run
Continuous Integration / check-codebase (push) Waiting to run
Continuous Integration / lint (push) Waiting to run
Continuous Integration / upload-coverage (push) Blocked by required conditions
Continuous Integration / check-for-fixups (push) Waiting to run
Codespell / Check for spelling errors (push) Waiting to run
Generate Sponsors README / deploy (push) Waiting to run
Some checks are pending
Continuous Integration / ci - ${{matrix.os}} (~/.cache/go-build, ubuntu-latest) (push) Waiting to run
Continuous Integration / ci - ${{matrix.os}} (~\AppData\Local\go-build, windows-latest) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}} (2.32.0) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}} (2.38.2) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}} (2.44.0) (push) Waiting to run
Continuous Integration / Integration Tests - git ${{matrix.git-version}} (latest) (push) Waiting to run
Continuous Integration / build (push) Waiting to run
Continuous Integration / check-codebase (push) Waiting to run
Continuous Integration / lint (push) Waiting to run
Continuous Integration / upload-coverage (push) Blocked by required conditions
Continuous Integration / check-for-fixups (push) Waiting to run
Codespell / Check for spelling errors (push) Waiting to run
Generate Sponsors README / deploy (push) Waiting to run
In many cases some of the side panels show a lot of empty space; for example the branches panel when there is only a `main` branch. This gets worse when `expandFocusedSidePanel` is on (accordion mode), in which case the almost-empty panel gets even bigger when focused and steals valuable space from the other panels that do have something to show. This PR adds a new `gui.shrinkSidePanelsToContent` option which causes side panels to never show more than their content (plus one blank line, so it's clear there's nothing more below). This means that panel heights are now dynamic and may change as their content changes; for example, when the working tree is clean the Files panel shows only two blank lines, but as you start changing file, it gets taller to show them. The option is off by default because it takes some getting used to. It is also independent of the `expandFocusedSidePanel` option; that one just makes the effect even more pronounced.
This commit is contained in:
commit
c085598100
|
|
@ -110,6 +110,11 @@ gui:
|
|||
# is true.
|
||||
expandedSidePanelWeight: 2
|
||||
|
||||
# If true, don't give a side panel more height than it needs to show its
|
||||
# content; when all panels fit, the leftover height is shared among them so that
|
||||
# they still fill the screen.
|
||||
shrinkSidePanelsToContent: false
|
||||
|
||||
# The side panels, in the order they appear from top to bottom.
|
||||
# Each entry is a list of one or more names that share a single panel as tabs
|
||||
# (cycle through them with the next-tab/previous-tab keys).
|
||||
|
|
|
|||
|
|
@ -111,6 +111,8 @@ type GuiConfig struct {
|
|||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
// The weight of the expanded side panel, relative to the other panels. 2 means twice as tall as the other panels. Only relevant if `expandFocusedSidePanel` is true.
|
||||
ExpandedSidePanelWeight int `yaml:"expandedSidePanelWeight"`
|
||||
// If true, don't give a side panel more height than it needs to show its content; when all panels fit, the leftover height is shared among them so that they still fill the screen.
|
||||
ShrinkSidePanelsToContent bool `yaml:"shrinkSidePanelsToContent"`
|
||||
// The side panels, in the order they appear from top to bottom.
|
||||
// Each entry is a list of one or more names that share a single panel as tabs (cycle through them with the next-tab/previous-tab keys).
|
||||
// Omit a name to hide it; give a name its own one-element list to promote a tab to a top-level panel.
|
||||
|
|
@ -847,18 +849,19 @@ func GetDefaultConfig() *UserConfig {
|
|||
func GetDefaultConfigForPlatform(platform string) *UserConfig {
|
||||
return &UserConfig{
|
||||
Gui: GuiConfig{
|
||||
ScrollHeight: 2,
|
||||
ScrollPastBottom: true,
|
||||
ScrollOffMargin: 2,
|
||||
ScrollOffBehavior: "margin",
|
||||
TabWidth: 4,
|
||||
MouseEvents: true,
|
||||
SkipAmendWarning: false,
|
||||
SkipDiscardChangeWarning: false,
|
||||
SkipStashWarning: false,
|
||||
SidePanelWidth: 0.3333,
|
||||
ExpandFocusedSidePanel: false,
|
||||
ExpandedSidePanelWeight: 2,
|
||||
ScrollHeight: 2,
|
||||
ScrollPastBottom: true,
|
||||
ScrollOffMargin: 2,
|
||||
ScrollOffBehavior: "margin",
|
||||
TabWidth: 4,
|
||||
MouseEvents: true,
|
||||
SkipAmendWarning: false,
|
||||
SkipDiscardChangeWarning: false,
|
||||
SkipStashWarning: false,
|
||||
SidePanelWidth: 0.3333,
|
||||
ExpandFocusedSidePanel: false,
|
||||
ExpandedSidePanelWeight: 2,
|
||||
ShrinkSidePanelsToContent: false,
|
||||
SidePanels: []SidePanel{
|
||||
{"status"},
|
||||
{"files", "worktrees", "submodules"},
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ type WindowArrangementArgs struct {
|
|||
// stash height special-cases key off (rather than the window itself, whose
|
||||
// name is just its first tab).
|
||||
ActiveViewForWindow func(window string) string
|
||||
// Returns the number of content lines of the view currently shown in the given
|
||||
// window. Used by the shrink-to-content feature to size a panel to its content.
|
||||
ContentHeightForWindow func(window string) int
|
||||
// Whether the main panel is split (as is the case e.g. when a file has both
|
||||
// staged and unstaged changes)
|
||||
SplitMainPanel bool
|
||||
|
|
@ -97,15 +100,18 @@ func (self *WindowArrangementHelper) GetWindowDimensions(informationStr string,
|
|||
CurrentWindow: self.c.Context().CurrentStatic().GetWindowName(),
|
||||
CurrentSideWindow: self.c.Context().CurrentSide().GetWindowName(),
|
||||
ActiveViewForWindow: self.windowHelper.GetViewNameForWindow,
|
||||
SplitMainPanel: repoState.GetSplitMainPanel(),
|
||||
ScreenMode: repoState.GetScreenMode(),
|
||||
AppStatus: appStatus,
|
||||
InformationStr: informationStr,
|
||||
ShowExtrasWindow: self.c.State().GetShowExtrasWindow(),
|
||||
InDemo: self.c.InDemo(),
|
||||
IsAnyModeActive: self.modeHelper.IsAnyModeActive(),
|
||||
InSearchPrompt: repoState.InSearchPrompt(),
|
||||
SearchPrefix: searchPrefix,
|
||||
ContentHeightForWindow: func(window string) int {
|
||||
return self.windowHelper.GetContextForWindow(window).TotalContentHeight()
|
||||
},
|
||||
SplitMainPanel: repoState.GetSplitMainPanel(),
|
||||
ScreenMode: repoState.GetScreenMode(),
|
||||
AppStatus: appStatus,
|
||||
InformationStr: informationStr,
|
||||
ShowExtrasWindow: self.c.State().GetShowExtrasWindow(),
|
||||
InDemo: self.c.InDemo(),
|
||||
IsAnyModeActive: self.modeHelper.IsAnyModeActive(),
|
||||
InSearchPrompt: repoState.InSearchPrompt(),
|
||||
SearchPrefix: searchPrefix,
|
||||
}
|
||||
|
||||
return GetWindowDimensions(args)
|
||||
|
|
@ -430,6 +436,15 @@ func sidePanelChildren(args WindowArrangementArgs) func(width int, height int) [
|
|||
return func(width int, height int) []*boxlayout.Box {
|
||||
windows := sideWindowNames(args.UserConfig)
|
||||
|
||||
// These thresholds were originally tuned for the default five side panels.
|
||||
// With fewer panels there's less to fit, so scale them down proportionally
|
||||
// to keep using the proportional layout at smaller heights rather than
|
||||
// squashing unnecessarily. We only ever scale down: making more panels
|
||||
// squash sooner tends to work against the reason people add panels.
|
||||
const defaultSidePanelCount = 5
|
||||
minHeightForNormalLayout := min(28, 28*len(windows)/defaultSidePanelCount)
|
||||
minHeightForTallSquashedPanels := min(21, 21*len(windows)/defaultSidePanelCount)
|
||||
|
||||
boxForEachWindow := func(boxForWindow func(window string) *boxlayout.Box) []*boxlayout.Box {
|
||||
boxes := make([]*boxlayout.Box, 0, len(windows))
|
||||
for _, window := range windows {
|
||||
|
|
@ -454,7 +469,13 @@ func sidePanelChildren(args WindowArrangementArgs) func(width int, height int) [
|
|||
}
|
||||
|
||||
return boxForEachWindow(fullHeightBox)
|
||||
} else if height >= 28 {
|
||||
} else if height >= minHeightForNormalLayout {
|
||||
if args.UserConfig.Gui.ShrinkSidePanelsToContent {
|
||||
if boxes, ok := shrinkToContentSidePanelBoxes(args, windows, height); ok {
|
||||
return boxes
|
||||
}
|
||||
}
|
||||
|
||||
accordionMode := args.UserConfig.Gui.ExpandFocusedSidePanel
|
||||
accordionBox := func(defaultBox *boxlayout.Box) *boxlayout.Box {
|
||||
if accordionMode && defaultBox.Window == args.CurrentSideWindow {
|
||||
|
|
@ -487,7 +508,7 @@ func sidePanelChildren(args WindowArrangementArgs) func(width int, height int) [
|
|||
}
|
||||
|
||||
squashedHeight := 1
|
||||
if height >= 21 {
|
||||
if height >= minHeightForTallSquashedPanels {
|
||||
squashedHeight = 3
|
||||
}
|
||||
|
||||
|
|
@ -508,3 +529,144 @@ func sidePanelChildren(args WindowArrangementArgs) func(width int, height int) [
|
|||
return boxForEachWindow(squashedSidePanelBox)
|
||||
}
|
||||
}
|
||||
|
||||
// shrinkToContentSidePanelBoxes implements the gui.shrinkSidePanelsToContent
|
||||
// feature: rather than giving every side panel an equal share of the height, we
|
||||
// size each panel to its own content (plus one blank line, so it's clear there's
|
||||
// nothing more below), which stops panels with little content from wasting space.
|
||||
//
|
||||
// The height freed up by a small panel flows to the panels that have more content
|
||||
// than their share; those grow up to their own content and then scroll. If every
|
||||
// panel fits its content with room to spare, there's nothing to absorb the
|
||||
// leftover, so it's shared among all panels by weight (which, in accordion mode,
|
||||
// gives the focused panel more of it).
|
||||
//
|
||||
// The status panel, and the stash panel when it's not focused, keep their
|
||||
// constant height and don't take part; ok is false when there are no panels to
|
||||
// size (so the caller falls back to the normal weighted layout).
|
||||
func shrinkToContentSidePanelBoxes(args WindowArrangementArgs, windows []string, height int) ([]*boxlayout.Box, bool) {
|
||||
const frameSize = 2
|
||||
|
||||
accordionMode := args.UserConfig.Gui.ExpandFocusedSidePanel
|
||||
|
||||
// A flexible panel is one we size to its content. Fixed panels (the status
|
||||
// panel, and the stash panel when unfocused) get their constant height and
|
||||
// are excluded from the distribution below.
|
||||
type flexiblePanel struct {
|
||||
boxIndex int
|
||||
desired int // target height: content rows (see below) plus the frame
|
||||
weight int
|
||||
height int // final height, only computed for the room-to-spare case
|
||||
capped bool // true once it fits its content within its share
|
||||
}
|
||||
|
||||
boxes := make([]*boxlayout.Box, len(windows))
|
||||
flexible := []*flexiblePanel{}
|
||||
availableForFlexible := height
|
||||
for i, window := range windows {
|
||||
focused := window == args.CurrentSideWindow
|
||||
|
||||
// The status and stash sizing is a property of those views, so we key off
|
||||
// the tab the window is currently showing, not the window's name (its first
|
||||
// tab); see the comment on normalBox in sidePanelChildren.
|
||||
activeView := args.ActiveViewForWindow(window)
|
||||
if activeView == "status" || (activeView == "stash" && !focused) {
|
||||
boxes[i] = &boxlayout.Box{Window: window, Size: 3}
|
||||
availableForFlexible -= 3
|
||||
continue
|
||||
}
|
||||
|
||||
weight := 1
|
||||
if accordionMode && focused {
|
||||
weight = args.UserConfig.Gui.ExpandedSidePanelWeight
|
||||
}
|
||||
// Show the content plus a blank line, so it's clear there's nothing more
|
||||
// below, but never fewer than two rows: a lone blank row looks cramped,
|
||||
// and an empty Files panel is the common state right after launching.
|
||||
contentRows := max(args.ContentHeightForWindow(window)+1, 2)
|
||||
flexible = append(flexible, &flexiblePanel{
|
||||
boxIndex: i,
|
||||
desired: contentRows + frameSize,
|
||||
weight: weight,
|
||||
})
|
||||
}
|
||||
|
||||
if len(flexible) == 0 || availableForFlexible <= 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Water-filling: repeatedly cap the panels whose desired height is no more
|
||||
// than their weighted share of what's left. Capping a panel only raises the
|
||||
// others' shares, so this converges once no further panel fits its content.
|
||||
// Whatever remains is what the still-uncapped panels have to share.
|
||||
remaining := availableForFlexible
|
||||
for {
|
||||
totalWeight := 0
|
||||
for _, p := range flexible {
|
||||
if !p.capped {
|
||||
totalWeight += p.weight
|
||||
}
|
||||
}
|
||||
if totalWeight == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
newlyCapped := []*flexiblePanel{}
|
||||
for _, p := range flexible {
|
||||
if !p.capped && p.desired*totalWeight <= remaining*p.weight {
|
||||
newlyCapped = append(newlyCapped, p)
|
||||
}
|
||||
}
|
||||
if len(newlyCapped) == 0 {
|
||||
break
|
||||
}
|
||||
for _, p := range newlyCapped {
|
||||
p.capped = true
|
||||
remaining -= p.desired
|
||||
}
|
||||
}
|
||||
|
||||
anyUncapped := false
|
||||
for _, p := range flexible {
|
||||
if !p.capped {
|
||||
anyUncapped = true
|
||||
}
|
||||
}
|
||||
|
||||
if anyUncapped {
|
||||
// Some panels have more content than fits: give the ones that fit exactly
|
||||
// their content, and let boxlayout share what's left among the rest by
|
||||
// weight (they'll scroll). This is the common, real-world case.
|
||||
for _, p := range flexible {
|
||||
if p.capped {
|
||||
boxes[p.boxIndex] = &boxlayout.Box{Window: windows[p.boxIndex], Size: p.desired}
|
||||
} else {
|
||||
boxes[p.boxIndex] = &boxlayout.Box{Window: windows[p.boxIndex], Weight: p.weight}
|
||||
}
|
||||
}
|
||||
return boxes, true
|
||||
}
|
||||
|
||||
// Every panel fits its content with room to spare, so no panel needs to
|
||||
// scroll. Share the leftover equally among them, regardless of focus and
|
||||
// accordion mode: enlarging the focused panel here reveals no more content
|
||||
// (it already fits) and would only make panels jump around as focus moves.
|
||||
// Deal out the rounding remainder one row at a time so the heights fill the
|
||||
// available space exactly.
|
||||
base := remaining / len(flexible)
|
||||
extra := remaining % len(flexible)
|
||||
for i, p := range flexible {
|
||||
p.height = p.desired + base
|
||||
if i < extra {
|
||||
p.height++
|
||||
}
|
||||
}
|
||||
|
||||
// boxlayout can't lay out a set of boxes that are all statically sized (it
|
||||
// needs a weighted box to absorb the space), so we hand it the heights as
|
||||
// weights: they sum to the available height, so it reproduces them exactly.
|
||||
for _, p := range flexible {
|
||||
boxes[p.boxIndex] = &boxlayout.Box{Window: windows[p.boxIndex], Weight: p.height}
|
||||
}
|
||||
return boxes, true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,14 @@ import (
|
|||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// contentHeights builds a ContentHeightForWindow function from a map of window
|
||||
// name to content height; windows not in the map report a height of 0.
|
||||
func contentHeights(heights map[string]int) func(window string) int {
|
||||
return func(window string) int {
|
||||
return heights[window]
|
||||
}
|
||||
}
|
||||
|
||||
// The best way to add test cases here is to set your args and then get the
|
||||
// test to fail and copy+paste the output into the test case's expected string.
|
||||
// TODO: add more test cases
|
||||
|
|
@ -710,6 +718,188 @@ func TestGetWindowDimensions(t *testing.T) {
|
|||
B: statusSpacer2
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "shrink to content, one panel overflows",
|
||||
mutateArgs: func(args *WindowArrangementArgs) {
|
||||
args.UserConfig.Gui.ShrinkSidePanelsToContent = true
|
||||
args.ContentHeightForWindow = contentHeights(map[string]int{
|
||||
"files": 2,
|
||||
"branches": 1,
|
||||
"commits": 100,
|
||||
})
|
||||
},
|
||||
expected: `
|
||||
╭status─────────────────╮╭main────────────────────────────────────────────╮
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭files──────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭branches───────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭commits────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭stash──────────────────╮│ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯╰────────────────────────────────────────────────╯
|
||||
<options──────────────────────────────────────────────────────>A<B────────>
|
||||
A: statusSpacer1
|
||||
B: information
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "shrink to content, everything fits with room to spare",
|
||||
mutateArgs: func(args *WindowArrangementArgs) {
|
||||
args.UserConfig.Gui.ShrinkSidePanelsToContent = true
|
||||
args.ContentHeightForWindow = contentHeights(map[string]int{
|
||||
"files": 2,
|
||||
"branches": 1,
|
||||
"commits": 3,
|
||||
})
|
||||
},
|
||||
expected: `
|
||||
╭status─────────────────╮╭main────────────────────────────────────────────╮
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭files──────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭branches───────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭commits────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭stash──────────────────╮│ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯╰────────────────────────────────────────────────╯
|
||||
<options──────────────────────────────────────────────────────>A<B────────>
|
||||
A: statusSpacer1
|
||||
B: information
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "shrink to content, accordion doesn't resize panels when everything fits",
|
||||
mutateArgs: func(args *WindowArrangementArgs) {
|
||||
args.UserConfig.Gui.ShrinkSidePanelsToContent = true
|
||||
args.UserConfig.Gui.ExpandFocusedSidePanel = true
|
||||
args.CurrentSideWindow = "branches"
|
||||
args.ContentHeightForWindow = contentHeights(map[string]int{
|
||||
"files": 2,
|
||||
"branches": 1,
|
||||
"commits": 3,
|
||||
})
|
||||
},
|
||||
expected: `
|
||||
╭status─────────────────╮╭main────────────────────────────────────────────╮
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭files──────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭branches───────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭commits────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭stash──────────────────╮│ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯╰────────────────────────────────────────────────╯
|
||||
<options──────────────────────────────────────────────────────>A<B────────>
|
||||
A: statusSpacer1
|
||||
B: information
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "shrink to content, empty panel keeps two rows rather than one",
|
||||
mutateArgs: func(args *WindowArrangementArgs) {
|
||||
args.UserConfig.Gui.ShrinkSidePanelsToContent = true
|
||||
args.ContentHeightForWindow = contentHeights(map[string]int{
|
||||
"files": 0,
|
||||
"branches": 1,
|
||||
"commits": 100,
|
||||
})
|
||||
},
|
||||
expected: `
|
||||
╭status─────────────────╮╭main────────────────────────────────────────────╮
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭files──────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭branches───────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭commits────────────────╮│ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯│ │
|
||||
╭stash──────────────────╮│ │
|
||||
│ ││ │
|
||||
╰───────────────────────╯╰────────────────────────────────────────────────╯
|
||||
<options──────────────────────────────────────────────────────>A<B────────>
|
||||
A: statusSpacer1
|
||||
B: information
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
|
|
|||
|
|
@ -590,6 +590,11 @@
|
|||
"description": "The weight of the expanded side panel, relative to the other panels. 2 means twice as tall as the other panels. Only relevant if `expandFocusedSidePanel` is true.",
|
||||
"default": 2
|
||||
},
|
||||
"shrinkSidePanelsToContent": {
|
||||
"type": "boolean",
|
||||
"description": "If true, don't give a side panel more height than it needs to show its content; when all panels fit, the leftover height is shared among them so that they still fill the screen.",
|
||||
"default": false
|
||||
},
|
||||
"sidePanels": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/SidePanel"
|
||||
|
|
|
|||
Loading…
Reference in a new issue