> [!WARNING] wip > all of this is work in progress, these notes are more like a journal for this project I am going to write a tools that will let me track time for billable hours at my job. I think I can expand it to do much more than that in the future but for now that it where I will start. I am going to write this project using Go and the bubbletea library for writting command line tools, therefore part of these notes will be about me getting good at writting Go since I am using this project for that purpose as well. ### Thursday, June 6th 2024 I very simple database interactions happening! use this to populate the dropdown for when selecting a project https://github.com/charmbracelet/bubbletea/tree/master/examples/list-default ## Wednesday, May 29th 2024 So in order to create a bubble tea app we need a few things: **Model** A model, this model is usually a struct and it will hold the state of the application. In the example for gathering user input the model is as follows: ```go type model struct { textInput textinput.Model err error } ``` so in this case we create a struct with to elements, the first the `textInput` which has type `textinput.Model`, this is a type from the bubble library that lets us get user input. The next element is an `err` variable this will just store any errors and is of type `error`. We also need to initialize the model to some state that we will use when the app first starts up. We create a function for this: ```go func initialModel() model { ti := textinput.New() ti.Placeholder = "enter task description" ti.Focus() ti.CharLimit = 500 ti.Width = 50 return model{ textInput: ti, err: nil, } } ``` this is all very straighforward, we create a new "instance" of textinput and set some of its parameters in the following lines. As the function signature indicates we return a model which is a struct composed of two elements a textInput and an error, therefore we return this from this function. There is no error so we just set this value to `nil`. With the model defined we need three methods to complete the interface (I don't think this is the way to say it). We need an `Init`, `View` and `Update`. The Init is what we would expect it to be, the View is the way we want it to render on the terminal and the Update is the behavior/logic for when there in an event like a key press, or some other thing we are interested in capturing. **Init** The init function: ```go func (m model) Init() tea.Cmd { return textinput.Blink } ``` **Update** This is probably the most exiting of the function, tbh. Here we respond message we get from the user. In this case we are trying to capture user input therefore we will respond to key messages. One thing that is new to me here is this `switch` `switch msg := msg.(type)` in go we can switch based on the type of the message and the syntax is this. The rest is actually really straighforward too. I have some experience in C and so a lot of this is not too crazy, at least the syntax. ```go 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 } // We handle errors just like any other message case errMsg: m.err = msg return m, nil } m.textInput, cmd = m.textInput.Update(msg) return m, cmd } ``` ## Tuesday, May 28th 2024 Ok lets go over this basic program: Define types: ```go type model int type tickMsg time.Time ``` so a couple things here, we define our own types using the syntax `type WhatWantToCallIT someType` so in this case we define our own type model that is just basically an alias for the int, same thing tickMsg. In general this is like using typedef in C except in C its stricly just like creating an alias in Go is goes a little further in that a whole new type is created, this new type can have its own methods and all that. Functions are also defined a little differently, namely the type goes at the end, you can also return multiple values and you can add "receivers" to functions, these are somewhat like methods in OOP. Here is the tutorial for bubbletea https://github.com/charmbracelet/bubbletea?tab=readme-ov-file#tutorial it was really helpful and going to use this as base and start creating the very basic time tracking now. A couple things i need to learn to do are how to do time.Monotonic in go, so that I keep track of timers. The design of the app I want to create: https://app.excalidraw.com/s/5bUZrm9V8WF/AwAF3J2KBHt ## Monday, May 27th 2024 Ok so here is the most simple example in the bubbletea library, and the goal for today is to just rewrite it tweak it a little and explain how it works for the most part. ```go package main import ( "fmt" "log" "time" tea "github.com/charmbracelet/bubbletea" ) type model int type tickMsg time.Time func (m model) Init() tea.Cmd { return tick } func tick() tea.Msg { time.Sleep(time.Second) return tickMsg{} } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg.(type) { case tea.KeyMsg: fmt.Println("you pressed a key! exiting now....") return m, tea.Quit case tickMsg: m-- if m <= 0 { return m, tea.Quit } return m, tick } return m, nil } func (m model) View() string { return fmt.Sprintf("Hello there, this program will quit in %d seconds, unless you press any key, in which case it will quit right away.\n", m) } func main() { fmt.Println("hello world!") p := tea.NewProgram(model(10)) if _, err := p.Run(); err != nil { log.Fatal(err) } } ```