@@ -47,6 +47,7 @@ import (
47 "github.com/charmbracelet/crush/internal/ui/util"
47 "github.com/charmbracelet/crush/internal/ui/util"
48 "github.com/charmbracelet/crush/internal/version"
48 "github.com/charmbracelet/crush/internal/version"
49 uv "github.com/charmbracelet/ultraviolet"
49 uv "github.com/charmbracelet/ultraviolet"
50+ "github.com/charmbracelet/ultraviolet/layout"
50 "github.com/charmbracelet/ultraviolet/screen"
51 "github.com/charmbracelet/ultraviolet/screen"
51 "github.com/charmbracelet/x/editor"
52 "github.com/charmbracelet/x/editor"
@@ -135,7 +136,7 @@ type UI struct {
135 // The width and height of the terminal in cells.
136 // The width and height of the terminal in cells.
136 width int
137 width int
137 height int
138 height int
138− layout layout
139+ layout uiLayout
140 isTransparent bool
141 isTransparent bool
@@ -2235,7 +2236,7 @@ func (m *UI) updateSize() {
2236// generateLayout calculates the layout rectangles for all UI components based
2237// generateLayout calculates the layout rectangles for all UI components based
2237// on the current UI state and terminal dimensions.
2238// on the current UI state and terminal dimensions.
2238−func (m *UI) generateLayout(w, h int) layout {
2239+func (m *UI) generateLayout(w, h int) uiLayout {
2239 // The screen area we're working with
2240 // The screen area we're working with
2240 area := image.Rect(0, 0, w, h)
2241 area := image.Rect(0, 0, w, h)
@@ -2256,7 +2257,7 @@ func (m *UI) generateLayout(w, h int) layout {
2258 // Add app margins
2259 // Add app margins
2259− appRect, helpRect := uv.SplitVertical(area, uv.Fixed(area.Dy()-helpHeight))
2260+ appRect, helpRect := layout.SplitVertical(area, layout.Fixed(area.Dy()-helpHeight))
2260 appRect.Min.Y += 1
2261 appRect.Min.Y += 1
2261 appRect.Max.Y -= 1
2262 appRect.Max.Y -= 1
2262 helpRect.Min.Y -= 1
2263 helpRect.Min.Y -= 1
@@ -2269,7 +2270,7 @@ func (m *UI) generateLayout(w, h int) layout {
2269 appRect.Max.X -= 1
2270 appRect.Max.X -= 1
2272− layout := layout{
2273+ uiLayout := uiLayout{
2273 area: area,
2274 area: area,
2274 status: helpRect,
2275 status: helpRect,
@@ -2285,9 +2286,9 @@ func (m *UI) generateLayout(w, h int) layout {
2285 // ------
2286 // ------
2288− headerRect, mainRect := uv.SplitVertical(appRect, uv.Fixed(landingHeaderHeight))
2289+ headerRect, mainRect := layout.SplitVertical(appRect, layout.Fixed(landingHeaderHeight))
2289− layout.header = headerRect
2290+ uiLayout.header = headerRect
2290− layout.main = mainRect
2291+ uiLayout.main = mainRect
2292 case uiLanding:
2293 case uiLanding:
2293 // Layout
2294 // Layout
@@ -2299,14 +2300,14 @@ func (m *UI) generateLayout(w, h int) layout {
2299 // editor
2300 // editor
2300 // ------
2301 // ------
2302− headerRect, mainRect := uv.SplitVertical(appRect, uv.Fixed(landingHeaderHeight))
2303+ headerRect, mainRect := layout.SplitVertical(appRect, layout.Fixed(landingHeaderHeight))
2303− mainRect, editorRect := uv.SplitVertical(mainRect, uv.Fixed(mainRect.Dy()-editorHeight))
2304+ mainRect, editorRect := layout.SplitVertical(mainRect, layout.Fixed(mainRect.Dy()-editorHeight))
2304 // Remove extra padding from editor (but keep it for header and main)
2305 // Remove extra padding from editor (but keep it for header and main)
2305 editorRect.Min.X -= 1
2306 editorRect.Min.X -= 1
2306 editorRect.Max.X += 1
2307 editorRect.Max.X += 1
2307− layout.header = headerRect
2308+ uiLayout.header = headerRect
2308− layout.main = mainRect
2309+ uiLayout.main = mainRect
2309− layout.editor = editorRect
2310+ uiLayout.editor = editorRect
2311 case uiChat:
2312 case uiChat:
2312 if m.isCompact {
2313 if m.isCompact {
@@ -2320,28 +2321,28 @@ func (m *UI) generateLayout(w, h int) layout {
2320 // ------
2321 // ------
2322 const compactHeaderHeight = 1
2323 const compactHeaderHeight = 1
2323− headerRect, mainRect := uv.SplitVertical(appRect, uv.Fixed(compactHeaderHeight))
2324+ headerRect, mainRect := layout.SplitVertical(appRect, layout.Fixed(compactHeaderHeight))
2324 detailsHeight := min(sessionDetailsMaxHeight, area.Dy()-1) // One row for the header
2325 detailsHeight := min(sessionDetailsMaxHeight, area.Dy()-1) // One row for the header
2325− sessionDetailsArea, _ := uv.SplitVertical(appRect, uv.Fixed(detailsHeight))
2326+ sessionDetailsArea, _ := layout.SplitVertical(appRect, layout.Fixed(detailsHeight))
2326− layout.sessionDetails = sessionDetailsArea
2327+ uiLayout.sessionDetails = sessionDetailsArea
2327− layout.sessionDetails.Min.Y += compactHeaderHeight // adjust for header
2328+ uiLayout.sessionDetails.Min.Y += compactHeaderHeight // adjust for header
2328 // Add one line gap between header and main content
2329 // Add one line gap between header and main content
2329 mainRect.Min.Y += 1
2330 mainRect.Min.Y += 1
2330− mainRect, editorRect := uv.SplitVertical(mainRect, uv.Fixed(mainRect.Dy()-editorHeight))
2331+ mainRect, editorRect := layout.SplitVertical(mainRect, layout.Fixed(mainRect.Dy()-editorHeight))
2331 mainRect.Max.X -= 1 // Add padding right
2332 mainRect.Max.X -= 1 // Add padding right
2332− layout.header = headerRect
2333+ uiLayout.header = headerRect
2333 pillsHeight := m.pillsAreaHeight()
2334 pillsHeight := m.pillsAreaHeight()
2334 if pillsHeight > 0 {
2335 if pillsHeight > 0 {
2335 pillsHeight = min(pillsHeight, mainRect.Dy())
2336 pillsHeight = min(pillsHeight, mainRect.Dy())
2336− chatRect, pillsRect := uv.SplitVertical(mainRect, uv.Fixed(mainRect.Dy()-pillsHeight))
2337+ chatRect, pillsRect := layout.SplitVertical(mainRect, layout.Fixed(mainRect.Dy()-pillsHeight))
2337− layout.main = chatRect
2338+ uiLayout.main = chatRect
2338− layout.pills = pillsRect
2339+ uiLayout.pills = pillsRect
2339 } else {
2340 } else {
2340− layout.main = mainRect
2341+ uiLayout.main = mainRect
2342 // Add bottom margin to main
2343 // Add bottom margin to main
2343− layout.main.Max.Y -= 1
2344+ uiLayout.main.Max.Y -= 1
2344− layout.editor = editorRect
2345+ uiLayout.editor = editorRect
2345 } else {
2346 } else {
2346 // Layout
2347 // Layout
@@ -2352,40 +2353,40 @@ func (m *UI) generateLayout(w, h int) layout {
2352 // ----------
2353 // ----------
2355− mainRect, sideRect := uv.SplitHorizontal(appRect, uv.Fixed(appRect.Dx()-sidebarWidth))
2356+ mainRect, sideRect := layout.SplitHorizontal(appRect, layout.Fixed(appRect.Dx()-sidebarWidth))
2356 // Add padding left
2357 // Add padding left
2357 sideRect.Min.X += 1
2358 sideRect.Min.X += 1
2358− mainRect, editorRect := uv.SplitVertical(mainRect, uv.Fixed(mainRect.Dy()-editorHeight))
2359+ mainRect, editorRect := layout.SplitVertical(mainRect, layout.Fixed(mainRect.Dy()-editorHeight))
2359 mainRect.Max.X -= 1 // Add padding right
2360 mainRect.Max.X -= 1 // Add padding right
2360− layout.sidebar = sideRect
2361+ uiLayout.sidebar = sideRect
2361 pillsHeight := m.pillsAreaHeight()
2362 pillsHeight := m.pillsAreaHeight()
2362 if pillsHeight > 0 {
2363 if pillsHeight > 0 {
2363 pillsHeight = min(pillsHeight, mainRect.Dy())
2364 pillsHeight = min(pillsHeight, mainRect.Dy())
2364− chatRect, pillsRect := uv.SplitVertical(mainRect, uv.Fixed(mainRect.Dy()-pillsHeight))
2365+ chatRect, pillsRect := layout.SplitVertical(mainRect, layout.Fixed(mainRect.Dy()-pillsHeight))
2365− layout.main = chatRect
2366+ uiLayout.main = chatRect
2366− layout.pills = pillsRect
2367+ uiLayout.pills = pillsRect
2367 } else {
2368 } else {
2368− layout.main = mainRect
2369+ uiLayout.main = mainRect
2370 // Add bottom margin to main
2371 // Add bottom margin to main
2371− layout.main.Max.Y -= 1
2372+ uiLayout.main.Max.Y -= 1
2372− layout.editor = editorRect
2373+ uiLayout.editor = editorRect
2376− if !layout.editor.Empty() {
2377+ if !uiLayout.editor.Empty() {
2377 // Add editor margins 1 top and bottom
2378 // Add editor margins 1 top and bottom
2378 if len(m.attachments.List()) == 0 {
2379 if len(m.attachments.List()) == 0 {
2379− layout.editor.Min.Y += 1
2380+ uiLayout.editor.Min.Y += 1
2381− layout.editor.Max.Y -= 1
2382+ uiLayout.editor.Max.Y -= 1
2384− return layout
2385+ return uiLayout
2387−// layout defines the positioning of UI elements.
2388+// uiLayout defines the positioning of UI elements.
2388−type layout struct {
2389+type uiLayout struct {
2389 // area is the overall available area.
2390 // area is the overall available area.
2390 area uv.Rectangle
2391 area uv.Rectangle