fix: fs tree min width to be enough to not have line splits

This commit is contained in:
2026-01-27 03:18:39 +00:00
parent e7f450867b
commit e555d74b51
3 changed files with 52 additions and 20 deletions
+33 -13
View File
@@ -73,18 +73,23 @@ type PerformActionMsg struct {
// ==================== FsNode definition ==================== // ==================== FsNode definition ====================
type FsTree struct { type FsTree struct {
Root *FsNode Root *FsNode
lines map[int]*FsNode // flattened view of nodes for easy line access, map so that I can handle blank padding lines map[int]*FsNode // flattened view of nodes for easy line access, map so that I can handle blank padding
SelectedNode *FsNode SelectedNode *FsNode
hoveredNode *FsNode hoveredNode *FsNode
ErrMsg string ErrMsg string
height int height int
width int width int
viewStart int viewStart int
viewEnd int viewEnd int
totalLines int totalLines int
oldSelected *FsNode oldSelected *FsNode
startOffset int startOffset int
maxContentWidth int
}
func (t *FsTree) ContentWidth() int {
return t.maxContentWidth
} }
// ==================== Bubble Tea Interface Implementation ==================== // ==================== Bubble Tea Interface Implementation ====================
@@ -92,13 +97,18 @@ func (t *FsTree) Init() tea.Cmd {
return nil return nil
} }
type ContentSizeChangeMsg struct{}
func (t *FsTree) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (t *FsTree) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch m := msg.(type) { switch m := msg.(type) {
case PerformActionMsg: case PerformActionMsg:
err := t.PerformAction(m.Action, m.Name) err := t.PerformAction(m.Action, m.Name)
if err != nil { if err != nil {
t.ErrMsg = err.Error() t.ErrMsg = err.Error()
return t, nil
} }
// todo: standardise these messages
return t, func() tea.Msg { return ContentSizeChangeMsg{} }
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
t.width = m.Width t.width = m.Width
t.height = m.Height t.height = m.Height
@@ -446,6 +456,7 @@ func (t *FsTree) renderNode(node *FsNode, depth int, builder *strings.Builder) {
// builds a cache of line num to rendered node in view // builds a cache of line num to rendered node in view
func (t *FsTree) BuildLines() { func (t *FsTree) BuildLines() {
t.maxContentWidth = 0
t.lines = make(map[int]*FsNode) t.lines = make(map[int]*FsNode)
line := -1 line := -1
flatTree := make([]*FsNode, 0) flatTree := make([]*FsNode, 0)
@@ -461,7 +472,7 @@ func (t *FsTree) BuildLines() {
} }
} }
// Deprecated: isn't meant to be used directly // isn't meant to be used directly
func (t *FsTree) buildLinesRec(node *FsNode, depth int, currentLine *int, flatTree *[]*FsNode) { func (t *FsTree) buildLinesRec(node *FsNode, depth int, currentLine *int, flatTree *[]*FsNode) {
if node == nil { if node == nil {
return return
@@ -475,6 +486,14 @@ func (t *FsTree) buildLinesRec(node *FsNode, depth int, currentLine *int, flatTr
*flatTree = append(*flatTree, node) *flatTree = append(*flatTree, node)
(*currentLine)++ (*currentLine)++
// update max width
if depth > 0 {
w := depth + 2 + len(node.FileName())
if w > t.maxContentWidth {
t.maxContentWidth = w
}
}
if node.Expanded { if node.Expanded {
for _, child := range node.Children { for _, child := range node.Children {
t.buildLinesRec(child, depth+1, currentLine, flatTree) t.buildLinesRec(child, depth+1, currentLine, flatTree)
@@ -539,6 +558,7 @@ func (t *FsTree) CreateNode(folder *FsNode, name string, nodeType FsNodeType) er
} }
t.SelectedNode = newNode t.SelectedNode = newNode
t.BuildLines() t.BuildLines()
return nil return nil
} }
+5 -5
View File
@@ -2,7 +2,6 @@ package main
// layout constants // layout constants
const ( const (
minFsTreeWidth = 5
minNoteViewWidth = 10 minNoteViewWidth = 10
dividerWidth = 1 dividerWidth = 1
dragHitArea = 2 // +/- chars around divider dragHitArea = 2 // +/- chars around divider
@@ -11,9 +10,9 @@ const (
fsTreeWidthPercent = 15 fsTreeWidthPercent = 15
) )
// calculateLayout computes the widths for the file tree and note view // getUpdatedWindowSizes computes the widths for the file tree and note view
// based on the terminal dimensions and the desired tree width. // based on the terminal dimensions and the desired tree width.
func calculateLayout(totalWidth, requestedTreeWidth int) (treeWidth, noteWidth int) { func getUpdatedWindowSizes(totalWidth, requestedTreeWidth, minTreeWidth int) (treeWidth, noteWidth int) {
treeWidth = requestedTreeWidth treeWidth = requestedTreeWidth
if treeWidth == 0 { if treeWidth == 0 {
@@ -26,8 +25,9 @@ func calculateLayout(totalWidth, requestedTreeWidth int) (treeWidth, noteWidth i
if treeWidth > maxTreeWidth { if treeWidth > maxTreeWidth {
treeWidth = maxTreeWidth treeWidth = maxTreeWidth
} }
if treeWidth < minFsTreeWidth {
treeWidth = minFsTreeWidth if treeWidth < minTreeWidth {
treeWidth = minTreeWidth
} }
noteWidth = max(0, totalWidth-treeWidth-dividerWidth) noteWidth = max(0, totalWidth-treeWidth-dividerWidth)
+14 -2
View File
@@ -79,7 +79,11 @@ func (m *model) loadTreeCmd(path string) tea.Cmd {
func (m *model) layout(width, height int) { func (m *model) layout(width, height int) {
m.terminalWidth = width m.terminalWidth = width
m.terminalHeight = height m.terminalHeight = height
m.fsTreeWidth, m.noteViewWidth = calculateLayout(width, m.fsTreeWidth) minW := 0
if m.tree != nil {
minW = m.tree.ContentWidth()
}
m.fsTreeWidth, m.noteViewWidth = getUpdatedWindowSizes(width, m.fsTreeWidth, minW)
h := height h := height
if m.showStatusBar || m.inputMode { if m.showStatusBar || m.inputMode {
@@ -120,6 +124,8 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case treeLoadedMsg: case treeLoadedMsg:
m.tree = msg.tree m.tree = msg.tree
m.loading = false m.loading = false
m.fsTreeWidth = m.tree.ContentWidth()
m.fsTreeWidth, m.noteViewWidth = getUpdatedWindowSizes(m.terminalWidth, m.fsTreeWidth, m.tree.ContentWidth())
_, cmd := m.tree.Update(tea.WindowSizeMsg{ _, cmd := m.tree.Update(tea.WindowSizeMsg{
Height: m.contentHeight, Height: m.contentHeight,
Width: m.fsTreeWidth, Width: m.fsTreeWidth,
@@ -149,6 +155,12 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, cmd return m, cmd
} }
case fstree.ContentSizeChangeMsg:
// layout update needed, sent when a new note is created
m.fsTreeWidth, m.noteViewWidth = getUpdatedWindowSizes(m.terminalWidth, m.tree.ContentWidth(), m.tree.ContentWidth())
// TODO: this can be simplified a lot
return m, m.resizeChildren() // batches two updates
case fstree.RequestInputMsg: case fstree.RequestInputMsg:
m.inputMode = true m.inputMode = true
m.pendingAction = msg.Action m.pendingAction = msg.Action
@@ -250,7 +262,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.isHoveringDivider = isHoveringDivider(msg.X, m.fsTreeWidth) m.isHoveringDivider = isHoveringDivider(msg.X, m.fsTreeWidth)
if m.isDragging { if m.isDragging {
m.fsTreeWidth, m.noteViewWidth = calculateLayout(m.terminalWidth, msg.X) m.fsTreeWidth, m.noteViewWidth = getUpdatedWindowSizes(m.terminalWidth, msg.X, m.tree.ContentWidth())
// Update children with new sizes // Update children with new sizes
return m, m.resizeChildren() return m, m.resizeChildren()