From e555d74b51ee4416c39b766abb8cb0738ec8d02a Mon Sep 17 00:00:00 2001 From: ruinivist Date: Tue, 27 Jan 2026 03:18:39 +0000 Subject: [PATCH] fix: fs tree min width to be enough to not have line splits --- internal/ui/fstree/model.go | 46 ++++++++++++++++++++++++++----------- layout.go | 10 ++++---- main.go | 16 +++++++++++-- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/internal/ui/fstree/model.go b/internal/ui/fstree/model.go index 87c4d4f..281ac37 100644 --- a/internal/ui/fstree/model.go +++ b/internal/ui/fstree/model.go @@ -73,18 +73,23 @@ type PerformActionMsg struct { // ==================== FsNode definition ==================== type FsTree struct { - Root *FsNode - lines map[int]*FsNode // flattened view of nodes for easy line access, map so that I can handle blank padding - SelectedNode *FsNode - hoveredNode *FsNode - ErrMsg string - height int - width int - viewStart int - viewEnd int - totalLines int - oldSelected *FsNode - startOffset int + Root *FsNode + lines map[int]*FsNode // flattened view of nodes for easy line access, map so that I can handle blank padding + SelectedNode *FsNode + hoveredNode *FsNode + ErrMsg string + height int + width int + viewStart int + viewEnd int + totalLines int + oldSelected *FsNode + startOffset int + maxContentWidth int +} + +func (t *FsTree) ContentWidth() int { + return t.maxContentWidth } // ==================== Bubble Tea Interface Implementation ==================== @@ -92,13 +97,18 @@ func (t *FsTree) Init() tea.Cmd { return nil } +type ContentSizeChangeMsg struct{} + func (t *FsTree) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch m := msg.(type) { case PerformActionMsg: err := t.PerformAction(m.Action, m.Name) if err != nil { t.ErrMsg = err.Error() + return t, nil } + // todo: standardise these messages + return t, func() tea.Msg { return ContentSizeChangeMsg{} } case tea.WindowSizeMsg: t.width = m.Width 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 func (t *FsTree) BuildLines() { + t.maxContentWidth = 0 t.lines = make(map[int]*FsNode) line := -1 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) { if node == nil { return @@ -475,6 +486,14 @@ func (t *FsTree) buildLinesRec(node *FsNode, depth int, currentLine *int, flatTr *flatTree = append(*flatTree, node) (*currentLine)++ + // update max width + if depth > 0 { + w := depth + 2 + len(node.FileName()) + if w > t.maxContentWidth { + t.maxContentWidth = w + } + } + if node.Expanded { for _, child := range node.Children { 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.BuildLines() + return nil } diff --git a/layout.go b/layout.go index 367c5d2..c6c78a2 100644 --- a/layout.go +++ b/layout.go @@ -2,7 +2,6 @@ package main // layout constants const ( - minFsTreeWidth = 5 minNoteViewWidth = 10 dividerWidth = 1 dragHitArea = 2 // +/- chars around divider @@ -11,9 +10,9 @@ const ( 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. -func calculateLayout(totalWidth, requestedTreeWidth int) (treeWidth, noteWidth int) { +func getUpdatedWindowSizes(totalWidth, requestedTreeWidth, minTreeWidth int) (treeWidth, noteWidth int) { treeWidth = requestedTreeWidth if treeWidth == 0 { @@ -26,8 +25,9 @@ func calculateLayout(totalWidth, requestedTreeWidth int) (treeWidth, noteWidth i if treeWidth > maxTreeWidth { treeWidth = maxTreeWidth } - if treeWidth < minFsTreeWidth { - treeWidth = minFsTreeWidth + + if treeWidth < minTreeWidth { + treeWidth = minTreeWidth } noteWidth = max(0, totalWidth-treeWidth-dividerWidth) diff --git a/main.go b/main.go index e2d58f4..e7240bc 100644 --- a/main.go +++ b/main.go @@ -79,7 +79,11 @@ func (m *model) loadTreeCmd(path string) tea.Cmd { func (m *model) layout(width, height int) { m.terminalWidth = width 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 if m.showStatusBar || m.inputMode { @@ -120,6 +124,8 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case treeLoadedMsg: m.tree = msg.tree 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{ Height: m.contentHeight, Width: m.fsTreeWidth, @@ -149,6 +155,12 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.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: m.inputMode = true 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) 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 return m, m.resizeChildren()