Compare commits

..

No commits in common. "f69e6db582a09de8990b160ae296bc2ce8a15d41" and "7754b2c91c6227bd841acc86935f6343a318fe2d" have entirely different histories.

11 changed files with 186 additions and 424 deletions

View File

@ -1,11 +1,9 @@
package main package main
import ( import (
"whspbrd/internal/tui" "whspbrd/pkg/render_image"
//"whspbrd/pkg/render_image"
) )
func main() { func main() {
//config.NewConfigLoadTemplate() render_image.RenderImage("kogami-pf-edit.jpg", 0, 3, 20, 0, 1)
tui.Run()
} }

View File

@ -1,26 +0,0 @@
{
"messages" :
[
{
"id": "1",
"sender": "Alice",
"receiver": "bob",
"content": "SGVsbG8gQm9iLCBob3cgYXJlIHlvdT8K",
"timestamp": "2023-10-01T10:00:00Z"
},
{
"id": "2",
"sender": "bob",
"receiver": "Alice",
"content": "SGkgQWxpY2UhIEknbSBkb2luZyB3ZWxsLCB0aGFua3MuIEhvdyBhYm91dCB5b3U/Cg==",
"timestamp": "2023-10-01T10:05:00Z"
},
{
"id": "3",
"sender": "Alice",
"receiver": "bob",
"content": "SSdtIGdyZWF0LCB0aGFua3MgZm9yIGFza2luZwo=",
"timestamp": "2023-10-01T10:06:00Z"
}
]
}

View File

@ -1,26 +0,0 @@
{
"messages" :
[
{
"id": "1",
"sender": "Bob",
"receiver": "Filip",
"content": "SGVsbG8gQm9iLCBob3cgYXJlIHlvdT8K",
"timestamp": "2023-10-01T10:00:00Z"
},
{
"id": "2",
"sender": "Filip",
"receiver": "Bob",
"content": "SGkgQWxpY2UhIEknbSBkb2luZyB3ZWxsLCB0aGFua3MuIEhvdyBhYm91dCB5b3U/Cg==",
"timestamp": "2023-10-01T10:05:00Z"
},
{
"id": "3",
"sender": "Bob",
"receiver": "FIlip",
"content": "SSdtIGdyZWF0LCB0aGFua3MgZm9yIGFza2luZwo=",
"timestamp": "2023-10-01T10:06:00Z"
}
]
}

View File

@ -1,18 +0,0 @@
package config
import (
"embed"
"fmt"
)
//go:embed config.json
var configuration embed.FS
func NewConfigLoadTemplate() {
config, err := configuration.ReadFile("config.json")
if err != nil {
panic(err)
}
fmt.Println(string(config))
}

View File

@ -1,74 +0,0 @@
package tui
import (
"fmt"
"log"
"whspbrd/pkg/cell_size"
"whspbrd/pkg/render_image"
"github.com/jroimartin/gocui"
)
func layoutChat(g *gocui.Gui, maxX, maxY int) error {
if v, err := g.SetView("chat", 21, 0, maxX-1, maxY-5); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Chat"
v.Wrap = true
v.Autoscroll = true
updateChatView(v)
}
return nil
}
func layoutInput(g *gocui.Gui, maxX, maxY int) error {
if v, err := g.SetView("input", 21, maxY-4, maxX-1, maxY-1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Type your message"
v.Editable = true
v.Wrap = true
if _, err := g.SetCurrentView("input"); err != nil {
return err
}
}
return nil
}
func updateChatView(v *gocui.View) {
v.Clear()
for i, msg := range messages {
fmt.Fprintf(v, "%s\n\n", msg)
w, h, err := cell_size.GetTerminalCellSizePixels()
if err != nil {
log.Println("Error getting terminal cell size:", err)
continue
}
if h > w {
h = h * 2
w = 0
} else {
w = w*3 - (w / 10)
h = 0
}
render_image.RenderImage("kogami-rounded.png", i*3+2, 23, w, h, 0)
}
}
func sendMessage(g *gocui.Gui, v *gocui.View) error {
input := v.Buffer()
v.Clear()
v.SetCursor(0, 0)
v.SetOrigin(0, 0)
if input != "" {
messages = append(messages, "\t\t\t\tYou:\n\t\t\t\t"+input)
if chatView, err := g.View("chat"); err == nil {
updateChatView(chatView)
}
}
return nil
}

View File

@ -1,39 +0,0 @@
package tui
var Colors = struct {
base00 string
base01 string
base02 string
base03 string
base04 string
base05 string
base06 string
base07 string
base08 string
base09 string
base10 string
base11 string
base12 string
base13 string
base14 string
base15 string
base16 string
}{
base00: "\033[31;7m",
base01: "\033[32;7m",
base02: "\033[33;7m",
base03: "\033[34;7m",
base04: "\033[35;7m",
base05: "\033[36;7m",
base06: "\033[37;7m",
base07: "\033[38;7m",
base08: "\033[39;7m",
base09: "\033[310;7m",
base10: "\033[311;7m",
base11: "\033[312;7m",
base12: "\033[313;7m",
base13: "\033[314;7m",
base14: "\033[315;7m",
base15: "\033[316;7m",
base16: "\033[0m",
}

