// SPDX-License-Identifier: GPL-3.0-or-later package model import ( "encoding/json" "os" "path/filepath" "krino/internal/xdg" ) // Prefs is how the window behaves - nothing about what krino does to // files, which belongs in the configuration. It lives beside krino.conf as // gui.json, a file krino itself never reads (his request, 2026-09-16). type Prefs struct { // Colours paints the configuration in the Text tab. Colours bool `json:"colours"` // Preview shows the file behind the selected row in the Plan tab. Preview bool `json:"preview"` // SelectAll starts a plan's rows checked, as the terminal review does. SelectAll bool `json:"select_all"` // PreviewHeight is how tall the preview under the explanation is, in // pixels; the divider between them sets it, and so does Settings. PreviewHeight int `json:"preview_height"` // PreviewWidth is the same measurement in the stacked layout, where // the preview sits beside the explanation rather than under it. PreviewWidth int `json:"preview_width"` // ListWidth and ListHeight are where the divider between the file list // and the explanation was left, in each layout. Every divider a hand // moves is kept (his request, 2026-09-17). ListWidth int `json:"list_width"` ListHeight int `json:"list_height"` // ShowSize, ShowAge and ShowRule are the columns that can be turned off // when the window is narrow or they are not wanted. ShowSize bool `json:"show_size"` ShowAge bool `json:"show_age"` ShowRule bool `json:"show_rule"` // Sort is the order a freshly scanned plan is read in; the picker in // the toolbar changes it for the window in front of you. Sort string `json:"sort"` // Layout is how the Plan tab is arranged: "side" puts the file list // beside the explanation, "stacked" puts it above, with the preview // beside the explanation underneath. Layout string `json:"layout"` } // The two arrangements of the Plan tab. const ( LayoutSide = "side" LayoutStacked = "stacked" ) // The sizes a window opens with until a divider is moved, and the floor a // smaller one is raised to. const ( DefaultPreviewHeight = 280 DefaultPreviewWidth = 360 DefaultListWidth = 780 DefaultListHeight = 420 ) // DefaultPrefs is what a window does before anything is chosen. func DefaultPrefs() Prefs { return Prefs{Colours: true, Preview: true, SelectAll: true, ShowSize: true, ShowAge: true, ShowRule: true, Sort: SortDefault, PreviewHeight: DefaultPreviewHeight, PreviewWidth: DefaultPreviewWidth, ListWidth: DefaultListWidth, ListHeight: DefaultListHeight, Layout: LayoutSide} } // PrefsFile is where they are kept. func PrefsFile() string { return filepath.Join(xdg.ConfigHome(), "krino", "gui.json") } // LoadPrefs reads them. A missing or unreadable file is not an error: the // window opens with the defaults, as it does the first time. func LoadPrefs() Prefs { p := DefaultPrefs() data, err := os.ReadFile(PrefsFile()) if err != nil { return p } if err := json.Unmarshal(data, &p); err != nil { return DefaultPrefs() } if p.PreviewHeight < 80 { p.PreviewHeight = DefaultPreviewHeight } if p.PreviewWidth < 80 { p.PreviewWidth = DefaultPreviewWidth } if p.Layout != LayoutSide && p.Layout != LayoutStacked { p.Layout = LayoutSide } if !IsSortOrder(p.Sort) { p.Sort = SortDefault } return p } // Save writes them, creating the directory if it is not there yet. func (p Prefs) Save() error { data, err := json.MarshalIndent(p, "", " ") if err != nil { return err } file := PrefsFile() if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil { return err } return os.WriteFile(file, append(data, '\n'), 0o644) }