init
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.env
|
||||||
28
go.mod
Normal file
28
go.mod
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
module filepass
|
||||||
|
|
||||||
|
go 1.26.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
charm.land/bubbles/v2 v2.1.0
|
||||||
|
charm.land/bubbletea/v2 v2.0.2
|
||||||
|
charm.land/lipgloss/v2 v2.0.2
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.2 // indirect
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||||
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
|
)
|
||||||
44
go.sum
Normal file
44
go.sum
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
charm.land/bubbles/v2 v2.1.0 h1:YSnNh5cPYlYjPxRrzs5VEn3vwhtEn3jVGRBT3M7/I0g=
|
||||||
|
charm.land/bubbles/v2 v2.1.0/go.mod h1:l97h4hym2hvWBVfmJDtrEHHCtkIKeTEb3TTJ4ZOB3wY=
|
||||||
|
charm.land/bubbletea/v2 v2.0.2 h1:4CRtRnuZOdFDTWSff9r8QFt/9+z6Emubz3aDMnf/dx0=
|
||||||
|
charm.land/bubbletea/v2 v2.0.2/go.mod h1:3LRff2U4WIYXy7MTxfbAQ+AdfM3D8Xuvz2wbsOD9OHQ=
|
||||||
|
charm.land/lipgloss/v2 v2.0.2 h1:xFolbF8JdpNkM2cEPTfXEcW1p6NRzOWTSamRfYEw8cs=
|
||||||
|
charm.land/lipgloss/v2 v2.0.2/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM=
|
||||||
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8=
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 h1:eyFRbAmexyt43hVfeyBofiGSEmJ7krjLOYt/9CF5NKA=
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
|
||||||
|
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
16
internal/pages/home.go
Normal file
16
internal/pages/home.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package pages
|
||||||
|
|
||||||
|
type HomePageMsg struct{}
|
||||||
|
|
||||||
|
type MenuItem struct {
|
||||||
|
Label string
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
|
||||||
|
func HomeMenuItems() []MenuItem {
|
||||||
|
return []MenuItem{
|
||||||
|
{Label: "Select Server", Key: "server"},
|
||||||
|
{Label: "Config", Key: "config"},
|
||||||
|
{Label: "Exit", Key: "exit"},
|
||||||
|
}
|
||||||
|
}
|
||||||
56
internal/services/config.go
Normal file
56
internal/services/config.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Host string `json:"host"`
|
||||||
|
User string `json:"user"`
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
Port string `json:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigService struct {
|
||||||
|
path string
|
||||||
|
servers map[string]Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigService() (*ConfigService, error) {
|
||||||
|
configDir, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := filepath.Join(configDir, "filepass")
|
||||||
|
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
path := filepath.Join(dir, "servers.json")
|
||||||
|
|
||||||
|
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
||||||
|
if err := os.WriteFile(path, []byte("{}"), 0o600); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var servers map[string]Server
|
||||||
|
if err := json.Unmarshal(data, &servers); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ConfigService{path: path, servers: servers}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConfigService) Servers() map[string]Server {
|
||||||
|
return c.servers
|
||||||
|
}
|
||||||
13
internal/services/services_store.go
Normal file
13
internal/services/services_store.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
type ServicesStore struct {
|
||||||
|
Config *ConfigService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServicesStore() (*ServicesStore, error) {
|
||||||
|
cfg, err := NewConfigService()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ServicesStore{Config: cfg}, nil
|
||||||
|
}
|
||||||
66
internal/styles/styles.go
Normal file
66
internal/styles/styles.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package styles
|
||||||
|
|
||||||
|
import lipgloss "charm.land/lipgloss/v2"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Card / box
|
||||||
|
CardStyle = lipgloss.NewStyle().
|
||||||
|
Border(lipgloss.RoundedBorder()).
|
||||||
|
BorderForeground(lipgloss.Color("62")).
|
||||||
|
Width(52)
|
||||||
|
|
||||||
|
CardInnerStyle = lipgloss.NewStyle().
|
||||||
|
Padding(1, 3)
|
||||||
|
|
||||||
|
CardTitleStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("86")).
|
||||||
|
Bold(true).
|
||||||
|
MarginBottom(1)
|
||||||
|
|
||||||
|
CardSubtitleStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("245")).
|
||||||
|
MarginBottom(1)
|
||||||
|
|
||||||
|
// Menu items
|
||||||
|
menuItemBase = lipgloss.NewStyle().
|
||||||
|
PaddingLeft(2).
|
||||||
|
Width(44)
|
||||||
|
|
||||||
|
menuItemActive = menuItemBase.
|
||||||
|
Foreground(lipgloss.Color("75")).
|
||||||
|
Bold(true).
|
||||||
|
SetString("▸ ")
|
||||||
|
|
||||||
|
menuItemInactive = menuItemBase.
|
||||||
|
Foreground(lipgloss.Color("245"))
|
||||||
|
|
||||||
|
// Status lines
|
||||||
|
StatusWarnStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("221")).
|
||||||
|
MarginTop(1)
|
||||||
|
|
||||||
|
StatusErrStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("203")).
|
||||||
|
MarginTop(1)
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
FooterStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("240")).
|
||||||
|
BorderTop(true).
|
||||||
|
BorderStyle(lipgloss.NormalBorder()).
|
||||||
|
BorderForeground(lipgloss.Color("237")).
|
||||||
|
Padding(0, 1).
|
||||||
|
Width(50)
|
||||||
|
|
||||||
|
FooterKeyStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("86")).Bold(true)
|
||||||
|
FooterSepStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("237"))
|
||||||
|
FooterDescStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("243"))
|
||||||
|
)
|
||||||
|
|
||||||
|
// MenuItemStyle returns the appropriate style for a menu row.
|
||||||
|
func MenuItemStyle(active bool) lipgloss.Style {
|
||||||
|
if active {
|
||||||
|
return menuItemActive
|
||||||
|
}
|
||||||
|
return menuItemInactive
|
||||||
|
}
|
||||||
22
internal/tui/init.go
Normal file
22
internal/tui/init.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"filepass/internal/pages"
|
||||||
|
"filepass/internal/services"
|
||||||
|
|
||||||
|
tea "charm.land/bubbletea/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type configLoadedMsg struct {
|
||||||
|
servers map[string]services.Server
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m TUIInterface) Init() tea.Cmd {
|
||||||
|
return tea.Batch(
|
||||||
|
func() tea.Msg { return pages.HomePageMsg{} },
|
||||||
|
func() tea.Msg {
|
||||||
|
return configLoadedMsg{servers: m.Services.Config.Servers()}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
25
internal/tui/tui.go
Normal file
25
internal/tui/tui.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"filepass/internal/pages"
|
||||||
|
"filepass/internal/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TUIInterface struct {
|
||||||
|
Services *services.ServicesStore
|
||||||
|
MenuItems []pages.MenuItem
|
||||||
|
Selected int
|
||||||
|
Servers map[string]services.Server
|
||||||
|
NoServers bool
|
||||||
|
InitErr error
|
||||||
|
Quitting bool
|
||||||
|
WindowWidth int
|
||||||
|
WindowHeight int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTUIInterface(store *services.ServicesStore) TUIInterface {
|
||||||
|
return TUIInterface{
|
||||||
|
Services: store,
|
||||||
|
MenuItems: pages.HomeMenuItems(),
|
||||||
|
}
|
||||||
|
}
|
||||||
51
internal/tui/update.go
Normal file
51
internal/tui/update.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"filepass/internal/pages"
|
||||||
|
|
||||||
|
tea "charm.land/bubbletea/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m TUIInterface) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case pages.HomePageMsg:
|
||||||
|
return m, nil
|
||||||
|
|
||||||
|
case configLoadedMsg:
|
||||||
|
if msg.err != nil {
|
||||||
|
m.InitErr = msg.err
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
m.Servers = msg.servers
|
||||||
|
m.NoServers = len(msg.servers) == 0
|
||||||
|
return m, nil
|
||||||
|
|
||||||
|
case tea.WindowSizeMsg:
|
||||||
|
m.WindowWidth = msg.Width
|
||||||
|
m.WindowHeight = msg.Height
|
||||||
|
return m, nil
|
||||||
|
|
||||||
|
case tea.KeyPressMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "up", "k":
|
||||||
|
if m.Selected > 0 {
|
||||||
|
m.Selected--
|
||||||
|
}
|
||||||
|
case "down", "j":
|
||||||
|
if m.Selected < len(m.MenuItems)-1 {
|
||||||
|
m.Selected++
|
||||||
|
}
|
||||||
|
case "enter":
|
||||||
|
if m.MenuItems[m.Selected].Key == "exit" {
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
// TODO: dispatch to server/config pages
|
||||||
|
case "ctrl+c", "esc":
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
91
internal/tui/view.go
Normal file
91
internal/tui/view.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"filepass/internal/styles"
|
||||||
|
|
||||||
|
tea "charm.land/bubbletea/v2"
|
||||||
|
lipgloss "charm.land/lipgloss/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func footerHint(key, desc string) string {
|
||||||
|
return styles.FooterKeyStyle.Render(key) +
|
||||||
|
" " +
|
||||||
|
styles.FooterDescStyle.Render(desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func footerSep() string {
|
||||||
|
return styles.FooterSepStyle.Render(" · ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m TUIInterface) View() tea.View {
|
||||||
|
if m.Quitting {
|
||||||
|
return tea.NewView("")
|
||||||
|
}
|
||||||
|
|
||||||
|
w := m.WindowWidth
|
||||||
|
h := m.WindowHeight
|
||||||
|
if w == 0 {
|
||||||
|
w = 80
|
||||||
|
}
|
||||||
|
if h == 0 {
|
||||||
|
h = 24
|
||||||
|
}
|
||||||
|
|
||||||
|
// menu rows
|
||||||
|
var menuRows []string
|
||||||
|
for i, item := range m.MenuItems {
|
||||||
|
menuRows = append(menuRows, styles.MenuItemStyle(i == m.Selected).Render(item.Label))
|
||||||
|
}
|
||||||
|
menu := lipgloss.JoinVertical(lipgloss.Left, menuRows...)
|
||||||
|
|
||||||
|
// status line — error takes priority over no-servers hint
|
||||||
|
var statusLine string
|
||||||
|
switch {
|
||||||
|
case m.InitErr != nil:
|
||||||
|
statusLine = styles.StatusErrStyle.Render("✗ " + m.InitErr.Error())
|
||||||
|
case m.NoServers:
|
||||||
|
statusLine = styles.StatusWarnStyle.Render("⚠ No servers configured. Select Config to add one.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// top content
|
||||||
|
innerRows := []string{
|
||||||
|
styles.CardTitleStyle.Render("✦ filepass"),
|
||||||
|
styles.CardSubtitleStyle.Render("Secure file transfer"),
|
||||||
|
menu,
|
||||||
|
}
|
||||||
|
if statusLine != "" {
|
||||||
|
innerRows = append(innerRows, statusLine)
|
||||||
|
}
|
||||||
|
topContent := styles.CardInnerStyle.Render(
|
||||||
|
lipgloss.JoinVertical(lipgloss.Left, innerRows...),
|
||||||
|
)
|
||||||
|
|
||||||
|
// footer
|
||||||
|
hints := footerHint("↑↓", "navigate") +
|
||||||
|
footerSep() +
|
||||||
|
footerHint("enter", "select") +
|
||||||
|
footerSep() +
|
||||||
|
footerHint("esc", "quit")
|
||||||
|
footer := styles.FooterStyle.Render(hints)
|
||||||
|
|
||||||
|
// card
|
||||||
|
card := styles.CardStyle.Render(
|
||||||
|
lipgloss.JoinVertical(lipgloss.Left,
|
||||||
|
topContent,
|
||||||
|
footer,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
cardHeight := lipgloss.Height(card)
|
||||||
|
topPad := max((h-cardHeight)/2, 0)
|
||||||
|
|
||||||
|
centeredCard := lipgloss.NewStyle().
|
||||||
|
Width(w).
|
||||||
|
Align(lipgloss.Center).
|
||||||
|
PaddingTop(topPad).
|
||||||
|
Render(card)
|
||||||
|
|
||||||
|
v := tea.NewView(centeredCard)
|
||||||
|
v.AltScreen = true
|
||||||
|
return v
|
||||||
|
}
|
||||||
26
main.go
Normal file
26
main.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"filepass/internal/services"
|
||||||
|
"filepass/internal/tui"
|
||||||
|
|
||||||
|
tea "charm.land/bubbletea/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
store, err := services.NewServicesStore()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "failed to initialise config:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := tui.NewTUIInterface(store)
|
||||||
|
p := tea.NewProgram(m)
|
||||||
|
if _, err := p.Run(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user