init: first commit
This commit is contained in:
parent
05583d9a5b
commit
c933393ac5
3
.gitignore
vendored
3
.gitignore
vendored
@ -21,3 +21,6 @@
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
# Bin
|
||||
himitsu
|
||||
|
||||
|
13
config/config.go
Normal file
13
config/config.go
Normal file
@ -0,0 +1,13 @@
|
||||
package config
|
||||
|
||||
import "himitsu/totp"
|
||||
|
||||
const Version = "v0.0.1"
|
||||
|
||||
var Config = []totp.TOTP{
|
||||
{
|
||||
Label: "Test",
|
||||
Secret: "Test",
|
||||
Account: "Test",
|
||||
},
|
||||
}
|
27
go.mod
Normal file
27
go.mod
Normal file
@ -0,0 +1,27 @@
|
||||
module himitsu
|
||||
|
||||
go 1.22.2
|
||||
|
||||
require (
|
||||
github.com/charmbracelet/bubbletea v0.25.0
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/muesli/termenv v0.15.2
|
||||
github.com/pquerna/otp v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.6 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/term v0.6.0 // indirect
|
||||
golang.org/x/text v0.3.8 // indirect
|
||||
)
|
48
go.sum
Normal file
48
go.sum
Normal file
@ -0,0 +1,48 @@
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
||||
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
63
main.go
Normal file
63
main.go
Normal file
@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"himitsu/config"
|
||||
"himitsu/ui/components"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
func main() {
|
||||
p := tea.NewProgram(initialModel())
|
||||
if _, err := p.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
tickMsg struct{}
|
||||
)
|
||||
|
||||
func tick() tea.Cmd {
|
||||
return tea.Tick(time.Second, func(time.Time) tea.Msg {
|
||||
return tickMsg{}
|
||||
})
|
||||
}
|
||||
|
||||
type model struct {
|
||||
}
|
||||
|
||||
func initialModel() model {
|
||||
return model{}
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return tick()
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.Type {
|
||||
case tea.KeyEnter, tea.KeyCtrlC, tea.KeyEsc:
|
||||
return m, tea.Quit
|
||||
}
|
||||
case tickMsg:
|
||||
return m, tick()
|
||||
}
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
tpl := components.VersionNumber() + "\n\n"
|
||||
for _, totp := range config.Config {
|
||||
tpl += components.TOTP(totp)
|
||||
}
|
||||
tpl += "\n" + components.KeybindsHints([]string{"esc: quit"}) + "\n"
|
||||
return fmt.Sprintf(tpl)
|
||||
}
|
22
totp/totp.go
Normal file
22
totp/totp.go
Normal file
@ -0,0 +1,22 @@
|
||||
package totp
|
||||
|
||||
import (
|
||||
"himitsu/utils"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
totpLib "github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
type TOTP struct {
|
||||
Label string
|
||||
Secret string
|
||||
Account string
|
||||
}
|
||||
|
||||
func GetCode(secret string) string {
|
||||
code, err := totpLib.GenerateCode(strings.ToUpper(strings.ReplaceAll(secret, " ", "")), time.Now())
|
||||
utils.CheckError(err)
|
||||
return code
|
||||
|
||||
}
|
59
ui/components/components.go
Normal file
59
ui/components/components.go
Normal file
@ -0,0 +1,59 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"himitsu/config"
|
||||
"himitsu/totp"
|
||||
"himitsu/ui/styling"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
func VersionNumber() string {
|
||||
return "himitsu " + styling.ColorFg(config.Version, styling.HighlightedColor)
|
||||
}
|
||||
|
||||
func Checkbox(label string, checked bool, selected bool) string {
|
||||
s := fmt.Sprintf("[ ] %s", label)
|
||||
if checked {
|
||||
s = "[x] " + label
|
||||
}
|
||||
if selected {
|
||||
return styling.ColorFg(s, styling.HighlightedColor)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func Progressbar(width int, percent float64) string {
|
||||
w := float64(width)
|
||||
|
||||
fullSize := int(math.Round(w * percent))
|
||||
var fullCells string
|
||||
for i := 0; i < fullSize; i++ {
|
||||
fullCells += termenv.String(styling.ProgressFullChar).Foreground(styling.Term.Color(styling.Ramp[i])).String()
|
||||
}
|
||||
|
||||
emptySize := int(w) - fullSize
|
||||
emptyCells := strings.Repeat(styling.ProgressEmpty, emptySize)
|
||||
|
||||
return fmt.Sprintf("%s%s %02ds", fullCells, emptyCells, 30-int(math.Round(percent*30)))
|
||||
}
|
||||
|
||||
func TOTP(currTotp totp.TOTP) string {
|
||||
s := fmt.Sprintf("╔═══════════════════════════════════════════════════════════════════╗\n║ %s %-25.25s %s %s ║\n╚═══════════════════════════════════════════════════════════════════╝\n", styling.ColorFg(fmt.Sprintf("%-15.15s", currTotp.Label), styling.HighlightedColor), currTotp.Account, totp.GetCode(currTotp.Secret), Progressbar(10, float64(time.Now().UnixMilli()%30000)/30000.))
|
||||
return s
|
||||
}
|
||||
|
||||
func KeybindsHints(keybinds []string) string {
|
||||
s := ""
|
||||
for index, keybind := range keybinds {
|
||||
if index != 0 {
|
||||
s += styling.Dot
|
||||
}
|
||||
s += styling.Subtle(keybind)
|
||||
}
|
||||
return s
|
||||
}
|
78
ui/styling/styling.go
Normal file
78
ui/styling/styling.go
Normal file
@ -0,0 +1,78 @@
|
||||
package styling
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/lucasb-eyer/go-colorful"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
const (
|
||||
HighlightedColor = "212"
|
||||
SecondaryColor = "225"
|
||||
)
|
||||
|
||||
const (
|
||||
ProgressBarWidth = 71
|
||||
ProgressFullChar = "█"
|
||||
ProgressEmptyChar = "░"
|
||||
)
|
||||
|
||||
var (
|
||||
Term = termenv.EnvColorProfile()
|
||||
Keyword = MakeFgStyle("211")
|
||||
Subtle = MakeFgStyle("241")
|
||||
ProgressEmpty = Subtle(ProgressEmptyChar)
|
||||
Dot = ColorFg(" • ", "236")
|
||||
|
||||
// Gradient colors we'll use for the progress bar
|
||||
Ramp = MakeRamp("#B14FFF", "#00FFA3", ProgressBarWidth)
|
||||
)
|
||||
|
||||
// Utils
|
||||
|
||||
// Color a string's foreground with the given value.
|
||||
func ColorFg(val, color string) string {
|
||||
return termenv.String(val).Foreground(Term.Color(color)).String()
|
||||
}
|
||||
|
||||
// Return a function that will colorize the foreground of a given string.
|
||||
func MakeFgStyle(color string) func(string) string {
|
||||
return termenv.Style{}.Foreground(Term.Color(color)).Styled
|
||||
}
|
||||
|
||||
// Color a string's foreground and background with the given value.
|
||||
func MakeFgBgStyle(fg, bg string) func(string) string {
|
||||
return termenv.Style{}.
|
||||
Foreground(Term.Color(fg)).
|
||||
Background(Term.Color(bg)).
|
||||
Styled
|
||||
}
|
||||
|
||||
// Generate a blend of colors.
|
||||
func MakeRamp(colorA, colorB string, steps float64) (s []string) {
|
||||
cA, _ := colorful.Hex(colorA)
|
||||
cB, _ := colorful.Hex(colorB)
|
||||
|
||||
for i := 0.0; i < steps; i++ {
|
||||
c := cA.BlendLuv(cB, i/steps)
|
||||
s = append(s, ColorToHex(c))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Convert a colorful.Color to a hexadecimal format compatible with termenv.
|
||||
func ColorToHex(c colorful.Color) string {
|
||||
return fmt.Sprintf("#%s%s%s", ColorFloatToHex(c.R), ColorFloatToHex(c.G), ColorFloatToHex(c.B))
|
||||
}
|
||||
|
||||
// Helper function for converting colors to hex. Assumes a value between 0 and
|
||||
// 1.
|
||||
func ColorFloatToHex(f float64) (s string) {
|
||||
s = strconv.FormatInt(int64(f*255), 16)
|
||||
if len(s) == 1 {
|
||||
s = "0" + s
|
||||
}
|
||||
return
|
||||
}
|
9
utils/utils.go
Normal file
9
utils/utils.go
Normal file
@ -0,0 +1,9 @@
|
||||
package utils
|
||||
|
||||
import "log"
|
||||
|
||||
func CheckError(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user