zen-timer 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zen/__init__.py +1 -0
- zen/__main__.py +11 -0
- zen/timer.py +174 -0
- zen_timer-0.2.0.dist-info/METADATA +124 -0
- zen_timer-0.2.0.dist-info/RECORD +9 -0
- zen_timer-0.2.0.dist-info/WHEEL +5 -0
- zen_timer-0.2.0.dist-info/entry_points.txt +2 -0
- zen_timer-0.2.0.dist-info/licenses/LICENSE +21 -0
- zen_timer-0.2.0.dist-info/top_level.txt +1 -0
zen/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Zen - Deep-work terminal timer."""
|
zen/__main__.py
ADDED
zen/timer.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Zen - Deep-work terminal timer."""
|
|
2
|
+
import time
|
|
3
|
+
import typer
|
|
4
|
+
# pylint: disable=import-outside-toplevel
|
|
5
|
+
# Import rich lazily inside the command for faster --help execution
|
|
6
|
+
|
|
7
|
+
app = typer.Typer(
|
|
8
|
+
help="🧘 Deep-work terminal timer for focus sessions.",
|
|
9
|
+
add_completion=False,
|
|
10
|
+
rich_markup_mode=None,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def version_callback(value: bool) -> None:
|
|
15
|
+
"""Callback for the --version option."""
|
|
16
|
+
if value:
|
|
17
|
+
import importlib.metadata # pylint: disable=import-outside-toplevel
|
|
18
|
+
try:
|
|
19
|
+
version = importlib.metadata.version("zen-timer")
|
|
20
|
+
except importlib.metadata.PackageNotFoundError:
|
|
21
|
+
version = "unknown"
|
|
22
|
+
print(f"zen-timer version {version}")
|
|
23
|
+
raise typer.Exit()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@app.command() # type: ignore[untyped-decorator]
|
|
27
|
+
def focus( # pylint: disable=too-many-statements,too-many-branches
|
|
28
|
+
minutes: int = typer.Argument(
|
|
29
|
+
25, min=1, max=1440, help="Minutes to focus for"
|
|
30
|
+
),
|
|
31
|
+
is_break: bool = typer.Option(
|
|
32
|
+
False, "--break", help="Start a break session."
|
|
33
|
+
),
|
|
34
|
+
silent: bool = typer.Option(
|
|
35
|
+
False, "--silent", help="Run without desktop notifications."
|
|
36
|
+
),
|
|
37
|
+
version: bool = typer.Option( # pylint: disable=unused-argument
|
|
38
|
+
None,
|
|
39
|
+
"--version",
|
|
40
|
+
callback=version_callback,
|
|
41
|
+
is_eager=True,
|
|
42
|
+
help="Show the application version and exit."
|
|
43
|
+
)
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Start a deep-work focus session."""
|
|
46
|
+
# pylint: disable=too-many-locals
|
|
47
|
+
seconds = minutes * 60
|
|
48
|
+
console = None
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
from rich.console import Console
|
|
52
|
+
from rich.progress import (
|
|
53
|
+
Progress, BarColumn, TextColumn, TimeRemainingColumn
|
|
54
|
+
)
|
|
55
|
+
from rich.panel import Panel
|
|
56
|
+
|
|
57
|
+
console = Console()
|
|
58
|
+
console.clear()
|
|
59
|
+
minute_str = "Minute" if minutes == 1 else "Minutes"
|
|
60
|
+
if is_break:
|
|
61
|
+
title_text = (
|
|
62
|
+
f"[bold green]☕ Break Time: {minutes} "
|
|
63
|
+
f"{minute_str}[/bold green]\n"
|
|
64
|
+
"[gray]Rest and recharge.[/gray]"
|
|
65
|
+
)
|
|
66
|
+
border_style = "green"
|
|
67
|
+
task_desc = "[green]Recharging..."
|
|
68
|
+
bar_complete_style = "green"
|
|
69
|
+
else:
|
|
70
|
+
title_text = (
|
|
71
|
+
f"[bold cyan]🧘 Zen Mode Activated: {minutes} "
|
|
72
|
+
f"{minute_str} of Deep Work[/bold cyan]\n"
|
|
73
|
+
"[gray]Do not disturb. No GUI, just flow.[/gray]"
|
|
74
|
+
)
|
|
75
|
+
border_style = "cyan"
|
|
76
|
+
task_desc = "[cyan]Flow State..."
|
|
77
|
+
bar_complete_style = "cyan"
|
|
78
|
+
|
|
79
|
+
title = Panel.fit(
|
|
80
|
+
title_text,
|
|
81
|
+
border_style=border_style,
|
|
82
|
+
padding=(1, 4)
|
|
83
|
+
)
|
|
84
|
+
console.print(title, justify="center")
|
|
85
|
+
console.print("\n")
|
|
86
|
+
|
|
87
|
+
with Progress(
|
|
88
|
+
TextColumn("[progress.description]{task.description}"),
|
|
89
|
+
BarColumn(
|
|
90
|
+
bar_width=None,
|
|
91
|
+
style="magenta",
|
|
92
|
+
complete_style=bar_complete_style,
|
|
93
|
+
finished_style=bar_complete_style
|
|
94
|
+
),
|
|
95
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
96
|
+
TimeRemainingColumn(),
|
|
97
|
+
console=console,
|
|
98
|
+
transient=False,
|
|
99
|
+
auto_refresh=False, # Disable background thread for performance
|
|
100
|
+
) as progress:
|
|
101
|
+
task = progress.add_task(task_desc, total=seconds)
|
|
102
|
+
|
|
103
|
+
start_time = time.monotonic()
|
|
104
|
+
last_second = -1
|
|
105
|
+
|
|
106
|
+
while True:
|
|
107
|
+
elapsed = time.monotonic() - start_time
|
|
108
|
+
remaining = seconds - elapsed
|
|
109
|
+
|
|
110
|
+
if remaining <= 0:
|
|
111
|
+
progress.update(task, completed=seconds, refresh=True)
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
# Only refresh UI when a full second has passed
|
|
115
|
+
current_second = int(elapsed)
|
|
116
|
+
if current_second > last_second:
|
|
117
|
+
progress.update(
|
|
118
|
+
task, completed=elapsed, refresh=True
|
|
119
|
+
)
|
|
120
|
+
last_second = current_second
|
|
121
|
+
|
|
122
|
+
# Recalculate elapsed to natively absorb loop execution
|
|
123
|
+
# overhead
|
|
124
|
+
elapsed = time.monotonic() - start_time
|
|
125
|
+
remaining = seconds - elapsed
|
|
126
|
+
|
|
127
|
+
# Drift-compensated sleep to maintain exact 1Hz refresh rate
|
|
128
|
+
sleep_interval = 1.0 - (elapsed % 1.0)
|
|
129
|
+
time.sleep(max(0, min(sleep_interval, remaining)))
|
|
130
|
+
|
|
131
|
+
console.print("\n")
|
|
132
|
+
if not silent:
|
|
133
|
+
console.bell()
|
|
134
|
+
|
|
135
|
+
if is_break:
|
|
136
|
+
completion = Panel.fit(
|
|
137
|
+
"[bold cyan]✨ Break complete. Ready to focus?[/bold cyan]",
|
|
138
|
+
border_style="cyan"
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
completion = Panel.fit(
|
|
142
|
+
"[bold green]✨ Focus session complete. "
|
|
143
|
+
"Take a break.[/bold green]",
|
|
144
|
+
border_style="green"
|
|
145
|
+
)
|
|
146
|
+
console.print(completion, justify="center")
|
|
147
|
+
except KeyboardInterrupt as exc:
|
|
148
|
+
if is_break:
|
|
149
|
+
msg = "⏸️ Break paused. Rest is important too."
|
|
150
|
+
else:
|
|
151
|
+
msg = "⏸️ Session paused. Your focus still matters."
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
if console is not None:
|
|
155
|
+
console.print("\n")
|
|
156
|
+
interrupted = Panel.fit(
|
|
157
|
+
f"[bold yellow]{msg}[/bold yellow]",
|
|
158
|
+
border_style="yellow"
|
|
159
|
+
)
|
|
160
|
+
console.print(interrupted, justify="center")
|
|
161
|
+
else:
|
|
162
|
+
print(f"\n{msg}")
|
|
163
|
+
except KeyboardInterrupt:
|
|
164
|
+
pass
|
|
165
|
+
raise typer.Exit(code=130) from exc
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def main() -> None:
|
|
169
|
+
"""Main entrypoint for the CLI app."""
|
|
170
|
+
app()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__": # pragma: no cover
|
|
174
|
+
main()
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zen-timer
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: 🧘 Deep-work timer. Simple terminal app for focus sessions. No GUI, just flow.
|
|
5
|
+
Author-email: Shenal Diluksha <shenald@users.noreply.github.com>
|
|
6
|
+
Maintainer-email: shenald-dev <shenald@users.noreply.github.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/shenald-dev/zen
|
|
9
|
+
Project-URL: Documentation, https://github.com/shenald-dev/zen#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/shenald-dev/zen
|
|
11
|
+
Project-URL: Issues, https://github.com/shenald-dev/zen/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/shenald-dev/zen/blob/main/CHANGELOG.md
|
|
13
|
+
Keywords: zen,timer,pomodoro,focus,deep-work,cli,tui,productivity
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: rich<16.0.0,>=13.9.0
|
|
28
|
+
Requires-Dist: typer<1.0.0,>=0.12.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest<10.0.0,>=8.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-mock<4.0.0,>=3.14.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov<8.0.0,>=5.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: flake8<8.0.0,>=7.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pylint<5.0.0,>=3.0.0; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# README.md
|
|
38
|
+
<div align="center">
|
|
39
|
+
<img src="assets/logo.png" alt="zen Logo" width="250" />
|
|
40
|
+
|
|
41
|
+
<br/>
|
|
42
|
+
|
|
43
|
+
<h1>🧘♂️ zen</h1>
|
|
44
|
+
<p><b>Minimalist Terminal Deep-Work Timer</b></p>
|
|
45
|
+
<i>Simple terminal app for focus sessions. No GUI, just flow.</i>
|
|
46
|
+
|
|
47
|
+
<br/>
|
|
48
|
+
|
|
49
|
+
[](https://pypi.org/project/zen-timer/)
|
|
50
|
+
[](https://python.org)
|
|
51
|
+
[](LICENSE)
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 📑 Table of Contents
|
|
57
|
+
- [🌟 Overview](#-overview)
|
|
58
|
+
- [🚀 Enterprise Features](#-enterprise-features)
|
|
59
|
+
- [⚡ Quick Start Guide](#-quick-start-guide)
|
|
60
|
+
- [💻 Comprehensive Usage](#-comprehensive-usage)
|
|
61
|
+
- [🤝 Contributing](#-contributing)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 🌟 Overview
|
|
66
|
+
|
|
67
|
+
**zen** is an ultra-minimalist, distraction-free terminal application built to safeguard developer focus. It ditches complex GUIs in favor of beautiful, native terminal progress indicators. Dive straight into flow state.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 🚀 Enterprise Features
|
|
72
|
+
|
|
73
|
+
- **🌊 Flow State**: Visually stunning, distraction-free TUI progress bar.
|
|
74
|
+
- **⏱️ Flexible**: Fully configurable focus sessions and break reminders.
|
|
75
|
+
- **🎨 Premium Aesthetic**: Powered by `Rich` and `Typer` to look incredible in any modern terminal emulator (iTerm, Kitty, Windows Terminal).
|
|
76
|
+
- **🚀 Performance**: Lightning-fast startup with lazy-loaded imports and zero-overhead UI rendering.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## ⚡ Quick Start Guide
|
|
81
|
+
|
|
82
|
+
### Installation
|
|
83
|
+
|
|
84
|
+
Install via pip:
|
|
85
|
+
```bash
|
|
86
|
+
pip install zen-timer
|
|
87
|
+
```
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 💻 Comprehensive Usage
|
|
91
|
+
|
|
92
|
+
### Start a Session
|
|
93
|
+
To start a default 25-minute Pomodoro session:
|
|
94
|
+
```bash
|
|
95
|
+
zen
|
|
96
|
+
```
|
|
97
|
+
### Advanced Usage
|
|
98
|
+
|
|
99
|
+
**zen** supports additional CLI flags to customize your workflow:
|
|
100
|
+
|
|
101
|
+
- **`--break`**: Run the timer as a break session instead of a focus session. Ideal for timing your rest periods between deep work blocks.
|
|
102
|
+
```bash
|
|
103
|
+
zen 5 --break
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- **`--silent`**: Run the timer without sending desktop notifications upon completion. Perfect for environments where notifications are distracting or unsupported.
|
|
107
|
+
```bash
|
|
108
|
+
zen 25 --silent
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
You can also combine these flags for a silent break session:
|
|
112
|
+
```bash
|
|
113
|
+
zen 15 --break --silent
|
|
114
|
+
```
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 🤝 Contributing
|
|
118
|
+
Want to help safeguard developer focus? Let's flow together! 🌊
|
|
119
|
+
- 🐛 **Found a bug?** Open an issue to let us know.
|
|
120
|
+
- ✨ **Have a feature idea?** We are open to PRs! Keep the UI minimal and the code beautiful.
|
|
121
|
+
- 🎨 **Documentation tweaks?** Always welcome!
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
> *Crafted by a Vibe Coder. Protect your focus.*
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
zen/__init__.py,sha256=37EU0-DX3f1MIZFVrQ5KvO4oTyEonvP_JUJx7FnpFeg,39
|
|
2
|
+
zen/__main__.py,sha256=SaB5HGgJtJ8QGLJmWbpHRjCxM9LjoJ_9dANT3WbI160,194
|
|
3
|
+
zen/timer.py,sha256=U4hzHH2u68On6yTDzYo8zggSB6HIqelzG7uB8kmsFdw,5919
|
|
4
|
+
zen_timer-0.2.0.dist-info/licenses/LICENSE,sha256=S6mLu9vpRpkEYeoHLB7vV8Vh_LWbyvaUbV58jPLjegM,1089
|
|
5
|
+
zen_timer-0.2.0.dist-info/METADATA,sha256=uVC2CXRIKbWXKNW4WHfHi5V7qlWuB_oH_u15W969rfE,4341
|
|
6
|
+
zen_timer-0.2.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
7
|
+
zen_timer-0.2.0.dist-info/entry_points.txt,sha256=D7wSILFvXTbDFZ0_756H33tViF5-5TQ1VdABxi_JGRc,39
|
|
8
|
+
zen_timer-0.2.0.dist-info/top_level.txt,sha256=J6Rg4whb3lEX-U2oo41FON0O6pN2eVNF_0BcX1FwlSY,4
|
|
9
|
+
zen_timer-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 shenald-dev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
zen
|