pacebar 0.1.0__tar.gz

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.
@@ -0,0 +1,6 @@
1
+ # Store everything with LF in the repo; check out native where it matters.
2
+ * text=auto eol=lf
3
+
4
+ # Windows-only scripts must stay CRLF.
5
+ *.bat text eol=crlf
6
+ *.ps1 text eol=crlf
@@ -0,0 +1,29 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ .venv/
5
+ *.egg-info/
6
+
7
+ # uv
8
+ .uv/
9
+
10
+ # PyInstaller build output (pacebar.spec IS tracked — it is our build config)
11
+ build/
12
+ dist/
13
+
14
+ # Tooling caches
15
+ .ruff_cache/
16
+
17
+ # IDEs
18
+ .idea/
19
+ .vscode/
20
+
21
+ # Claude Code (local config / session state)
22
+ .claude/
23
+
24
+ # App runtime state (written next to the app on each Start)
25
+ pacebar_last_run.json
26
+
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
pacebar-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roman Voronov
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.
pacebar-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: pacebar
3
+ Version: 0.1.0
4
+ Summary: Floating top-strip pacing timer for timeboxed stages — meetings, talks, workouts, even cooking — with countdown and color signalling.
5
+ Author-email: Roman Voronov <roman.voronov.python.developer@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: countdown,meeting,overlay,pacing,presentation,pyside6,timer
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Win32 (MS Windows)
11
+ Classifier: Environment :: X11 Applications :: Qt
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Office/Business
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: pynput>=1.7
24
+ Requires-Dist: pyside6>=6.6
25
+ Provides-Extra: build
26
+ Requires-Dist: pyinstaller>=6.3; extra == 'build'
27
+ Provides-Extra: dev
28
+ Requires-Dist: ruff>=0.6; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # PaceBar
32
+
33
+ ![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white)
34
+ ![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux-555)
35
+ ![License: MIT](https://img.shields.io/badge/License-MIT-green)
36
+ ![Made with Claude Code](https://img.shields.io/badge/Made%20with-Claude%20Code-D97757?logo=anthropic&logoColor=white)
37
+
38
+ A small desktop tool that keeps you on time through any sequence of timed stages —
39
+ a sales call, a talk, a workout, even a recipe. You enter the stages and how many
40
+ minutes each should take; on **Start** a flat,
41
+ always-on-top strip appears across the top of the screen and counts the current
42
+ section down. The strip is **green** while you are on pace, **pastel yellow** when
43
+ the section is almost over, and **pastel red** once you have run over — so you can
44
+ feel the pacing without staring at numbers.
45
+
46
+ Written in Python with **PySide6**. Built cross-platform (Windows + Linux);
47
+ **tested and working on Windows 11**, **not yet tested on Linux** (feedback welcome).
48
+
49
+ ## Screenshots
50
+
51
+ The setup window — one row per section, plus the start / lateness / reset toolbar:
52
+
53
+ ![Setup window](https://raw.githubusercontent.com/Rioran/pacebar/main/docs/images/editor.png)
54
+
55
+ The running strip (here ~2 minutes left on the current section, next section as a
56
+ button), and the minimized square that signals status by color alone:
57
+
58
+ ![Running strip](https://raw.githubusercontent.com/Rioran/pacebar/main/docs/images/strip.png)
59
+
60
+ ![Minimized square](https://raw.githubusercontent.com/Rioran/pacebar/main/docs/images/minimized.png)
61
+
62
+ ## Requirements
63
+
64
+ - [uv](https://docs.astral.sh/uv/) (manages the virtual environment and dependencies)
65
+ - Python 3.10+ (uv can install it for you)
66
+
67
+ ## Install
68
+
69
+ ```bash
70
+ pip install pacebar
71
+ pacebar
72
+ ```
73
+
74
+ This pulls in PySide6 automatically and adds a `pacebar` command. (Prefer the
75
+ standalone exe if you don't want a Python environment at all — see below.)
76
+
77
+ ## Run from source
78
+
79
+ ```bash
80
+ uv sync # create the venv and install dependencies
81
+ uv run pacebar
82
+ ```
83
+
84
+ Or run it in **one click** — double-click the script for your OS:
85
+
86
+ - **Windows:** `scripts\run.bat`
87
+ - **Linux / macOS:** `scripts/run.sh`
88
+
89
+ (`uv sync` runs automatically the first time `uv run` is used.)
90
+
91
+ ## Build a standalone executable
92
+
93
+ One click: double-click `scripts\build.bat` (Windows) or `scripts/build.sh`
94
+ (Linux / macOS). Or run it manually:
95
+
96
+ ```bash
97
+ uv run --extra build pyinstaller --noconfirm --clean pacebar.spec
98
+ ```
99
+
100
+ The build is driven by [`pacebar.spec`](pacebar.spec) (a single
101
+ `--windowed --onefile` build). The result lands in `dist/` (`pacebar.exe` on
102
+ Windows) — one shareable file; it starts a touch slower because it unpacks to a temp
103
+ dir on launch.
104
+
105
+ PyInstaller does **not** cross-compile: it freezes for the OS you run the build on.
106
+ Build on Windows to get the `.exe`, and on Linux (or WSL) to get a Linux binary — each
107
+ runs only on its own platform. For a platform-independent option, use `pip install`
108
+ above instead.
109
+
110
+ ## Lint & format
111
+
112
+ ```bash
113
+ uv run ruff format . # format
114
+ uv run ruff check . # lint
115
+ ```
116
+
117
+ ## Publishing to PyPI (maintainer)
118
+
119
+ ```bash
120
+ uv build # builds the wheel + sdist into dist/
121
+ uv publish dist/pacebar-* # upload (needs a PyPI account + API token)
122
+ ```
123
+
124
+ The `pacebar-*` glob is deliberate: it uploads only the wheel and sdist and skips
125
+ `pacebar.exe`, which also lives in `dist/`. Test on TestPyPI first
126
+ (`uv publish --publish-url https://test.pypi.org/legacy/ ...`), and make sure the
127
+ project name is still available on PyPI before the first real upload.
128
+
129
+ ## How to use
130
+
131
+ **Setup window**
132
+
133
+ - Each row is one section: **minutes** (whole positive number — arrows or type) and a
134
+ **name** (max 30 characters; it blinks red if you try to type more).
135
+ - **Tab / Shift+Tab** move between the two fields of the current row only.
136
+ **Up / Down** (while in the name field) jump to the name field of the row above /
137
+ below. **Enter** adds a new row. The `+` button also adds one; the ▲/▼ control
138
+ reorders; `✕` deletes.
139
+ - **Start** is top-left. Next to it is **minutes late** — how late the meeting is
140
+ actually starting (subtracted from the first section). **Reset** clears everything
141
+ back to one empty row (no confirmation, by design).
142
+ - **Yellow at … % or … s** controls the warning threshold: the strip turns yellow
143
+ when the remaining time drops below *whichever is larger* — that percentage of the
144
+ section, or that many seconds.
145
+
146
+ **Running strip** (left → right)
147
+
148
+ - ◀ **Back** — roll back one section. It resumes on the same global timeline: with the
149
+ time it still had if you switched early, or already overdue if you had overrun it.
150
+ - **Timer** — counts the current section down; format `HH:MM:SS` with the hours group
151
+ hidden when no section is an hour or longer. Goes negative when you run over.
152
+ - **Current section name.**
153
+ - **→ Next section** — click to advance. On the last section it reads **→ End**;
154
+ clicking it exits the program.
155
+ - ▢ **Minimize** — collapses to a small square that signals status by color only.
156
+ Drag it anywhere; click it to restore the full strip.
157
+ - ✕ **Cancel** — quits the program.
158
+
159
+ **Global hotkeys** (work even when another app is focused), laid out like game
160
+ left/right (D = forward, A = back):
161
+
162
+ - **Ctrl+Alt+D** — next section (right); on the last section it triggers **End** (exit)
163
+ - **Ctrl+Alt+A** — back (left)
164
+
165
+ > Chosen to stay clear of common conflicts: plain Alt+A mutes the mic in Zoom and
166
+ > Alt+D jumps to the browser address bar. On non-US layouts Ctrl+Alt equals AltGr,
167
+ > but that only matters while typing into a text field, not during a call.
168
+
169
+ > The strip is intentionally visible to everyone on a screen share — it keeps the
170
+ > whole call honest about time. There is deliberately **no pause**: business calls
171
+ > have hard stops.
172
+
173
+ ## Saved state
174
+
175
+ On every Start the schedule (order, minutes, names — no lateness, no thresholds) is
176
+ written to `pacebar_last_run.json`, and it is loaded back the next time you launch. This
177
+ also makes the tool handy for rehearsing a talk.
178
+
179
+ The file lives **next to the running exe**, so each copy of the app keeps its own
180
+ typical call scenario — drop a copy in a different project folder and it remembers a
181
+ different schedule. (When running from source there is no exe, so it uses the current
182
+ working directory instead.)
183
+
184
+ ## Notes & limitations
185
+
186
+ - Global hotkeys need the `pynput` package (already a dependency). On Linux they work
187
+ under **X11**; under **Wayland** they may be blocked — the on-strip buttons always
188
+ work regardless.
189
+ - Always-on-top is reliable over windowed apps (Zoom/Meet/Teams). A few apps in true
190
+ fullscreen-exclusive mode can still cover any topmost window.
191
+ - The strip width is fixed for the run from the widest section name and worst-case
192
+ timer. An extreme overrun (more minutes over than the longest section) can still
193
+ nudge the timer width.
@@ -0,0 +1,163 @@
1
+ # PaceBar
2
+
3
+ ![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white)
4
+ ![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux-555)
5
+ ![License: MIT](https://img.shields.io/badge/License-MIT-green)
6
+ ![Made with Claude Code](https://img.shields.io/badge/Made%20with-Claude%20Code-D97757?logo=anthropic&logoColor=white)
7
+
8
+ A small desktop tool that keeps you on time through any sequence of timed stages —
9
+ a sales call, a talk, a workout, even a recipe. You enter the stages and how many
10
+ minutes each should take; on **Start** a flat,
11
+ always-on-top strip appears across the top of the screen and counts the current
12
+ section down. The strip is **green** while you are on pace, **pastel yellow** when
13
+ the section is almost over, and **pastel red** once you have run over — so you can
14
+ feel the pacing without staring at numbers.
15
+
16
+ Written in Python with **PySide6**. Built cross-platform (Windows + Linux);
17
+ **tested and working on Windows 11**, **not yet tested on Linux** (feedback welcome).
18
+
19
+ ## Screenshots
20
+
21
+ The setup window — one row per section, plus the start / lateness / reset toolbar:
22
+
23
+ ![Setup window](https://raw.githubusercontent.com/Rioran/pacebar/main/docs/images/editor.png)
24
+
25
+ The running strip (here ~2 minutes left on the current section, next section as a
26
+ button), and the minimized square that signals status by color alone:
27
+
28
+ ![Running strip](https://raw.githubusercontent.com/Rioran/pacebar/main/docs/images/strip.png)
29
+
30
+ ![Minimized square](https://raw.githubusercontent.com/Rioran/pacebar/main/docs/images/minimized.png)
31
+
32
+ ## Requirements
33
+
34
+ - [uv](https://docs.astral.sh/uv/) (manages the virtual environment and dependencies)
35
+ - Python 3.10+ (uv can install it for you)
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install pacebar
41
+ pacebar
42
+ ```
43
+
44
+ This pulls in PySide6 automatically and adds a `pacebar` command. (Prefer the
45
+ standalone exe if you don't want a Python environment at all — see below.)
46
+
47
+ ## Run from source
48
+
49
+ ```bash
50
+ uv sync # create the venv and install dependencies
51
+ uv run pacebar
52
+ ```
53
+
54
+ Or run it in **one click** — double-click the script for your OS:
55
+
56
+ - **Windows:** `scripts\run.bat`
57
+ - **Linux / macOS:** `scripts/run.sh`
58
+
59
+ (`uv sync` runs automatically the first time `uv run` is used.)
60
+
61
+ ## Build a standalone executable
62
+
63
+ One click: double-click `scripts\build.bat` (Windows) or `scripts/build.sh`
64
+ (Linux / macOS). Or run it manually:
65
+
66
+ ```bash
67
+ uv run --extra build pyinstaller --noconfirm --clean pacebar.spec
68
+ ```
69
+
70
+ The build is driven by [`pacebar.spec`](pacebar.spec) (a single
71
+ `--windowed --onefile` build). The result lands in `dist/` (`pacebar.exe` on
72
+ Windows) — one shareable file; it starts a touch slower because it unpacks to a temp
73
+ dir on launch.
74
+
75
+ PyInstaller does **not** cross-compile: it freezes for the OS you run the build on.
76
+ Build on Windows to get the `.exe`, and on Linux (or WSL) to get a Linux binary — each
77
+ runs only on its own platform. For a platform-independent option, use `pip install`
78
+ above instead.
79
+
80
+ ## Lint & format
81
+
82
+ ```bash
83
+ uv run ruff format . # format
84
+ uv run ruff check . # lint
85
+ ```
86
+
87
+ ## Publishing to PyPI (maintainer)
88
+
89
+ ```bash
90
+ uv build # builds the wheel + sdist into dist/
91
+ uv publish dist/pacebar-* # upload (needs a PyPI account + API token)
92
+ ```
93
+
94
+ The `pacebar-*` glob is deliberate: it uploads only the wheel and sdist and skips
95
+ `pacebar.exe`, which also lives in `dist/`. Test on TestPyPI first
96
+ (`uv publish --publish-url https://test.pypi.org/legacy/ ...`), and make sure the
97
+ project name is still available on PyPI before the first real upload.
98
+
99
+ ## How to use
100
+
101
+ **Setup window**
102
+
103
+ - Each row is one section: **minutes** (whole positive number — arrows or type) and a
104
+ **name** (max 30 characters; it blinks red if you try to type more).
105
+ - **Tab / Shift+Tab** move between the two fields of the current row only.
106
+ **Up / Down** (while in the name field) jump to the name field of the row above /
107
+ below. **Enter** adds a new row. The `+` button also adds one; the ▲/▼ control
108
+ reorders; `✕` deletes.
109
+ - **Start** is top-left. Next to it is **minutes late** — how late the meeting is
110
+ actually starting (subtracted from the first section). **Reset** clears everything
111
+ back to one empty row (no confirmation, by design).
112
+ - **Yellow at … % or … s** controls the warning threshold: the strip turns yellow
113
+ when the remaining time drops below *whichever is larger* — that percentage of the
114
+ section, or that many seconds.
115
+
116
+ **Running strip** (left → right)
117
+
118
+ - ◀ **Back** — roll back one section. It resumes on the same global timeline: with the
119
+ time it still had if you switched early, or already overdue if you had overrun it.
120
+ - **Timer** — counts the current section down; format `HH:MM:SS` with the hours group
121
+ hidden when no section is an hour or longer. Goes negative when you run over.
122
+ - **Current section name.**
123
+ - **→ Next section** — click to advance. On the last section it reads **→ End**;
124
+ clicking it exits the program.
125
+ - ▢ **Minimize** — collapses to a small square that signals status by color only.
126
+ Drag it anywhere; click it to restore the full strip.
127
+ - ✕ **Cancel** — quits the program.
128
+
129
+ **Global hotkeys** (work even when another app is focused), laid out like game
130
+ left/right (D = forward, A = back):
131
+
132
+ - **Ctrl+Alt+D** — next section (right); on the last section it triggers **End** (exit)
133
+ - **Ctrl+Alt+A** — back (left)
134
+
135
+ > Chosen to stay clear of common conflicts: plain Alt+A mutes the mic in Zoom and
136
+ > Alt+D jumps to the browser address bar. On non-US layouts Ctrl+Alt equals AltGr,
137
+ > but that only matters while typing into a text field, not during a call.
138
+
139
+ > The strip is intentionally visible to everyone on a screen share — it keeps the
140
+ > whole call honest about time. There is deliberately **no pause**: business calls
141
+ > have hard stops.
142
+
143
+ ## Saved state
144
+
145
+ On every Start the schedule (order, minutes, names — no lateness, no thresholds) is
146
+ written to `pacebar_last_run.json`, and it is loaded back the next time you launch. This
147
+ also makes the tool handy for rehearsing a talk.
148
+
149
+ The file lives **next to the running exe**, so each copy of the app keeps its own
150
+ typical call scenario — drop a copy in a different project folder and it remembers a
151
+ different schedule. (When running from source there is no exe, so it uses the current
152
+ working directory instead.)
153
+
154
+ ## Notes & limitations
155
+
156
+ - Global hotkeys need the `pynput` package (already a dependency). On Linux they work
157
+ under **X11**; under **Wayland** they may be blocked — the on-strip buttons always
158
+ work regardless.
159
+ - Always-on-top is reliable over windowed apps (Zoom/Meet/Teams). A few apps in true
160
+ fullscreen-exclusive mode can still cover any topmost window.
161
+ - The strip width is fixed for the run from the widest section name and worst-case
162
+ timer. An extreme overrun (more minutes over than the longest section) can still
163
+ nudge the timer width.
Binary file
Binary file
Binary file
pacebar-0.1.0/entry.py ADDED
@@ -0,0 +1,13 @@
1
+ """Frozen-build entry point.
2
+
3
+ PyInstaller runs its entry script as the top-level ``__main__`` module with no
4
+ package context, which breaks the relative imports inside ``pacebar``.
5
+ This launcher uses an absolute import so the package loads normally.
6
+ """
7
+
8
+ import sys
9
+
10
+ from pacebar.__main__ import main
11
+
12
+ if __name__ == "__main__":
13
+ sys.exit(main())
@@ -0,0 +1,45 @@
1
+ # -*- mode: python ; coding: utf-8 -*-
2
+ # Standard one-file build. Entry point is entry.py (absolute imports) rather than
3
+ # the package __main__, which would build but crash at launch when frozen.
4
+
5
+ from PyInstaller.utils.hooks import collect_submodules
6
+
7
+ # pynput loads its platform backend dynamically (importlib), so PyInstaller's
8
+ # static analysis misses it. Without these the global hotkeys silently die.
9
+ _HIDDEN_IMPORTS = collect_submodules("pynput")
10
+
11
+ a = Analysis(
12
+ ['entry.py'],
13
+ pathex=['src'],
14
+ binaries=[],
15
+ datas=[],
16
+ hiddenimports=_HIDDEN_IMPORTS,
17
+ hookspath=[],
18
+ hooksconfig={},
19
+ runtime_hooks=[],
20
+ excludes=[],
21
+ noarchive=False,
22
+ optimize=0,
23
+ )
24
+ pyz = PYZ(a.pure)
25
+
26
+ exe = EXE(
27
+ pyz,
28
+ a.scripts,
29
+ a.binaries,
30
+ a.datas,
31
+ [],
32
+ name='pacebar',
33
+ debug=False,
34
+ bootloader_ignore_signals=False,
35
+ strip=False,
36
+ upx=False, # avoid antivirus false-positives on packed Qt DLLs
37
+ upx_exclude=[],
38
+ runtime_tmpdir=None,
39
+ console=False, # GUI app, no console window
40
+ disable_windowed_traceback=False,
41
+ argv_emulation=False,
42
+ target_arch=None,
43
+ codesign_identity=None,
44
+ entitlements_file=None,
45
+ )
@@ -0,0 +1,52 @@
1
+ [project]
2
+ name = "pacebar"
3
+ version = "0.1.0"
4
+ description = "Floating top-strip pacing timer for timeboxed stages — meetings, talks, workouts, even cooking — with countdown and color signalling."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [
10
+ { name = "Roman Voronov", email = "roman.voronov.python.developer@gmail.com" },
11
+ ]
12
+ keywords = ["meeting", "timer", "countdown", "presentation", "pacing", "overlay", "pyside6"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Environment :: X11 Applications :: Qt",
16
+ "Environment :: Win32 (MS Windows)",
17
+ "Intended Audience :: End Users/Desktop",
18
+ "Operating System :: Microsoft :: Windows",
19
+ "Operating System :: POSIX :: Linux",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Office/Business",
26
+ "Topic :: Utilities",
27
+ ]
28
+ dependencies = [
29
+ "PySide6>=6.6",
30
+ "pynput>=1.7",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ build = ["pyinstaller>=6.3"]
35
+ dev = ["ruff>=0.6"]
36
+
37
+ [project.scripts]
38
+ pacebar = "pacebar.__main__:main"
39
+
40
+ [tool.ruff]
41
+ line-length = 100
42
+ src = ["src"]
43
+
44
+ [tool.ruff.lint]
45
+ select = ["E", "F", "I", "W", "B", "UP"]
46
+
47
+ [build-system]
48
+ requires = ["hatchling"]
49
+ build-backend = "hatchling.build"
50
+
51
+ [tool.hatch.build.targets.wheel]
52
+ packages = ["src/pacebar"]
@@ -0,0 +1,7 @@
1
+ @echo off
2
+ REM Double-click to build a slimmed-down standalone exe into dist\ (Windows).
3
+ cd /d "%~dp0\.."
4
+ uv run --extra build pyinstaller --noconfirm --clean pacebar.spec
5
+ echo.
6
+ echo Built: dist\pacebar.exe
7
+ pause
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ # Build a slimmed-down standalone binary into dist/ (Linux/macOS).
3
+ set -e
4
+ cd "$(dirname "$0")/.."
5
+ uv run --extra build pyinstaller --noconfirm --clean pacebar.spec
6
+ echo "Built: dist/pacebar"
@@ -0,0 +1,4 @@
1
+ @echo off
2
+ REM Double-click to run PaceBar from source (Windows).
3
+ cd /d "%~dp0\.."
4
+ uv run pacebar
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ # Run PaceBar from source (Linux/macOS).
3
+ set -e
4
+ cd "$(dirname "$0")/.."
5
+ uv run pacebar
@@ -0,0 +1,3 @@
1
+ """PaceBar — a floating pacing timer for timeboxed stages."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,20 @@
1
+ """Entry point: `python -m pacebar` / the `pacebar` script / the exe."""
2
+
3
+ import sys
4
+
5
+ from PySide6.QtWidgets import QApplication
6
+
7
+ from .app import App
8
+ from .constants import APP_DISPLAY_NAME
9
+
10
+
11
+ def main() -> int:
12
+ qt_application = QApplication(sys.argv)
13
+ qt_application.setApplicationName(APP_DISPLAY_NAME)
14
+ app = App()
15
+ app.show()
16
+ return qt_application.exec()
17
+
18
+
19
+ if __name__ == "__main__":
20
+ sys.exit(main())
@@ -0,0 +1,24 @@
1
+ """Application controller: wires the editor to a run, persists the run on start."""
2
+
3
+ from PySide6.QtWidgets import QApplication
4
+
5
+ from .editor import EditorWindow
6
+ from .overlay import RunController
7
+ from .persistence import save_sections
8
+
9
+
10
+ class App:
11
+ def __init__(self):
12
+ self.editor = EditorWindow()
13
+ self.editor.start_requested.connect(self._start_run)
14
+ self.run: RunController | None = None
15
+
16
+ def show(self):
17
+ self.editor.show()
18
+
19
+ def _start_run(self, sections, lateness_seconds, yellow_percent, yellow_seconds):
20
+ # Persist the schedule (order + minutes + names only) as the run begins.
21
+ save_sections(sections)
22
+ self.editor.hide()
23
+ self.run = RunController(sections, lateness_seconds, yellow_percent, yellow_seconds)
24
+ self.run.finished.connect(QApplication.instance().quit)
@@ -0,0 +1,29 @@
1
+ """Constants shared across modules.
2
+
3
+ Purely cosmetic, single-use dimensions live next to the code that uses them;
4
+ this file holds values that are shared or worth tuning in one place.
5
+ """
6
+
7
+ # Identity and storage
8
+ APP_DISPLAY_NAME = "PaceBar"
9
+ LAST_RUN_FILENAME = "pacebar_last_run.json"
10
+
11
+ # Section / editor limits and defaults
12
+ MIN_SECTION_MINUTES = 1
13
+ MAX_SECTION_MINUTES = 999
14
+ MAX_LATENESS_MINUTES = 999
15
+ MAX_SECTION_NAME_LENGTH = 30
16
+ MAX_YELLOW_PERCENT = 100
17
+ MAX_YELLOW_SECONDS = 3600
18
+ DEFAULT_YELLOW_PERCENT = 10
19
+ DEFAULT_YELLOW_SECONDS = 30
20
+
21
+ # Overlay rendering
22
+ RENDER_INTERVAL_MS = 100 # ~10 Hz; the smallest shown unit is one second
23
+ OVERLAY_FONT_POINT_INCREASE = 2
24
+ OVERLAY_HEIGHT_FACTOR = 1.6
25
+
26
+ # Pastel status colors for the strip and minimized square
27
+ PASTEL_GREEN = "#cfe8cf"
28
+ PASTEL_YELLOW = "#f6efb4"
29
+ PASTEL_RED = "#f2c4c4"
@@ -0,0 +1,5 @@
1
+ """The setup window where the user defines the meeting's sections."""
2
+
3
+ from .window import EditorWindow
4
+
5
+ __all__ = ["EditorWindow"]