-
Notifications
You must be signed in to change notification settings - Fork 0
Simple interactive terminal user interface (without brick) #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
de22e5a
Prepare t5 executable
memowe e20ef1c
Add case-insensitive completion unit test (and fix typo)
memowe 3f03471
Rename test suite (as t4/t5 are executables)
memowe 370bf17
Add haskeline completion integration
memowe 7ab1bc0
Add safe last function to Util
memowe 69b38e5
Add simple interactive terminal user interface
memowe 8e67ef8
Let empty completion substrings match all items
memowe 38c477e
Show spent time on startup and clock out
memowe 4da893c
Fix a minor category completion whitespace stripping bug
memowe a8c71ec
Use Haskelines word completion to auto-complete multiple tags
memowe c57bc8d
Include spent seconds to t5
memowe 9851e5f
Move duration map text reports to library
memowe 47b0b27
Use cats OR tags in Commands, so get rid of indentation
memowe d46cc75
Add reports to TUI options
memowe 8de491d
Fix minor empty string default
memowe b5ca3bd
Fix minor typo
memowe 7c72189
Cleanup executable source directory names
memowe 7137b0c
Add more details to README
memowe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,95 @@ | ||
| # t4: terminal time tracking tool | ||
|
|
||
| Haskell library and terminal GUI tool for time tracking. | ||
| **Haskell library and command line tools for simple time tracking.** | ||
|
|
||
| ## Run command tool | ||
| Storage uses very human-friendly yaml files in `~/.t4-data` that can be edited manually: | ||
|
|
||
| cabal run t4 | ||
| ``` | ||
| memowe@rakete:~$ ls ~/.t4-data | ||
| 2025-10-15.yml 2025-10-25.yml 2025-10-24.yml | ||
| 2025-10-23.yml 2025-10-27.yml | ||
| ``` | ||
|
|
||
| ## Run tests | ||
| ```yaml | ||
| $ cat ~/.t4-data/2025-10-27.yml | ||
| - in: | ||
| category: Writing t4 README | ||
| tags: | ||
| - t4 | ||
| - haskell | ||
| - documentation | ||
| time: 2025-10-27 10:24:57 | ||
| - out: | ||
| time: 2025-10-27 10:29:12 | ||
| - in: | ||
| category: Lunch break | ||
| tags: | ||
| - break | ||
| - recreation | ||
| - nom | ||
| time: 2025-10-27 12:42:17 | ||
| ``` | ||
|
|
||
| The project offers two command-line interfaces to edit these files for you. | ||
|
|
||
| ## Preparations | ||
|
|
||
| This is a standard [GHC][ghc]/[cabal][cabal] project with a core [library](lib) and two executables [t4](exe-t4-commands)/[t5](exe-t5-interactive), so you can use the standard cabal commands to build dependencies, the project itself and run tests: | ||
|
|
||
| cabal build --only-dependencies --enable-tests | ||
| cabal build | ||
| cabal test --test-show-details=direct | ||
|
|
||
| You can also build the core library's [Haddock API docs][haddock] by yourself (although they are not extensively commented): | ||
|
|
||
| cabal haddock | ||
|
|
||
| ## Command-based terminal interface `t4` | ||
|
|
||
| You can run the command-based tool (*terminal time tracking tool*) without installing using `cabal run t4 -- ARGUMENTS` and install it via `cabal install`. | ||
|
|
||
| ``` | ||
| $ t4 --help | ||
| t4 - terminal time tracking tool | ||
|
|
||
| Usage: t4 COMMAND | ||
|
|
||
| Simple interface for clocking in and out | ||
|
|
||
| Available options: | ||
| -h,--help Show this help text | ||
|
|
||
| Available commands: | ||
| in Clocking in | ||
| out Clocking out | ||
| status Show current status | ||
| categories List all categories | ||
| tags List all tags | ||
| report Report | ||
|
|
||
| $ t4 status | ||
| IN (2025-10-27 10:24:57) [Writing t4 README] #t4 #haskell #documentation | ||
| ``` | ||
|
|
||
| ## Interactive terminal interface `t5` | ||
|
|
||
| You can run the interactive terminal interface (*terminal time tracking tool terminal user interface*) without installing using `cabal run t5` and install it via `cabal install`. | ||
|
|
||
| ``` | ||
| $ t5 | ||
| IN (2025-10-27 10:24:57) [Writing t4 README] #t4 #haskell #documentation | ||
| Spent: 4mi 5s | ||
| [o]ut - [u]pdate - report [c]ategories - report [t]ags - [q]uit: o | ||
| Spent: 4mi 15s | ||
| OUT (2025-10-27 10:29:12) | ||
| ``` | ||
|
|
||
| ## Author and license | ||
|
|
||
| (c) 2025 Mirko Westermeier | ||
|
|
||
| Released under the MIT license. See [LICENSE](LICENSE) for details. | ||
|
|
||
| [ghc]: https://www.haskell.org/ghc/ | ||
| [cabal]: https://www.haskell.org/cabal/ | ||
| [haddock]: http://mirko.westermeier.de/t4/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| import T4.Data | ||
| import T4.Storage | ||
| import T4.Report | ||
| import Util | ||
| import Completion | ||
| import Data.Char | ||
| import Data.List | ||
| import Data.Maybe | ||
| import Data.Function | ||
| import Data.Map (Map) | ||
| import Data.Time | ||
| import qualified System.Console.Haskeline as H | ||
|
|
||
| main :: IO () | ||
| main = do | ||
| sdir <- getStorageDirectory | ||
| clock <- lastMaybe <$> loadDataFromDir sdir | ||
| showState clock | ||
| newClock <- if isJust clock && isIn (fromJust clock) | ||
| then promptIn (time $ fromJust clock) | ||
| else promptOut | ||
| case newClock of | ||
| Nothing -> showState clock | ||
| Just c -> do addClockToDir sdir c | ||
| showState (Just c) | ||
|
|
||
| showState :: Maybe Clock -> IO () | ||
| showState = putStrLn . maybe "No clock data yet" summary | ||
|
|
||
| promptIn :: SimpleLocalTime -> IO (Maybe Clock) | ||
| promptIn started = do | ||
| showSpent started | ||
| clocks <- loadDataFromDir =<< getStorageDirectory | ||
| choice <- run $ H.getInputChar | ||
| "[o]ut - [u]pdate - report [c]ategories - report [t]ags - [q]uit: " | ||
| case choice of | ||
| Just 'o' -> showSpent started >> Just . Out <$> getCurrentSLT | ||
| Just 'u' -> Just <$> clockIn | ||
| Just 'c' -> Nothing <$ report "Categories" (categoryDurations clocks) | ||
| Just 't' -> Nothing <$ report "Tags" (tagDurations clocks) | ||
| _ -> return Nothing | ||
|
|
||
| showSpent :: SimpleLocalTime -> IO () | ||
| showSpent started = do | ||
| now <- getCurrentSLT | ||
| putStrLn $ "Spent: " ++ showDuration (now `minusTime` started) | ||
| where showDuration = showDiffTime naturalDurationConfig | ||
| minusTime = diffLocalTime `on` getLocalTime | ||
|
|
||
| promptOut :: IO (Maybe Clock) | ||
| promptOut = do | ||
| clocks <- loadDataFromDir =<< getStorageDirectory | ||
| choice <- run $ H.getInputChar | ||
| "[i]n - report [c]ategories - report [t]ags - [q]uit: " | ||
| case choice of | ||
| Just 'i' -> Just <$> clockIn | ||
| Just 'c' -> Nothing <$ report "Categories" (categoryDurations clocks) | ||
| Just 't' -> Nothing <$ report "Tags" (tagDurations clocks) | ||
| _ -> return Nothing | ||
|
|
||
| clockIn :: IO Clock | ||
| clockIn = do | ||
| clocks <- loadDataFromDir =<< getStorageDirectory | ||
| let catsCompl = (`Compl` id) $ allCategories clocks | ||
| tagsCompl = (`Compl` id) $ allTags clocks | ||
| now <- getCurrentSLT | ||
| mc <- runWithCompletion catsCompl $ H.getInputLine "Category: " | ||
| mtags <- runWithCompletion tagsCompl $ H.getInputLine "Tags: " | ||
| return $ In now (parseCat mc) (parseTags mtags) | ||
| where parseCat = fmap $ dropWhile isSpace . dropWhileEnd isSpace | ||
| parseTags = map (dropWhile (== '#')) . words . fromMaybe "" | ||
|
|
||
| report :: String -> Map String NominalDiffTime -> IO () | ||
| report prefix durMap = do | ||
| putStrLn "[s]econds - [l]ength-ordered - [n]atural time instead of man-days" | ||
| mOptions <- run $ H.getInputLine "Options: " | ||
| case mOptions of | ||
| Nothing -> return () | ||
| Just options -> do | ||
| putStrLn prefix | ||
| let optSecs = 's' `elem` options | ||
| optLen = 'l' `elem` options | ||
| optNat = 'n' `elem` options | ||
| mapM_ putStrLn $ showDurMap optLen optNat optSecs durMap | ||
|
|
||
| run :: H.InputT IO a -> IO a | ||
| run = H.runInputTBehavior H.preferTerm H.defaultSettings | ||
|
|
||
| runWithCompletion :: Completion c -> H.InputT IO a -> IO a | ||
| runWithCompletion compl = H.runInputTBehavior H.preferTerm settings | ||
| where settings = (H.defaultSettings :: H.Settings IO) {H.complete = hcompl} | ||
| hcompl = haskelineCompletionFunc compl |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.