diff --git a/calendar/calendar.pb.go b/calendar/calendar.pb.go new file mode 100644 index 0000000..71eb6cf --- /dev/null +++ b/calendar/calendar.pb.go @@ -0,0 +1,186 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.6 +// protoc v6.30.2 +// source: proto/calendar.proto + +package calendar + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Event struct { + state protoimpl.MessageState `protogen:"open.v1"` + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Year int32 `protobuf:"varint,3,opt,name=year,proto3" json:"year,omitempty"` + Month int32 `protobuf:"varint,4,opt,name=month,proto3" json:"month,omitempty"` + Day int32 `protobuf:"varint,5,opt,name=day,proto3" json:"day,omitempty"` + StartHour int32 `protobuf:"varint,6,opt,name=start_hour,json=startHour,proto3" json:"start_hour,omitempty"` + EndHour int32 `protobuf:"varint,7,opt,name=end_hour,json=endHour,proto3" json:"end_hour,omitempty"` + Color string `protobuf:"bytes,8,opt,name=color,proto3" json:"color,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Event) Reset() { + *x = Event{} + mi := &file_proto_calendar_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_proto_calendar_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_proto_calendar_proto_rawDescGZIP(), []int{0} +} + +func (x *Event) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *Event) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Event) GetYear() int32 { + if x != nil { + return x.Year + } + return 0 +} + +func (x *Event) GetMonth() int32 { + if x != nil { + return x.Month + } + return 0 +} + +func (x *Event) GetDay() int32 { + if x != nil { + return x.Day + } + return 0 +} + +func (x *Event) GetStartHour() int32 { + if x != nil { + return x.StartHour + } + return 0 +} + +func (x *Event) GetEndHour() int32 { + if x != nil { + return x.EndHour + } + return 0 +} + +func (x *Event) GetColor() string { + if x != nil { + return x.Color + } + return "" +} + +var File_proto_calendar_proto protoreflect.FileDescriptor + +const file_proto_calendar_proto_rawDesc = "" + + "\n" + + "\x14proto/calendar.proto\x12\bcalendar\"\xcb\x01\n" + + "\x05Event\x12\x14\n" + + "\x05title\x18\x01 \x01(\tR\x05title\x12 \n" + + "\vdescription\x18\x02 \x01(\tR\vdescription\x12\x12\n" + + "\x04year\x18\x03 \x01(\x05R\x04year\x12\x14\n" + + "\x05month\x18\x04 \x01(\x05R\x05month\x12\x10\n" + + "\x03day\x18\x05 \x01(\x05R\x03day\x12\x1d\n" + + "\n" + + "start_hour\x18\x06 \x01(\x05R\tstartHour\x12\x19\n" + + "\bend_hour\x18\a \x01(\x05R\aendHour\x12\x14\n" + + "\x05color\x18\b \x01(\tR\x05colorB?Z=git.skdevstudios.com/SK-Development-Studios/GoCalTui/calendarb\x06proto3" + +var ( + file_proto_calendar_proto_rawDescOnce sync.Once + file_proto_calendar_proto_rawDescData []byte +) + +func file_proto_calendar_proto_rawDescGZIP() []byte { + file_proto_calendar_proto_rawDescOnce.Do(func() { + file_proto_calendar_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_calendar_proto_rawDesc), len(file_proto_calendar_proto_rawDesc))) + }) + return file_proto_calendar_proto_rawDescData +} + +var file_proto_calendar_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_proto_calendar_proto_goTypes = []any{ + (*Event)(nil), // 0: calendar.Event +} +var file_proto_calendar_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_proto_calendar_proto_init() } +func file_proto_calendar_proto_init() { + if File_proto_calendar_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_calendar_proto_rawDesc), len(file_proto_calendar_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_proto_calendar_proto_goTypes, + DependencyIndexes: file_proto_calendar_proto_depIdxs, + MessageInfos: file_proto_calendar_proto_msgTypes, + }.Build() + File_proto_calendar_proto = out.File + file_proto_calendar_proto_goTypes = nil + file_proto_calendar_proto_depIdxs = nil +} diff --git a/form.go b/form.go new file mode 100644 index 0000000..0263428 --- /dev/null +++ b/form.go @@ -0,0 +1,50 @@ + +package main + +import ( + "context" + "strconv" + + "github.com/charmbracelet/huh" +) + +func showEventForm(ctx context.Context, onSubmit func(EventInput)) error { + var title, description, year, month, day, startHour, endHour, color string + + form := huh.NewForm( + huh.NewGroup( + huh.NewInput().Title("Title").Value(&title), + huh.NewInput().Title("Description").Value(&description), + huh.NewInput().Title("Year").Value(&year), + huh.NewInput().Title("Month").Value(&month), + huh.NewInput().Title("Day").Value(&day), + huh.NewInput().Title("Start Hour (0–23)").Value(&startHour), + huh.NewInput().Title("End Hour (0–23)").Value(&endHour), + huh.NewInput().Title("Color (e.g. 99 or #ff5733)").Value(&color), + ), + ) + + if err := form.Run(); err != nil { + return err + } + + yr, _ := strconv.Atoi(year) + mo, _ := strconv.Atoi(month) + da, _ := strconv.Atoi(day) + sh, _ := strconv.Atoi(startHour) + eh, _ := strconv.Atoi(endHour) + + onSubmit(EventInput{ + Title: title, + Description: description, + Year: int32(yr), + Month: int32(mo), + Day: int32(da), + StartHour: int32(sh), + EndHour: int32(eh), + Color: color, + }) + + return nil +} + diff --git a/go.mod b/go.mod index 1d35b1b..eab7644 100644 --- a/go.mod +++ b/go.mod @@ -3,28 +3,49 @@ module git.skdevstudios.com/SK-Development-Studios/GoCalTui go 1.23.10 require ( - github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.5 + github.com/charmbracelet/huh v0.7.0 github.com/charmbracelet/lipgloss v1.1.0 + github.com/dgraph-io/badger/v4 v4.7.0 + google.golang.org/protobuf v1.36.6 ) require ( + github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/catppuccin/go v0.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charmbracelet/bubbles v0.21.0 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/x/ansi v0.8.0 // indirect - github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/charmbracelet/x/cellbuf v0.0.13 // indirect + github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/flatbuffers v25.2.10+incompatible // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + golang.org/x/net v0.38.0 // indirect golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect - golang.org/x/text v0.3.8 // indirect + golang.org/x/text v0.23.0 // indirect ) + +replace git.skdevstudios.com/SK-Development-Studios/GoCalTui/calendar => ./calendar diff --git a/go.sum b/go.sum index 48041bc..b50337d 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,68 @@ +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +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-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= +github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc= github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/huh v0.7.0 h1:W8S1uyGETgj9Tuda3/JdVkc3x7DBLZYPZc4c+/rnRdc= +github.com/charmbracelet/huh v0.7.0/go.mod h1:UGC3DZHlgOKHvHC07a5vHag41zzhpPFj34U92sOmyuk= github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= +github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U= +github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ= +github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA= +github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4= +github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +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/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI= +github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y= +github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA= +github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= +github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= +github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -28,24 +71,44 @@ github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2J github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= 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/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +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/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 08217df..691dc62 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "log" "fmt" "os" "time" @@ -9,6 +10,11 @@ import ( ) func main() { + if err := InitDB("data"); err != nil { + log.Fatalf("Failed to initialize BadgerDB: %v", err) + } + defer CloseDB() + // Get the current datetime now := time.Now() // Set the start date used for opening the current month instead of 1/1/1970 diff --git a/proto/calendar.proto b/proto/calendar.proto index 6277420..283d3cf 100644 --- a/proto/calendar.proto +++ b/proto/calendar.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package calendar; -option go_package = "calendar"; +option go_package = "git.skdevstudios.com/SK-Development-Studios/GoCalTui/calendar"; message Event { string title = 1; @@ -12,4 +12,6 @@ message Event { int32 day = 5; int32 start_hour = 6; int32 end_hour = 7; + string color = 8; } + diff --git a/storage.go b/storage.go new file mode 100644 index 0000000..8493bf7 --- /dev/null +++ b/storage.go @@ -0,0 +1,120 @@ + +package main + +import ( + "git.skdevstudios.com/SK-Development-Studios/GoCalTui/calendar" + "fmt" + "path/filepath" + "os" + "github.com/dgraph-io/badger/v4" + "google.golang.org/protobuf/proto" +) + +type EventInput struct { + Title string + Description string + Year int32 + Month int32 + Day int32 + StartHour int32 + EndHour int32 + Color string +} + + +var db *badger.DB + +func InitDB(path string) error { + if err := os.MkdirAll(path, 0755); err != nil { + return fmt.Errorf("create data dir: %w", err) + } + + var err error + db, err = badger.Open(badger.DefaultOptions(filepath.Join(path, "events_db")).WithLogger(nil)) + if err != nil { + return fmt.Errorf("open badger: %w", err) + } + return nil +} + + + + + +func CloseDB() { + if db != nil { + db.Close() + } +} + +func SaveEvent(input EventInput) error { + if db == nil { + return fmt.Errorf("badger DB is not initialized") + } + + event := &calendar.Event{ + Title: input.Title, + Description: input.Description, + Year: input.Year, + Month: input.Month, + Day: input.Day, + StartHour: input.StartHour, + EndHour: input.EndHour, + Color: input.Color, + } + + + data, err := proto.Marshal(event) + if err != nil { + return err + } + + key := []byte(fmt.Sprintf("events/%04d-%02d-%02d/%02d", event.Year, event.Month, event.Day, event.StartHour)) + + return db.Update(func(txn *badger.Txn) error { + return txn.Set(key, data) + }) +} + +func GetEvent(year, month, day, hour int32) (*calendar.Event, error) { + var evt calendar.Event + key := []byte(fmt.Sprintf("events/%04d-%02d-%02d/%02d", year, month, day, hour)) + + err := db.View(func(txn *badger.Txn) error { + item, err := txn.Get(key) + if err != nil { + return err + } + return item.Value(func(val []byte) error { + return proto.Unmarshal(val, &evt) + }) + }) + return &evt, err +} +func GetEventsForDay(year, month, day int32) ([]*calendar.Event, error) { + prefix := []byte(fmt.Sprintf("events/%04d-%02d-%02d/", year, month, day)) + var events []*calendar.Event + + err := db.View(func(txn *badger.Txn) error { + it := txn.NewIterator(badger.DefaultIteratorOptions) + defer it.Close() + + for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { + item := it.Item() + err := item.Value(func(val []byte) error { + var e calendar.Event + if err := proto.Unmarshal(val, &e); err != nil { + return err + } + events = append(events, &e) + return nil + }) + if err != nil { + return err + } + } + return nil + }) + + return events, err +} diff --git a/update.go b/update.go index fe720d3..a48779e 100644 --- a/update.go +++ b/update.go @@ -1,6 +1,7 @@ package main import ( + "context" "time" tea "github.com/charmbracelet/bubbletea" @@ -34,17 +35,6 @@ func (m model) updateMonthView(msg tea.Msg) (tea.Model, tea.Cmd) { } else { m.monthIndex-- } - - first := time.Date(m.year, time.Month(m.monthIndex+1), 1, 0, 0, 0, 0, time.UTC) - offset := int(first.Weekday()) - - m.startOffset = offset - m.daysInMonth = time.Date(m.year, time.Month(m.monthIndex+2), 0, 0, 0, 0, 0, time.UTC).Day() - - m.cursorRow = offset / numCols - m.cursorCol = offset % numCols - - return m, nil case "d": // Next month if m.monthIndex == 11 { m.monthIndex = 0 @@ -52,17 +42,6 @@ func (m model) updateMonthView(msg tea.Msg) (tea.Model, tea.Cmd) { } else { m.monthIndex++ } - - first := time.Date(m.year, time.Month(m.monthIndex+1), 1, 0, 0, 0, 0, time.UTC) - offset := int(first.Weekday()) - - m.startOffset = offset - m.daysInMonth = time.Date(m.year, time.Month(m.monthIndex+2), 0, 0, 0, 0, 0, time.UTC).Day() - - m.cursorRow = offset / numCols - m.cursorCol = offset % numCols - - return m, nil case "up": if m.cursorRow > 0 { m.cursorRow-- @@ -80,7 +59,6 @@ func (m model) updateMonthView(msg tea.Msg) (tea.Model, tea.Cmd) { m.cursorCol++ } case "enter": - // Calculate selected day based on cursor dayIndex := m.cursorRow*numCols + m.cursorCol if dayIndex >= m.startOffset && dayIndex < m.startOffset+m.daysInMonth { m.selectedDay = dayIndex - m.startOffset + 1 @@ -105,6 +83,10 @@ func (m model) updateHourlyView(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { + case "n": + showEventForm(context.Background(), func(e EventInput) { + _ = SaveEvent(e) + }) case "esc": m.mode = monthView return m, nil diff --git a/view.go b/view.go index 3f91ac4..e8f759c 100644 --- a/view.go +++ b/view.go @@ -3,6 +3,8 @@ package main import ( "fmt" "time" + "strings" + "github.com/charmbracelet/lipgloss" ) func (m model) View() string { @@ -63,27 +65,51 @@ func (m model) viewMonth() string { } func (m model) viewHourly() string { - var out string + var b strings.Builder - dateHeader := fmt.Sprintf("Schedule for %04d-%02d-%02d", m.year, m.monthIndex+1, m.selectedDay) - out += headerStyle.Render(dateHeader) + "\n\n" - - nowHour := time.Now().Hour() + events, _ := GetEventsForDay(int32(m.year), int32(m.monthIndex+1), int32(m.selectedDay)) for hour := 0; hour < 24; hour++ { - label := fmt.Sprintf("%02d:00 - %02d:00", hour, hour+1) + label := fmt.Sprintf("%02d:00", hour) + styled := false - switch { - case hour == m.hourCursor: - out += hourSelectedStyle.Render(label) + "\n" - case hour == nowHour: - out += currentHourStyle.Render(label) + "\n" - default: - out += hourCellStyle.Render(label) + "\n" + for _, e := range events { + if hour >= int(e.StartHour) && hour < int(e.EndHour) { + style := hourCellStyle.Copy().Background(lipgloss.Color(e.Color)) + + if hour == m.hourCursor { + if hour == int(e.StartHour) { + label = style.Copy().Bold(true).Render("▶ " + fmt.Sprintf("%02d:00 - %s", hour, e.Title)) + } else { + label = style.Copy().Bold(true).Render("▶ " + label) + } + } else { + if hour == int(e.StartHour) { + label = style.Render(" " + fmt.Sprintf("%02d:00 - %s", hour, e.Title)) + } else { + label = style.Render(" " + label) + } + } + + styled = true + break + } } + + if !styled { + if hour == m.hourCursor { + label = hourSelectedStyle.Render("▶ " + label) + } else if hour == time.Now().Hour() { + label = currentHourStyle.Render(" " + label) + } else { + label = hourCellStyle.Render(" " + label) + } + } + + b.WriteString(label + "\n") } - out += "\nPress ESC to return to month view, q to quit." - return out + return b.String() } +