Skip to content

Commit f854678

Browse files
committed
Add new statistics and tag features
1 parent b66dcf6 commit f854678

17 files changed

Lines changed: 918 additions & 2317 deletions

File tree

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
## Unreleased
2+
3+
### Changed
4+
- Fix "Top tag" label to correctly show "Top task" in statistics page
5+
- Replace Inter font with Archivo for better text rendering and fix font loading issues
6+
- Use Instrument Serif font for numbers and Archivo for text/charts for improved typography
7+
- Improve statistics page styling with cleaner design, removed box shadows, and better hover effects
8+
- Update color scheme for better contrast and readability in both light and dark modes
9+
10+
### Added
11+
- Improved statistics
12+
- Track tags in the pie chart
13+
- Add support for pre-defined tags in configuration file via `pre_defined_tags` setting
14+
- Add tag selection dropdown in flow timer when pre-defined tags are configured
15+
- Allow special characters and spaces in pre-defined tags for better flexibility
16+
117
## 1.6.0 (2025-09-08)
218

319
### Fixed

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ primarily uses **flow timer mode** for a more flexible approach to time tracking
3434
- You can pause and resume work sessions.
3535
- You can skip break sessions.
3636
- **Flow timer mode**: Count-up timer with task tracking and milestone notifications.
37+
- **Pre-defined tags**: Configure categories for quick task organization (supports spaces and special characters).
3738
- You can customise the number of sessions before a long break.
3839
- You can set a maximum number of sessions.
3940
- Desktop notifications are supported on all platforms.
@@ -197,6 +198,15 @@ flow_default: true # use flow timer mode by default (set to false for traditiona
197198
flow_bell: true # play bell sounds at flow timer milestones (50% and 100%)
198199
flow_bell_sound: 'tibetan_bell' # sound file for flow timer bells (bell, loud_bell, tibetan_bell)
199200

201+
# Pre-defined tags for categorizing work sessions
202+
pre_defined_tags: [] # optional list of tags for quick selection
203+
# Example:
204+
# pre_defined_tags:
205+
# - "Sales"
206+
# - "Company Finances"
207+
# - "Product Development"
208+
# - "Customer Support"
209+
200210
# Other settings
201211
session_cmd: '' # execute an arbitrary command after each session
202212
strict: false # when enabled, you can't resume a paused session
@@ -481,11 +491,44 @@ This project includes comprehensive development tooling:
481491
- **Pre-commit hooks**: Automatic linting and formatting on commit
482492
- **Build tools**: Run `just build` to compile binaries
483493

