feat(cmd): add `ticktock tui` grid subcommand; document in README
e95af753c387281918516c7ddb913c8e6fe5d2ac
Kevin Kortum <kevinkortum@me.com> · 2026-07-07 21:48
parent 6cab7d55
3 files changed
README.md +7 −0
@@ -13,5 +13,12 @@ ## Commands
- `ticktock day [date|today|yesterday]` — a day view (the tock-list layout) with
per-project totals and Enter-to-edit. `←/→` change day, `q` quits.
+- `ticktock tui [date|today|yesterday]` — a spatial day grid. Each slot is a
+ fixed row (default 30 min). `←/→` switch column, `↑/↓` move slot, `space`
+ in ACTIVITY starts a selection and a second `space` opens the add form for
+ that range, `enter` edits the logged session under the cursor, `d` deletes
+ it (with confirm), `n/p/t` change day, `r` reloads, `q` quits.
+ Reads `~/.config/ticktock/config.json` (`slot_minutes`, `grid_start`,
+ `grid_end`, `project`) if present.
Requires `tock` on PATH.
cmd/ticktock/main.go +20 −0
@@ -8,8 +8,10 @@
tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
+ "ticktock/internal/config"
"ticktock/internal/store"
"ticktock/internal/tui/day"
+ "ticktock/internal/tui/grid"
)
func newRootCmd() *cobra.Command {
@@ -18,7 +20,25 @@ Use: "ticktock",
Short: "Charm-native front-end for the tock time tracker",
}
root.AddCommand(newDayCmd())
+ root.AddCommand(newTuiCmd())
return root
+}
+
+func newTuiCmd() *cobra.Command {
+ return &cobra.Command{
+ Use: "tui [date|today|yesterday]",
+ Short: "Spatial day grid; range-select to add entries",
+ Args: cobra.MaximumNArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ arg := ""
+ if len(args) == 1 {
+ arg = args[0]
+ }
+ m := grid.New(store.New(store.ExecRunner{}), config.Load(), resolveDate(arg))
+ _, err := tea.NewProgram(m, tea.WithAltScreen()).Run()
+ return err
+ },
+ }
}
func newDayCmd() *cobra.Command {
cmd/ticktock/main_test.go +13 −0
@@ -15,6 +15,19 @@ t.Fatalf("root command is missing the 'day' subcommand")
}
}
+func TestTuiCommandRegistered(t *testing.T) {
+ root := newRootCmd()
+ var found bool
+ for _, c := range root.Commands() {
+ if c.Name() == "tui" {
+ found = true
+ }
+ }
+ if !found {
+ t.Fatal("expected a `tui` subcommand")
+ }
+}
+
func TestRootUse(t *testing.T) {
if got := newRootCmd().Use; got != "ticktock" {
t.Fatalf("root Use = %q, want %q", got, "ticktock")