View File

@ -1,64 +0,0 @@
package tui
import (
"github.com/jroimartin/gocui"
)
var selectedUserIdx int
func keybindings(g *gocui.Gui) error {
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
return err
}
if err := g.SetKeybinding("input", gocui.KeyEnter, gocui.ModNone, sendMessage); err != nil {
return err
}
if err := g.SetKeybinding("chat", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
return err
}
if err := g.SetKeybinding("chat", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, nextContact); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeySpace, gocui.ModNone, prevContact); err != nil {
return err
}
return nil
}
func insertNewline(g *gocui.Gui, v *gocui.View) error {
v.EditNewLine()
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
func cursorDown(g *gocui.Gui, v *gocui.View) error {
if v != nil {
cx, cy := v.Cursor()
if err := v.SetCursor(cx, cy+1); err != nil {
ox, oy := v.Origin()
if err := v.SetOrigin(ox, oy+1); err != nil {
return err
}
}
}
return nil
}
func cursorUp(g *gocui.Gui, v *gocui.View) error {
if v != nil {
ox, oy := v.Origin()
cx, cy := v.Cursor()
if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
if err := v.SetOrigin(ox, oy-1); err != nil {
return err
}
}
}
return nil
}

View File

@ -1,30 +0,0 @@
package tui
import (
"github.com/jroimartin/gocui"
)
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if maxX != prevWidth || maxY != prevHeight {
prevWidth, prevHeight = maxX, maxY
if chatView, err := g.View("chat"); err == nil {
updateChatView(chatView)
}
}
if err := layoutSidebar(g, maxY); err != nil {
return err
}
if err := layoutChat(g, maxX, maxY); err != nil {
return err
}
if err := layoutInput(g, maxX, maxY); err != nil {
return err
}
return nil
}

View File

@ -1,73 +0,0 @@
package tui
import (
"encoding/base64"
"encoding/json"
"log"
"os"
"path/filepath"
"strings"
"time"
)
type ChatMessage struct {
ID string `json:"id"`
Sender string `json:"sender"`
Receiver string `json:"receiver"`
Content string `json:"content"`
Timestamp string `json:"timestamp"`
}
type ChatData struct {
Messages []ChatMessage `json:"messages"`
}
func LoadContacts(path string) {
contactsPath := filepath.Join(path, "users")
folders, err := os.ReadDir(contactsPath)
if err != nil {
log.Printf("Error reading contacts directory: %v", err)
return
}
for _, folder := range folders {
if folder.IsDir() {
users = append(users, folder.Name())
}
}
}
func LoadMessages(username string) {
chatFile := filepath.Join("configs", "servers", "default", "users", strings.ToLower(username), "messages.json")
data, err := os.ReadFile(chatFile)
if err != nil {
log.Printf("Error reading chat file: %v", err)
return
}
var chatData ChatData
if err := json.Unmarshal(data, &chatData); err != nil {
log.Printf("Error parsing JSON: %v", err)
return
}
for _, msg := range chatData.Messages {
decoded, err := base64.StdEncoding.DecodeString(msg.Content)
if err != nil {
log.Printf("Error decoding message: %v", err)
continue
}
if strings.HasSuffix(string(decoded), "\n") {
decoded = []byte(strings.TrimSuffix(string(decoded), "\n"))
}
t, _ := time.Parse(time.RFC3339, msg.Timestamp)
formattedTime := t.Format("2006-01-02 15:04")
if !strings.EqualFold(msg.Sender, username) {
messages = append(messages, "\t\t\t\tYou ("+formattedTime+"):\n\t\t\t\t"+string(decoded))
} else {
messages = append(messages, "\t\t\t\t"+msg.Sender+" ("+formattedTime+"):\n\t\t\t\t"+string(decoded))
}
}
}

View File

@ -1,65 +0,0 @@
package tui
import (
"fmt"
"github.com/jroimartin/gocui"
)
// LAYOUT
func layoutSidebar(g *gocui.Gui, maxY int) error {
if v, err := g.SetView("users", 0, 0, 20, maxY-1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Users"
v.Clear()
updateUsersView(g)
}
return nil
}
func updateUsersView(g *gocui.Gui) error {
v, err := g.View("users")
if err != nil {
return err
}
v.Clear()
// TODO: Render profile image of users and change colors of each user maybe?
for i, u := range users {
// Change Selected User In The TUI Window
if i == selectedUserIdx {
fmt.Fprintf(v, "%s%s%s\n", Colors.base06, u, Colors.base16)
_, y := v.Size()
if i == 0 {
v.SetOrigin(0, 0)
} else {
v.SetOrigin(0, i-y+1)
}
} else {
fmt.Fprintln(v, u)
}
}
return nil
}
// KEYBINDINGS
func nextContact(g *gocui.Gui, v *gocui.View) error {
if len(users) == 0 {
return nil
}
selectedUserIdx = (selectedUserIdx + 1) % len(users)
return updateUsersView(g)
}
func prevContact(g *gocui.Gui, v *gocui.View) error {
if len(users) == 0 {
return nil
}
selectedUserIdx = (selectedUserIdx - 1 + len(users)) % len(users)
return updateUsersView(g)
}