494+
### Building the Web UI
495+
496+
The stats web interface uses embedded files that need to be rebuilt when changed:
497+
498+
1. **Install dependencies** (if not already installed):
499+
```bash
500+
npm install
501+
```
502+
503+
2. **Make changes** to files in `stats/web/`:
504+
- JavaScript: `stats/web/js/script.js`
505+
- CSS: `stats/web/css/styles.css`
506+
- HTML: `stats/web/index.html`
507+
508+
3. **Bundle JavaScript** (if JS was modified):
509+
```bash
510+
npx esbuild --bundle --minify --outfile=stats/web/dist/script.js --sourcemap stats/web/js/script.js
511+
```
512+
513+
4. **Rebuild the Go binary** (required for all web changes):
514+
```bash
515+
go build -o focus ./cmd/focus
516+
```
517+
518+
5. **Restart the stats server**:
519+
```bash
520+
./focus stats
521+
```
522+
523+
**Note:** The web files are embedded into the Go binary using `//go:embed`, so the binary must be rebuilt after any changes to see them take effect.
524+
484525
The project uses:
485526
- [just](https://github.com/casey/just) for task running
486527
- [golangci-lint v2](https://golangci-lint.run/) for code quality
487528
- [pre-commit](https://pre-commit.com/) for git hooks
488529
- tools.mod for development dependency management
530+
- [esbuild](https://esbuild.github.io/) for JavaScript bundling
531+
- Go embed for static file embedding
489532

490533
## ⚖ Licence
491534

app/action.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,23 @@ func sessionHelper(ctx *cli.Context) ([]*models.Session, store.DB, error) {
9696
return sessions, db, nil
9797
}
9898

99+
// sessionHelperReadOnly is a read-only version for stats that can run while timer is active
100+
func sessionHelperReadOnly(ctx *cli.Context) ([]*models.Session, store.DB, error) {
101+
conf := config.Filter(ctx)
102+
103+
db, err := store.NewReadOnlyClient(config.DBFilePath())
104+
if err != nil {
105+
return nil, nil, err
106+
}
107+
108+
sessions, err := db.GetSessions(conf.StartTime, conf.EndTime, conf.Tags)
109+
if err != nil {
110+
return nil, nil, err
111+
}
112+
113+
return sessions, db, nil
114+
}
115+
99116
// editConfigAction handles the edit-config command which opens the focus config
100117
// file in the user's default text editor.
101118
func editConfigAction(ctx *cli.Context) error {

config.example.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Example Focus Configuration
2+
# Copy this file to ~/.config/focus/config.yml and customize as needed
3+
4+
# Work session settings
5+
work:
6+
duration: 25m
7+
message: Focus on your task
8+
sound: loud_bell
9+
color: '#B0DB43'
10+
11+
# Short break settings
12+
short_break:
13+
duration: 5m
14+
message: Take a breather
15+
sound: bell
16+
color: '#12EAEA'
17+
18+
# Long break settings
19+
long_break:
20+
duration: 15m
21+
message: Take a long break
22+
sound: bell
23+
color: '#C492B1'
24+
25+
# General settings
26+
settings:
27+
long_break_interval: 4
28+
auto_start_break: true
29+
auto_start_work: false
30+
24hr_clock: false
31+
sound_on_break: false
32+
strict: false
33+
34+
# Flow timer settings
35+
flow_default: true
36+
flow_bell: true
37+
flow_bell_sound: tibetan_bell
38+
39+
# Confetti celebration (requires raycast-confetti-server)
40+
confetti_enabled: false
41+
42+
# Pre-defined tags for quick task categorization
43+
# These will appear as a dropdown in flow timer mode
44+
pre_defined_tags:
45+
- "Sales"
46+
- "Company Finances"
47+
- "Product Development"
48+
- "Customer Support"
49+
- "Marketing"
50+
- "Research & Development"
51+
- "Operations"
52+
- "HR & Admin"
53+
54+
# Notification settings
55+
notifications:
56+
enabled: true
57+
58+
# Display settings
59+
display:
60+
dark_theme: true

internal/config/config.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,19 @@ type (
3636

3737
// SettingsConfig contains general application settings.
3838
SettingsConfig struct {
39-
AmbientSound string `mapstructure:"ambient_sound"`
40-
Cmd string `mapstructure:"cmd"`
41-
LongBreakInterval int `mapstructure:"long_break_interval"`
42-
AutoStartBreak bool `mapstructure:"auto_start_break"`
43-
AutoStartWork bool `mapstructure:"auto_start_work"`
44-
SoundOnBreak bool `mapstructure:"sound_on_break"`
45-
Strict bool `mapstructure:"strict"`
46-
TwentyFourHour bool `mapstructure:"24hr_clock"`
47-
FlowBell bool `mapstructure:"flow_bell"`
48-
FlowBellSound string `mapstructure:"flow_bell_sound"`
49-
FlowDefault bool `mapstructure:"flow_default"`
50-
ConfettiEnabled bool `mapstructure:"confetti_enabled"`
39+
AmbientSound string `mapstructure:"ambient_sound"`
40+
Cmd string `mapstructure:"cmd"`
41+
LongBreakInterval int `mapstructure:"long_break_interval"`
42+
AutoStartBreak bool `mapstructure:"auto_start_break"`
43+
AutoStartWork bool `mapstructure:"auto_start_work"`
44+
SoundOnBreak bool `mapstructure:"sound_on_break"`
45+
Strict bool `mapstructure:"strict"`
46+
TwentyFourHour bool `mapstructure:"24hr_clock"`
47+
FlowBell bool `mapstructure:"flow_bell"`
48+
FlowBellSound string `mapstructure:"flow_bell_sound"`
49+
FlowDefault bool `mapstructure:"flow_default"`
50+
ConfettiEnabled bool `mapstructure:"confetti_enabled"`
51+
PreDefinedTags []string `mapstructure:"pre_defined_tags"`
5152
}
5253

5354
// arguments.

internal/config/viper.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const (
3636
keyFlowDefault = "flow_default"
3737
keyConfettiEnabled = "confetti_enabled"
3838
keyDarkTheme = "dark_theme"
39+
keyPreDefinedTags = "pre_defined_tags"
3940
)
4041

4142
// WithViperConfig returns an Option that loads configuration from Viper.
@@ -145,6 +146,7 @@ func loadViperConfig(v *viper.Viper, c *Config) error {
145146
c.Settings.FlowBellSound = v.GetString(keyFlowBellSound)
146147
c.Settings.FlowDefault = v.GetBool(keyFlowDefault)
147148
c.Settings.ConfettiEnabled = v.GetBool(keyConfettiEnabled)
149+
c.Settings.PreDefinedTags = v.GetStringSlice(keyPreDefinedTags)
148150

149151
c.Notifications.Enabled = v.GetBool(keyNotificationsEnabled)
150152
c.Display.DarkTheme = v.GetBool(keyDarkTheme)

stats/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func (s *Stats) Server(port uint) error {
151151

152152
pterm.Info.Printfln("starting server on port: %d", port)
153153

154-
openbrowser("http://localhost:1111")
154+
openbrowser(fmt.Sprintf("http://localhost:%d", port))
155155

156156
//nolint:gosec // no timeout is ok
157157
return http.ListenAndServe(fmt.Sprintf(":%d", port), mux)

0 commit comments

Comments
 (0)