add view layout and a basic content render

This commit is contained in:
2026-01-04 01:42:08 +00:00
parent 6aed5f916a
commit b9d44c3bda
6 changed files with 323 additions and 193 deletions
+50 -20
View File
@@ -45,6 +45,11 @@ func (n *FsNode) FileName() string {
return filepath.Base(n.path)
}
// ================== messages ===================
type nodeSelected struct {
path string
}
// ==================== FsNode definition ====================
type FsTree struct {
root *FsNode
@@ -54,6 +59,9 @@ type FsTree struct {
errMsg string
height int
width int
viewStart int
viewEnd int
oldSelected *FsNode
}
// ==================== Bubble Tea Interface Implementation ====================
@@ -98,6 +106,10 @@ func (t *FsTree) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
case tea.MouseMsg:
m.Y += t.viewStart // adjust for viewport
if m.X >= t.width {
break
}
t.errMsg = ""
t.hoveredNode = t.lines[m.Y] // hover
@@ -112,9 +124,39 @@ func (t *FsTree) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
}
t.viewStart, t.viewEnd = t.getViewportBounds(len(t.lines))
if t.oldSelected != t.selectedNode {
t.oldSelected = t.selectedNode
return t, func() tea.Msg {
return nodeSelected{path: t.selectedNode.path}
}
}
return t, nil
}
func (t *FsTree) getViewportBounds(totalLines int) (startLine, endLine int) {
selectedLine := t.selectedNode.line
halfHeight := t.height / 2
startLine = selectedLine - halfHeight
if startLine < 0 {
startLine = 0
}
endLine = startLine + t.height
if endLine > totalLines {
endLine = totalLines
startLine = endLine - t.height
if startLine < 0 {
startLine = 0
}
}
return startLine, endLine
}
func (t *FsTree) View() string {
if t.errMsg != "" {
return t.errMsg
@@ -128,28 +170,15 @@ func (t *FsTree) View() string {
t.renderNode(t.root, 0, builder)
rendered := builder.String()
// clamp to height around selected node
lines := strings.Split(rendered, "\n")
totalLines := len(lines)
selectedLine := t.selectedNode.line
halfHeight := t.height / 2
startLine := selectedLine - halfHeight
if startLine < 0 {
startLine = 0
}
endLine := startLine + t.height
if endLine > totalLines {
endLine = totalLines
startLine = endLine - t.height
if startLine < 0 {
startLine = 0
}
}
clampedLines := lines[startLine:endLine]
clampedLines := lines[t.viewStart:t.viewEnd]
rendered = strings.Join(clampedLines, "\n")
rendered = lipgloss.NewStyle().
Width(t.width).
Render(rendered)
return rendered
}
@@ -363,12 +392,13 @@ func (t *FsTree) CreateNode(folder *FsNode, name string, nodeType FsNodeType) er
path := filepath.Join(folder.path, name)
// materialise it first
if nodeType == FileNode {
switch nodeType {
case FileNode:
err := createFile(path, []byte{})
if err != nil {
return err
}
} else if nodeType == FolderNode {
case FolderNode:
err := createFolder(path)
if err != nil {
return err