View File

@ -1,20 +1,199 @@
package tui package tui
import ( import (
"fmt"
_ "image/jpeg"
_ "image/png"
"log" "log"
"strings"
"github.com/jroimartin/gocui" "github.com/jroimartin/gocui"
//"whspbrd/pkg/systray"
"whspbrd/pkg/render_image"
) )
var messages []string var messages []string
var users []string var users = []string{"Alice\n", "Bob\n", "Charlie\n"}
var prevWidth, prevHeight int var prevWidth, prevHeight int
func Run() { func layout(g *gocui.Gui) error {
//LoadContacts("configs/servers/default") maxX, maxY := g.Size()
users = []string{"Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Heidi", "Ivan", "Judy", "Karl", "Leo", "Mallory", "Nina", "Oscar", "Peggy", "Quentin", "Rupert", "Sybil", "Trent", "Uma", "Victor", "Walter", "Xena", "Yara", "Zane"}
LoadMessages(users[0])
if maxX != prevWidth || maxY != prevHeight {
prevWidth, prevHeight = maxX, maxY
if chatView, err := g.View("chat"); err == nil {
updateChatView(chatView)
}
}
if v, err := g.SetView("users", 0, 0, 20, maxY-1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Users"
v.Clear()
for _, u := range users {
fmt.Fprintln(v, u)
}
}
if v, err := g.SetView("chat", 21, 0, maxX-1, maxY-5); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Chat"
v.Wrap = true
v.Autoscroll = true
updateChatView(v)
}
if v, err := g.SetView("input", 21, maxY-4, maxX-1, maxY-1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Type your message"
v.Editable = true
v.Wrap = true
if _, err := g.SetCurrentView("input"); err != nil {
return err
}
}
return nil
}
func updateChatView(v *gocui.View) {
v.Clear()
for i, msg := range messages {
fmt.Fprintf(v, "%s\n\n", msg)
//imagePath := "kogami-rounded.png"
//file, err := os.Open(imagePath)
//if err != nil {
// log.Println("Error opening image:", err)
// continue
//}
//defer file.Close()
//img, _, err := image.Decode(file)
//if err != nil {
// log.Println("Error decoding image:", err)
// continue
//}
// Print image directly to terminal (stdout)
render_image.RenderImage("kogami-rounded.png", i*3+2, 23, 50, 50, 0)
//err = kittyimg.Fprintln(os.Stdout, img)
//if err != nil {
// log.Println("Error rendering image:", err)
//}
}
}
func sendMessage(g *gocui.Gui, v *gocui.View) error {
input := strings.TrimSpace(v.Buffer())
v.Clear()
v.SetCursor(0, 0)
v.SetOrigin(0, 0)
if input != "" {
messages = append(messages, "\t\t\t\tYou:\n\t\t\t\t"+input)
if chatView, err := g.View("chat"); err == nil {
updateChatView(chatView)
}
}
v.Clear()
v.SetCursor(0, 0)
return nil
}
func cursorDown(g *gocui.Gui, v *gocui.View) error {
if v != nil {
cx, cy := v.Cursor()
if err := v.SetCursor(cx, cy+1); err != nil {
ox, oy := v.Origin()
if err := v.SetOrigin(ox, oy+1); err != nil {
return err
}
}
}
return nil
}
func cursorUp(g *gocui.Gui, v *gocui.View) error {
if v != nil {
ox, oy := v.Origin()
cx, cy := v.Cursor()
if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
if err := v.SetOrigin(ox, oy-1); err != nil {
return err
}
}
}
return nil
}
func nextView(g *gocui.Gui, v *gocui.View) error {
if v == nil || v.Name() == "chat" {
_, err := g.SetCurrentView("input")
return err
}
_, err := g.SetCurrentView("chat")
return err
}
func keybindings(g *gocui.Gui) error {
// Ctrl+C to quit
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
return err
}
// Enter to send message
// Enter adds a new line (multiline input)
//if err := g.SetKeybinding("input", gocui.KeyArrowDown, gocui.ModNone, insertNewline); err != nil {
// return err
//}
if err := g.SetKeybinding("chat", gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
return err
}
if err := g.SetKeybinding("input", gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
return err
}
// Alt+Enter inserts newline
if err := g.SetKeybinding("input", gocui.KeyEnter, gocui.ModAlt, insertNewline); err != nil {
return err
}
// Enter (no modifiers) sends message
if err := g.SetKeybinding("input", gocui.KeyEnter, gocui.ModNone, sendMessage); err != nil {
return err
}
// Arrow up
if err := g.SetKeybinding("chat", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
return err
}
// Arrow down
if err := g.SetKeybinding("chat", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
return err
}
return nil
}
func insertNewline(g *gocui.Gui, v *gocui.View) error {
v.EditNewLine()
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
func Run() {
g, err := gocui.NewGui(gocui.OutputNormal) g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil { if err != nil {
log.Panicln(err) log.Panicln(err)