lolTasks 1.0.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.
loltasks-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Luke J. Stephens
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,3 @@
1
+ include task_spec.md
2
+ include README.md
3
+ include LICENSE
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.4
2
+ Name: lolTasks
3
+ Version: 1.0.0
4
+ Summary: A terminal-based weekly task management application with advanced text editing
5
+ Home-page: https://github.com/lstephensFederation/lolTasks
6
+ Author: Luke J. Stephens
7
+ Author-email: l.stephens@federation.edu.au
8
+ License: MIT
9
+ Project-URL: Bug Reports, https://github.com/lstephensFederation/lolTasks/issues
10
+ Project-URL: Source, https://github.com/lstephensFederation/lolTasks
11
+ Keywords: task management terminal curses weekly planner
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.6
17
+ Classifier: Programming Language :: Python :: 3.7
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.6
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Dynamic: author
27
+ Dynamic: author-email
28
+ Dynamic: classifier
29
+ Dynamic: description
30
+ Dynamic: description-content-type
31
+ Dynamic: home-page
32
+ Dynamic: keywords
33
+ Dynamic: license
34
+ Dynamic: license-file
35
+ Dynamic: project-url
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+ # lolTasks
40
+
41
+ A powerful terminal-based weekly task management application built with Python and curses.
42
+
43
+ ## Features
44
+
45
+ - **Weekly Task Management**: Organize tasks by week with a clean, intuitive interface
46
+ - **Advanced Text Editing**: Full-featured line editor with undo/redo, word navigation, and standard shortcuts
47
+ - **Cross-Platform**: Works on macOS, Linux, and Windows (with appropriate terminal)
48
+ - **Persistent Storage**: JSON-based data storage in your home directory
49
+ - **Vim-Style Shortcuts**: Familiar key bindings for power users
50
+
51
+ ## Installation
52
+
53
+ ### From PyPI (Recommended)
54
+ ```bash
55
+ pip install lolTasks
56
+ ```
57
+
58
+ ### From Source
59
+ ```bash
60
+ git clone https://github.com/lstephensFederation/lolTasks.git
61
+ cd lolTasks
62
+ pip install .
63
+ ```
64
+
65
+ ## Usage
66
+
67
+ ### Basic Usage
68
+ ```bash
69
+ lolTasks
70
+ # or
71
+ task
72
+ ```
73
+
74
+ ### Command Line Options
75
+ ```bash
76
+ lolTasks --help # Show help and key bindings
77
+ ```
78
+
79
+ ## Key Bindings
80
+
81
+ ### Navigation
82
+ - `↑/↓` or `k/j`: Move selection up/down
83
+ - `←/→` or `h/l`: Navigate to previous/next week
84
+ - `Tab`: Cycle task state forward (TO-DO → PENDING → COMPLETED)
85
+ - `Shift+Tab`: Cycle task state backward
86
+
87
+ ### Task Management
88
+ - `a`: Add new task after selected item
89
+ - `Enter`: Edit selected task (or week title if none selected)
90
+ - `I`: Edit task at beginning of line (vim-style)
91
+ - `d`: Delete selected task
92
+ - `r`: Toggle reorder mode for moving tasks up/down
93
+ - `Esc`: Exit edit mode
94
+
95
+ ### Global Actions
96
+ - `Ctrl + U`: Undo last action
97
+ - `Ctrl + R`: Redo last undone action
98
+ - `n`: Move task to next week
99
+ - `p`: Move task to previous week
100
+ - `q`: Quit application
101
+
102
+ ### Text Editing (when in edit mode)
103
+ - `Esc + U`: Undo last change (vim-style)
104
+ - `Esc + R`: Redo last undone change
105
+ - `Option + Left` (macOS): Skip to previous word
106
+ - `Option + Right` (macOS): Skip to next word
107
+ - `Ctrl + A`: Move to start of line
108
+ - `Ctrl + E`: Move to end of line
109
+ - `Left/Right`: Move cursor
110
+ - `Home/End`: Move to start/end of line
111
+ - `Backspace/Delete`: Delete characters
112
+ - `Enter/Esc`: Save and exit edit mode
113
+
114
+ ## Data Storage
115
+
116
+ Tasks are stored in `~/.lolTasks/weekly_tasks.json`. The application automatically creates this directory and file on first run.
117
+
118
+ ## Requirements
119
+
120
+ - Python 3.6+
121
+ - A terminal that supports curses (most modern terminals)
122
+
123
+ ## Development
124
+
125
+ ### Setup Development Environment
126
+ ```bash
127
+ git clone https://github.com/lstephensFederation/lolTasks.git
128
+ cd lolTasks
129
+ pip install -e .
130
+ ```
131
+
132
+ ### Running Tests
133
+ ```bash
134
+ # No tests implemented yet
135
+ ```
136
+
137
+ ## Contributing
138
+
139
+ 1. Fork the repository
140
+ 2. Create a feature branch
141
+ 3. Make your changes
142
+ 4. Test thoroughly
143
+ 5. Submit a pull request
144
+
145
+ ## License
146
+
147
+ MIT License - see LICENSE file for details.
148
+
149
+ ## Changelog
150
+
151
+ ### Version 1.0.0
152
+ - Initial release
153
+ - Complete task management functionality
154
+ - Advanced text editing features
155
+ - Cross-platform compatibility
156
+ - Comprehensive documentation
157
+
158
+ ## Support
159
+
160
+ For issues, questions, or contributions, please visit:
161
+ https://github.com/lstephensFederation/lolTasks
@@ -0,0 +1,123 @@
1
+ # lolTasks
2
+
3
+ A powerful terminal-based weekly task management application built with Python and curses.
4
+
5
+ ## Features
6
+
7
+ - **Weekly Task Management**: Organize tasks by week with a clean, intuitive interface
8
+ - **Advanced Text Editing**: Full-featured line editor with undo/redo, word navigation, and standard shortcuts
9
+ - **Cross-Platform**: Works on macOS, Linux, and Windows (with appropriate terminal)
10
+ - **Persistent Storage**: JSON-based data storage in your home directory
11
+ - **Vim-Style Shortcuts**: Familiar key bindings for power users
12
+
13
+ ## Installation
14
+
15
+ ### From PyPI (Recommended)
16
+ ```bash
17
+ pip install lolTasks
18
+ ```
19
+
20
+ ### From Source
21
+ ```bash
22
+ git clone https://github.com/lstephensFederation/lolTasks.git
23
+ cd lolTasks
24
+ pip install .
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Basic Usage
30
+ ```bash
31
+ lolTasks
32
+ # or
33
+ task
34
+ ```
35
+
36
+ ### Command Line Options
37
+ ```bash
38
+ lolTasks --help # Show help and key bindings
39
+ ```
40
+
41
+ ## Key Bindings
42
+
43
+ ### Navigation
44
+ - `↑/↓` or `k/j`: Move selection up/down
45
+ - `←/→` or `h/l`: Navigate to previous/next week
46
+ - `Tab`: Cycle task state forward (TO-DO → PENDING → COMPLETED)
47
+ - `Shift+Tab`: Cycle task state backward
48
+
49
+ ### Task Management
50
+ - `a`: Add new task after selected item
51
+ - `Enter`: Edit selected task (or week title if none selected)
52
+ - `I`: Edit task at beginning of line (vim-style)
53
+ - `d`: Delete selected task
54
+ - `r`: Toggle reorder mode for moving tasks up/down
55
+ - `Esc`: Exit edit mode
56
+
57
+ ### Global Actions
58
+ - `Ctrl + U`: Undo last action
59
+ - `Ctrl + R`: Redo last undone action
60
+ - `n`: Move task to next week
61
+ - `p`: Move task to previous week
62
+ - `q`: Quit application
63
+
64
+ ### Text Editing (when in edit mode)
65
+ - `Esc + U`: Undo last change (vim-style)
66
+ - `Esc + R`: Redo last undone change
67
+ - `Option + Left` (macOS): Skip to previous word
68
+ - `Option + Right` (macOS): Skip to next word
69
+ - `Ctrl + A`: Move to start of line
70
+ - `Ctrl + E`: Move to end of line
71
+ - `Left/Right`: Move cursor
72
+ - `Home/End`: Move to start/end of line
73
+ - `Backspace/Delete`: Delete characters
74
+ - `Enter/Esc`: Save and exit edit mode
75
+
76
+ ## Data Storage
77
+
78
+ Tasks are stored in `~/.lolTasks/weekly_tasks.json`. The application automatically creates this directory and file on first run.
79
+
80
+ ## Requirements
81
+
82
+ - Python 3.6+
83
+ - A terminal that supports curses (most modern terminals)
84
+
85
+ ## Development
86
+
87
+ ### Setup Development Environment
88
+ ```bash
89
+ git clone https://github.com/lstephensFederation/lolTasks.git
90
+ cd lolTasks
91
+ pip install -e .
92
+ ```
93
+
94
+ ### Running Tests
95
+ ```bash
96
+ # No tests implemented yet
97
+ ```
98
+
99
+ ## Contributing
100
+
101
+ 1. Fork the repository
102
+ 2. Create a feature branch
103
+ 3. Make your changes
104
+ 4. Test thoroughly
105
+ 5. Submit a pull request
106
+
107
+ ## License
108
+
109
+ MIT License - see LICENSE file for details.
110
+
111
+ ## Changelog
112
+
113
+ ### Version 1.0.0
114
+ - Initial release
115
+ - Complete task management functionality
116
+ - Advanced text editing features
117
+ - Cross-platform compatibility
118
+ - Comprehensive documentation
119
+
120
+ ## Support
121
+
122
+ For issues, questions, or contributions, please visit:
123
+ https://github.com/lstephensFederation/lolTasks
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.4
2
+ Name: lolTasks
3
+ Version: 1.0.0
4
+ Summary: A terminal-based weekly task management application with advanced text editing
5
+ Home-page: https://github.com/lstephensFederation/lolTasks
6
+ Author: Luke J. Stephens
7
+ Author-email: l.stephens@federation.edu.au
8
+ License: MIT
9
+ Project-URL: Bug Reports, https://github.com/lstephensFederation/lolTasks/issues
10
+ Project-URL: Source, https://github.com/lstephensFederation/lolTasks
11
+ Keywords: task management terminal curses weekly planner
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.6
17
+ Classifier: Programming Language :: Python :: 3.7
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.6
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Dynamic: author
27
+ Dynamic: author-email
28
+ Dynamic: classifier
29
+ Dynamic: description
30
+ Dynamic: description-content-type
31
+ Dynamic: home-page
32
+ Dynamic: keywords
33
+ Dynamic: license
34
+ Dynamic: license-file
35
+ Dynamic: project-url
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+ # lolTasks
40
+
41
+ A powerful terminal-based weekly task management application built with Python and curses.
42
+
43
+ ## Features
44
+
45
+ - **Weekly Task Management**: Organize tasks by week with a clean, intuitive interface
46
+ - **Advanced Text Editing**: Full-featured line editor with undo/redo, word navigation, and standard shortcuts
47
+ - **Cross-Platform**: Works on macOS, Linux, and Windows (with appropriate terminal)
48
+ - **Persistent Storage**: JSON-based data storage in your home directory
49
+ - **Vim-Style Shortcuts**: Familiar key bindings for power users
50
+
51
+ ## Installation
52
+
53
+ ### From PyPI (Recommended)
54
+ ```bash
55
+ pip install lolTasks
56
+ ```
57
+
58
+ ### From Source
59
+ ```bash
60
+ git clone https://github.com/lstephensFederation/lolTasks.git
61
+ cd lolTasks
62
+ pip install .
63
+ ```
64
+
65
+ ## Usage
66
+
67
+ ### Basic Usage
68
+ ```bash
69
+ lolTasks
70
+ # or
71
+ task
72
+ ```
73
+
74
+ ### Command Line Options
75
+ ```bash
76
+ lolTasks --help # Show help and key bindings
77
+ ```
78
+
79
+ ## Key Bindings
80
+
81
+ ### Navigation
82
+ - `↑/↓` or `k/j`: Move selection up/down
83
+ - `←/→` or `h/l`: Navigate to previous/next week
84
+ - `Tab`: Cycle task state forward (TO-DO → PENDING → COMPLETED)
85
+ - `Shift+Tab`: Cycle task state backward
86
+
87
+ ### Task Management
88
+ - `a`: Add new task after selected item
89
+ - `Enter`: Edit selected task (or week title if none selected)
90
+ - `I`: Edit task at beginning of line (vim-style)
91
+ - `d`: Delete selected task
92
+ - `r`: Toggle reorder mode for moving tasks up/down
93
+ - `Esc`: Exit edit mode
94
+
95
+ ### Global Actions
96
+ - `Ctrl + U`: Undo last action
97
+ - `Ctrl + R`: Redo last undone action
98
+ - `n`: Move task to next week
99
+ - `p`: Move task to previous week
100
+ - `q`: Quit application
101
+
102
+ ### Text Editing (when in edit mode)
103
+ - `Esc + U`: Undo last change (vim-style)
104
+ - `Esc + R`: Redo last undone change
105
+ - `Option + Left` (macOS): Skip to previous word
106
+ - `Option + Right` (macOS): Skip to next word
107
+ - `Ctrl + A`: Move to start of line
108
+ - `Ctrl + E`: Move to end of line
109
+ - `Left/Right`: Move cursor
110
+ - `Home/End`: Move to start/end of line
111
+ - `Backspace/Delete`: Delete characters
112
+ - `Enter/Esc`: Save and exit edit mode
113
+
114
+ ## Data Storage
115
+
116
+ Tasks are stored in `~/.lolTasks/weekly_tasks.json`. The application automatically creates this directory and file on first run.
117
+
118
+ ## Requirements
119
+
120
+ - Python 3.6+
121
+ - A terminal that supports curses (most modern terminals)
122
+
123
+ ## Development
124
+
125
+ ### Setup Development Environment
126
+ ```bash
127
+ git clone https://github.com/lstephensFederation/lolTasks.git
128
+ cd lolTasks
129
+ pip install -e .
130
+ ```
131
+
132
+ ### Running Tests
133
+ ```bash
134
+ # No tests implemented yet
135
+ ```
136
+
137
+ ## Contributing
138
+
139
+ 1. Fork the repository
140
+ 2. Create a feature branch
141
+ 3. Make your changes
142
+ 4. Test thoroughly
143
+ 5. Submit a pull request
144
+
145
+ ## License
146
+
147
+ MIT License - see LICENSE file for details.
148
+
149
+ ## Changelog
150
+
151
+ ### Version 1.0.0
152
+ - Initial release
153
+ - Complete task management functionality
154
+ - Advanced text editing features
155
+ - Cross-platform compatibility
156
+ - Comprehensive documentation
157
+
158
+ ## Support
159
+
160
+ For issues, questions, or contributions, please visit:
161
+ https://github.com/lstephensFederation/lolTasks
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ setup.py
5
+ task.py
6
+ task_spec.md
7
+ lolTasks.egg-info/PKG-INFO
8
+ lolTasks.egg-info/SOURCES.txt
9
+ lolTasks.egg-info/dependency_links.txt
10
+ lolTasks.egg-info/entry_points.txt
11
+ lolTasks.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ lolTasks = task:entry_point
3
+ task = task:entry_point
@@ -0,0 +1 @@
1
+ task
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ lolTasks - A terminal-based weekly task management application
4
+ """
5
+
6
+ from setuptools import setup, find_packages
7
+ import os
8
+
9
+ # Read the README file
10
+ this_directory = os.path.abspath(os.path.dirname(__file__))
11
+ with open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f:
12
+ long_description = f.read()
13
+
14
+ setup(
15
+ name="lolTasks",
16
+ version="1.0.0",
17
+ author="Luke J. Stephens",
18
+ author_email="l.stephens@federation.edu.au",
19
+ description="A terminal-based weekly task management application with advanced text editing",
20
+ long_description=long_description,
21
+ long_description_content_type="text/markdown",
22
+ url="https://github.com/lstephensFederation/lolTasks",
23
+ packages=find_packages(),
24
+ py_modules=['task'],
25
+ include_package_data=True,
26
+ license="MIT",
27
+ classifiers=[
28
+ "Development Status :: 4 - Beta",
29
+ "Intended Audience :: End Users/Desktop",
30
+ "Operating System :: OS Independent",
31
+ "Programming Language :: Python :: 3",
32
+ "Programming Language :: Python :: 3.6",
33
+ "Programming Language :: Python :: 3.7",
34
+ "Programming Language :: Python :: 3.8",
35
+ "Programming Language :: Python :: 3.9",
36
+ "Programming Language :: Python :: 3.10",
37
+ "Programming Language :: Python :: 3.11",
38
+ "Topic :: Utilities",
39
+ ],
40
+ python_requires=">=3.6",
41
+ entry_points={
42
+ 'console_scripts': [
43
+ 'lolTasks=task:entry_point',
44
+ 'task=task:entry_point',
45
+ ],
46
+ },
47
+ keywords="task management terminal curses weekly planner",
48
+ project_urls={
49
+ "Bug Reports": "https://github.com/lstephensFederation/lolTasks/issues",
50
+ "Source": "https://github.com/lstephensFederation/lolTasks",
51
+ },
52
+ )
loltasks-1.0.0/task.py ADDED
@@ -0,0 +1,575 @@
1
+ #!/usr/bin/env python3
2
+ import curses
3
+ import datetime
4
+ import json
5
+ import os
6
+ import sys
7
+ from datetime import timedelta
8
+
9
+ DATA_DIR = os.path.expanduser('~/.lolTasks')
10
+ DATA_FILE = os.path.join(DATA_DIR, 'weekly_tasks.json')
11
+
12
+ STATES = ['TO-DO', 'PENDING', 'COMPLETED']
13
+ STATE_CYCLE_FORWARD = {s: STATES[(i + 1) % 3] for i, s in enumerate(STATES)}
14
+ STATE_CYCLE_BACKWARD = {s: STATES[(i - 1) % 3] for i, s in enumerate(STATES)}
15
+
16
+ # Display symbols (fixed visual width = 4 chars including spaces/brackets)
17
+ STATE_SYMBOLS = {
18
+ 'TO-DO': '[ ] ',
19
+ 'PENDING': '[~] ',
20
+ 'COMPLETED': '[x] ',
21
+ }
22
+
23
+ # For consistent offset calculation during editing (always 4 chars)
24
+ PREFIX_WIDTH = 4
25
+
26
+ COLORS = {'TO-DO': 1, 'PENDING': 2, 'COMPLETED': 3}
27
+
28
+ def get_week_key(date):
29
+ y, w, _ = date.isocalendar()
30
+ return f"{y}-W{w:02d}"
31
+
32
+ def week_to_date(year, week):
33
+ d = datetime.date(year, 1, 4)
34
+ d -= timedelta(days=d.isocalendar()[2] - 1)
35
+ return d + timedelta(weeks=week - 1)
36
+
37
+ def load_data():
38
+ if os.path.exists(DATA_FILE):
39
+ with open(DATA_FILE, 'r') as f:
40
+ return json.load(f)
41
+ return {}
42
+
43
+ def save_data(data):
44
+ os.makedirs(DATA_DIR, exist_ok=True)
45
+ with open(DATA_FILE, 'w') as f:
46
+ json.dump(data, f, indent=4)
47
+
48
+ def get_input(stdscr, base_y, base_x, initial='', start_at_beginning=False):
49
+ """Safer line editor with cursor movement + vim-style start support + undo/redo + word navigation"""
50
+ curses.curs_set(1)
51
+ stdscr.keypad(True)
52
+ s = list(initial)
53
+ pos = 0 if start_at_beginning else len(s)
54
+
55
+ # Undo/redo history
56
+ history = [''.join(s)]
57
+ history_pos = 0
58
+
59
+ _, max_x = stdscr.getmaxyx()
60
+ display_width = max_x - base_x - 2 # margin
61
+
62
+ def save_state():
63
+ nonlocal history, history_pos
64
+ current = ''.join(s)
65
+ # Remove any history after current position
66
+ history = history[:history_pos + 1]
67
+ history.append(current)
68
+ history_pos = len(history) - 1
69
+ # Limit history to 50 entries
70
+ if len(history) > 50:
71
+ history.pop(0)
72
+ history_pos -= 1
73
+
74
+ def undo():
75
+ nonlocal s, pos, history_pos
76
+ if history_pos > 0:
77
+ history_pos -= 1
78
+ s = list(history[history_pos])
79
+ pos = min(pos, len(s))
80
+
81
+ def redo():
82
+ nonlocal s, pos, history_pos
83
+ if history_pos < len(history) - 1:
84
+ history_pos += 1
85
+ s = list(history[history_pos])
86
+ pos = min(pos, len(s))
87
+
88
+ def skip_word_left():
89
+ nonlocal pos
90
+ # Skip whitespace
91
+ while pos > 0 and s[pos - 1].isspace():
92
+ pos -= 1
93
+ # Skip word
94
+ while pos > 0 and not s[pos - 1].isspace():
95
+ pos -= 1
96
+
97
+ def skip_word_right():
98
+ nonlocal pos
99
+ # Skip word
100
+ while pos < len(s) and not s[pos].isspace():
101
+ pos += 1
102
+ # Skip whitespace
103
+ while pos < len(s) and s[pos].isspace():
104
+ pos += 1
105
+
106
+ while True:
107
+ start = max(0, pos - display_width + 5)
108
+ visible = s[start:start + display_width]
109
+ visible_str = ''.join(visible)
110
+
111
+ try:
112
+ stdscr.move(base_y, base_x)
113
+ stdscr.clrtoeol()
114
+ stdscr.addstr(base_y, base_x, visible_str)
115
+ except curses.error:
116
+ pass # Skip if can't draw
117
+ try:
118
+ cursor_x = base_x + (pos - start)
119
+ stdscr.move(base_y, cursor_x)
120
+ except curses.error:
121
+ pass
122
+ stdscr.refresh()
123
+
124
+ key = stdscr.getkey()
125
+
126
+ if key == '\n':
127
+ break
128
+ elif key == '\x01': # Ctrl+A - jump to start of line
129
+ pos = 0
130
+ elif key == '\x05': # Ctrl+E - jump to end of line
131
+ pos = len(s)
132
+ elif key == '\x1b': # Escape key or start of escape sequence
133
+ # Check for escape sequences
134
+ stdscr.nodelay(True)
135
+ try:
136
+ next_key = stdscr.getkey()
137
+ if next_key == 'u': # Esc + U = undo (vim-style)
138
+ undo()
139
+ elif next_key == 'r': # Esc + R = redo (vim-style)
140
+ redo()
141
+ elif next_key == 'b': # Option + Left on macOS (\x1bb)
142
+ skip_word_left()
143
+ elif next_key == 'f': # Option + Right on macOS (\x1bf)
144
+ skip_word_right()
145
+ elif next_key == '[': # CSI sequences
146
+ seq = stdscr.getkey()
147
+ if seq == 'D': # Left arrow
148
+ pos = max(0, pos - 1)
149
+ elif seq == 'C': # Right arrow
150
+ pos = min(len(s), pos + 1)
151
+ elif seq == '1': # \x1b[1~ (Home) or \x1b[1;9D (Option+Left)
152
+ next_char = stdscr.getkey()
153
+ if next_char == '~': # \x1b[1~ - Home
154
+ pos = 0
155
+ elif next_char == ';': # \x1b[1;9D - Option+Left
156
+ modifier = stdscr.getkey()
157
+ direction = stdscr.getkey()
158
+ if modifier == '9' and direction == 'D':
159
+ skip_word_left()
160
+ elif seq == '4': # \x1b[4~ (End)
161
+ next_char = stdscr.getkey()
162
+ if next_char == '~':
163
+ pos = len(s)
164
+ elif seq == '7': # \x1b[7~ (Home on some terminals)
165
+ next_char = stdscr.getkey()
166
+ if next_char == '~':
167
+ pos = 0
168
+ elif seq == '8': # \x1b[8~ (End on some terminals)
169
+ next_char = stdscr.getkey()
170
+ if next_char == '~':
171
+ pos = len(s)
172
+ else:
173
+ # Unknown CSI sequence
174
+ pass
175
+ else:
176
+ # Single escape - exit edit mode
177
+ break
178
+ except curses.error:
179
+ # Timeout or no more keys - treat as single escape
180
+ break
181
+ finally:
182
+ stdscr.nodelay(False)
183
+ elif key in ('KEY_LEFT', 'KEY_BACKSPACE', '\x7f', '\b'):
184
+ save_state()
185
+ if pos > 0:
186
+ pos -= 1
187
+ if key in ('\x7f', '\b'):
188
+ del s[pos]
189
+ elif key == 'KEY_RIGHT':
190
+ if pos < len(s):
191
+ pos += 1
192
+ elif key == 'KEY_HOME':
193
+ pos = 0
194
+ elif key == 'KEY_END':
195
+ pos = len(s)
196
+ elif key == 'KEY_DC':
197
+ save_state()
198
+ if pos < len(s):
199
+ del s[pos]
200
+ elif len(key) == 1 and 32 <= ord(key) <= 126:
201
+ save_state()
202
+ s.insert(pos, key)
203
+ pos += 1
204
+
205
+ curses.curs_set(0)
206
+ return ''.join(s)
207
+
208
+ def show_help():
209
+ print("Weekly Tasks App - task")
210
+ print("Keys:")
211
+ print(" ↑↓/kj Move selection / Reorder (when in reorder mode)")
212
+ print(" r Toggle reorder mode")
213
+ print(" ←→/hl Prev/Next week")
214
+ print(" Tab Cycle state forward")
215
+ print(" Shift+Tab Cycle state backward")
216
+ print(" I Edit current item at start of line (vim-style I)")
217
+ print(" a Add new task after selected")
218
+ print(" Enter Edit selected item (cursor at end)")
219
+ print(" d Delete selected task")
220
+ print(" n / p Shift task next / prev week")
221
+ print(" Ctrl+U Undo last action")
222
+ print(" Ctrl+R Redo last undone action")
223
+ print(" q Quit")
224
+ print()
225
+ print("In edit mode:")
226
+ print(" Esc Exit edit mode")
227
+ print(" Esc+u Undo (vim-style)")
228
+ print(" Esc+r Redo (vim-style)")
229
+ print(" Option+←→ Word navigation")
230
+ print(" Ctrl+A/E Line navigation (start/end)")
231
+ print(" Arrow keys Cursor movement")
232
+ sys.exit(0)
233
+
234
+ def main(stdscr):
235
+ if len(sys.argv) > 1 and sys.argv[1] in ('--help', '-h'):
236
+ show_help()
237
+
238
+ curses.curs_set(0)
239
+ stdscr.keypad(True)
240
+ curses.start_color()
241
+ curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
242
+ curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
243
+ curses.init_pair(3, curses.COLOR_GREEN, curses.COLOR_BLACK)
244
+
245
+ today = datetime.date.today()
246
+ current_week = get_week_key(today)
247
+
248
+ active_week = current_week
249
+ selected = -1
250
+ edit_mode = False
251
+ reorder_mode = False
252
+ scroll_offset = 0
253
+ force_start = False
254
+
255
+ # Global undo/redo history
256
+ undo_history = []
257
+ undo_pos = -1
258
+ MAX_UNDO = 20
259
+
260
+ # Save initial state
261
+ initial_data = load_data()
262
+ undo_history.append(json.dumps(initial_data))
263
+ undo_pos = 0
264
+
265
+ def save_undo_state():
266
+ nonlocal undo_history, undo_pos
267
+ # Save current in-memory data, not from disk
268
+ current_data = data.copy()
269
+ # Remove any history after current position
270
+ undo_history = undo_history[:undo_pos + 1]
271
+ undo_history.append(json.dumps(current_data))
272
+ undo_pos = len(undo_history) - 1
273
+ # Limit history
274
+ if len(undo_history) > MAX_UNDO:
275
+ undo_history.pop(0)
276
+ undo_pos -= 1
277
+
278
+ def undo():
279
+ nonlocal undo_history, undo_pos, selected, active, prev, nxt, data
280
+ if undo_pos > 0:
281
+ undo_pos -= 1
282
+ restored_data = json.loads(undo_history[undo_pos])
283
+ save_data(restored_data)
284
+ # Reload data immediately
285
+ data = load_data()
286
+ active = data[active_week]
287
+ nxt = data[next_week]
288
+ prev = data[prev_week]
289
+ # Adjust selected index
290
+ selected = max(-1, min(selected, len(active['tasks']) - 1))
291
+ return True
292
+ return False
293
+
294
+ def redo():
295
+ nonlocal undo_history, undo_pos, selected, active, prev, nxt, data
296
+ if undo_pos < len(undo_history) - 1:
297
+ undo_pos += 1
298
+ restored_data = json.loads(undo_history[undo_pos])
299
+ save_data(restored_data)
300
+ # Reload data immediately
301
+ data = load_data()
302
+ active = data[active_week]
303
+ nxt = data[next_week]
304
+ prev = data[prev_week]
305
+ # Adjust selected index
306
+ selected = max(-1, min(selected, len(active['tasks']) - 1))
307
+ return True
308
+ return False
309
+
310
+ while True:
311
+ data = load_data()
312
+
313
+ if active_week not in data:
314
+ data[active_week] = {'title': 'Editable Title here for the week', 'tasks': []}
315
+ save_data(data)
316
+
317
+ stdscr.clear()
318
+ maxy, maxx = stdscr.getmaxyx()
319
+
320
+ y, w_part = active_week.split('-W')
321
+ y = int(y)
322
+ w = int(w_part)
323
+ active_date = week_to_date(y, w)
324
+ prev_date = active_date - timedelta(weeks=1)
325
+ next_date = active_date + timedelta(weeks=1)
326
+ prev_week = get_week_key(prev_date)
327
+ next_week = get_week_key(next_date)
328
+
329
+ for wk in [prev_week, active_week, next_week]:
330
+ if wk not in data:
331
+ data[wk] = {'title': 'Week title', 'tasks': []}
332
+ save_data(data)
333
+
334
+ prev = data[prev_week]
335
+ active = data[active_week]
336
+ nxt = data[next_week]
337
+
338
+ selected = max(-1, min(selected, len(active['tasks']) - 1))
339
+
340
+ # Layout positions - ensure active title is always visible
341
+ title_y = 0
342
+ prev_start_y = 2
343
+ prev_end_y = 6 # Previous week: title at 0, sep at 1, tasks at 2-5 (max 4 tasks)
344
+ active_title_y = prev_end_y + 1 # Title at line 7
345
+ active_start_y = active_title_y + 2 # Tasks start at line 9
346
+ next_start_y = maxy - 12 if maxy > 35 else active_start_y + 12
347
+
348
+ # Adjust scroll_offset to make selected visible
349
+ if selected >= 0:
350
+ visible_rows = next_start_y - active_start_y - 1
351
+ if selected < scroll_offset:
352
+ scroll_offset = selected
353
+ elif selected > scroll_offset + visible_rows - 1:
354
+ scroll_offset = selected - (visible_rows - 1)
355
+ scroll_offset = max(0, scroll_offset)
356
+
357
+ # Titles & separators
358
+ def draw_title(y, week_key, text, attr=curses.A_NORMAL):
359
+ if y >= maxy: return # Skip if beyond screen
360
+ label = f"{week_key} – {text}"
361
+ if len(label) > maxx - 6:
362
+ label = label[:maxx-9] + "..."
363
+ stdscr.addstr(y, 2, label, attr)
364
+
365
+ draw_title(title_y, prev_week, prev['title'], curses.A_DIM)
366
+ if title_y + 1 < maxy:
367
+ stdscr.addstr(title_y + 1, 0, "─" * (maxx - 2), curses.A_DIM)
368
+
369
+ draw_title(active_title_y, active_week, active['title'], curses.A_BOLD)
370
+ if active_title_y + 1 < maxy:
371
+ stdscr.addstr(active_title_y + 1, 0, "═" * (maxx - 2), curses.A_BOLD)
372
+
373
+ draw_title(next_start_y - 2, next_week, nxt['title'], curses.A_DIM)
374
+ if next_start_y - 1 < maxy:
375
+ stdscr.addstr(next_start_y - 1, 0, "─" * (maxx - 2), curses.A_DIM)
376
+
377
+ # Improved tasks drawing with word-wrap for selected
378
+ def draw_week_tasks(base_y, week_data, is_active, sel_idx=-1, max_tasks=8, scroll_offset=0, max_y=None):
379
+ if max_y is None:
380
+ max_y = maxy
381
+ max_y = min(max_y, maxy - 2) # Leave room for help bar at maxy-1
382
+ tasks = week_data['tasks']
383
+ y = base_y
384
+ wrap_width = maxx - 16 # margin for prefix + indent
385
+
386
+ start_idx = scroll_offset if is_active else 0
387
+ end_idx = len(tasks)
388
+ if max_tasks is not None:
389
+ end_idx = min(end_idx, start_idx + max_tasks)
390
+
391
+ idx = start_idx
392
+ while idx < end_idx and y < max_y:
393
+ t = tasks[idx]
394
+ prefix = STATE_SYMBOLS[t['state']] # always 4 chars
395
+ text = t['text']
396
+ attr = curses.color_pair(COLORS.get(t['state'], 1))
397
+ if is_active and idx == sel_idx:
398
+ attr |= curses.A_REVERSE
399
+ if not is_active:
400
+ attr |= curses.A_DIM
401
+
402
+ full = prefix + text
403
+ if is_active and idx == sel_idx and len(full) > wrap_width:
404
+ lines = []
405
+ rem = full
406
+ while rem:
407
+ if len(rem) <= wrap_width:
408
+ lines.append(rem)
409
+ break
410
+ split = rem.rfind(' ', 0, wrap_width)
411
+ if split == -1:
412
+ split = wrap_width
413
+ lines.append(rem[:split])
414
+ rem = rem[split:].lstrip()
415
+ if rem:
416
+ rem = ' ' * PREFIX_WIDTH + rem
417
+ for line in lines:
418
+ if y >= max_y: break
419
+ try:
420
+ stdscr.addstr(y, 2, line, attr)
421
+ except curses.error:
422
+ pass
423
+ y += 1
424
+ else:
425
+ if len(full) > wrap_width + 5:
426
+ full = full[:wrap_width - 3] + "..."
427
+ if y < max_y:
428
+ try:
429
+ stdscr.addstr(y, 2, full, attr)
430
+ except curses.error:
431
+ pass
432
+ y += 1
433
+
434
+ idx += 1
435
+
436
+ if idx < len(tasks) and y < max_y:
437
+ stdscr.addstr(y, 2, "... more", curses.A_DIM)
438
+
439
+ draw_week_tasks(prev_start_y, prev, False, max_tasks=4, max_y=active_title_y - 1)
440
+ draw_week_tasks(active_start_y, active, True, selected, max_tasks=None, scroll_offset=scroll_offset, max_y=next_start_y - 2)
441
+ draw_week_tasks(next_start_y, nxt, False, max_tasks=8, max_y=maxy - 1)
442
+
443
+ # Help bar
444
+ mode = " [REORDER]" if reorder_mode else ""
445
+ hint = " (after selected)" if 0 <= selected < len(active['tasks']) else " (at end)"
446
+ help_txt = f"↑↓/kj:Move{'/Reorder'+mode} | r:Reorder | ←→:Week | Tab/S-Tab:State | I:Edit@start | a:Add{hint} | ⏎:Edit | d:Del | n/p:Shift | Ctrl+U:Undo | Ctrl+R:Redo | q:Quit"
447
+ if maxy - 1 < maxy:
448
+ stdscr.addstr(maxy - 1, 0, help_txt[:maxx - 1], curses.A_DIM)
449
+
450
+ stdscr.refresh()
451
+
452
+ if edit_mode:
453
+ if selected == -1:
454
+ offset = len(f"{active_week} – ")
455
+ new_title = get_input(stdscr, active_title_y, 2 + offset,
456
+ active['title'], start_at_beginning=force_start)
457
+ active['title'] = new_title
458
+ else:
459
+ t = active['tasks'][selected]
460
+ offset = PREFIX_WIDTH # ← Fixed!
461
+ edit_y = active_start_y + (selected - scroll_offset)
462
+ new_text = get_input(stdscr, edit_y, 2 + offset,
463
+ t['text'], start_at_beginning=force_start)
464
+ t['text'] = new_text
465
+ save_data(data)
466
+ edit_mode = False
467
+ force_start = False
468
+ continue
469
+
470
+ key = stdscr.getkey()
471
+ force_start = False
472
+
473
+ # Handle control key sequences
474
+ if key == '\x15': # Ctrl+U = undo
475
+ if undo():
476
+ continue
477
+ elif key == '\x12': # Ctrl+R = redo
478
+ if redo():
479
+ continue
480
+
481
+ # Normal mode commands
482
+ if key.lower() == 'q':
483
+ save_data(data)
484
+ break
485
+ elif key.lower() == 'r':
486
+ reorder_mode = not reorder_mode
487
+ elif key == '\t' and 0 <= selected < len(active['tasks']):
488
+ active['tasks'][selected]['state'] = STATE_CYCLE_FORWARD[active['tasks'][selected]['state']]
489
+ save_data(data)
490
+ save_undo_state()
491
+ elif key == '\x1b[Z' and 0 <= selected < len(active['tasks']):
492
+ active['tasks'][selected]['state'] = STATE_CYCLE_BACKWARD[active['tasks'][selected]['state']]
493
+ save_data(data)
494
+ save_undo_state()
495
+ elif key == 'I': # Shift+I - edit at start
496
+ if selected == -1 or 0 <= selected < len(active['tasks']):
497
+ edit_mode = True
498
+ force_start = True
499
+ elif key in ('KEY_UP', 'k'):
500
+ tasks = active['tasks']
501
+ if reorder_mode:
502
+ if selected > 0:
503
+ tasks[selected-1], tasks[selected] = tasks[selected], tasks[selected-1]
504
+ selected -= 1
505
+ save_data(data)
506
+ save_undo_state()
507
+ else:
508
+ if selected > 0:
509
+ selected -= 1
510
+ elif selected == -1 and tasks:
511
+ selected = len(tasks) - 1
512
+ elif key in ('KEY_DOWN', 'j'):
513
+ tasks = active['tasks']
514
+ if reorder_mode:
515
+ if selected < len(tasks) - 1:
516
+ tasks[selected+1], tasks[selected] = tasks[selected], tasks[selected+1]
517
+ selected += 1
518
+ save_data(data)
519
+ save_undo_state()
520
+ else:
521
+ if selected == -1 and tasks:
522
+ selected = 0
523
+ elif selected < len(tasks) - 1:
524
+ selected += 1
525
+ elif selected == len(tasks) - 1:
526
+ selected = -1
527
+ elif key in ('KEY_LEFT', 'h'):
528
+ active_week = prev_week
529
+ selected = -1
530
+ scroll_offset = 0
531
+ elif key in ('KEY_RIGHT', 'l'):
532
+ active_week = next_week
533
+ selected = -1
534
+ scroll_offset = 0
535
+ elif key.lower() == 'a':
536
+ tasks = active['tasks']
537
+ new_task = {'text': 'New task', 'state': 'TO-DO'}
538
+ if selected == -1 or selected >= len(tasks):
539
+ tasks.append(new_task)
540
+ selected = len(tasks) - 1
541
+ else:
542
+ pos = selected + 1
543
+ tasks.insert(pos, new_task)
544
+ selected = pos
545
+ save_data(data)
546
+ save_undo_state()
547
+ edit_mode = True
548
+ force_start = False
549
+ elif key == '\n' and selected is not None:
550
+ edit_mode = True
551
+ force_start = False
552
+ elif key.lower() == 'd' and 0 <= selected < len(active['tasks']):
553
+ del active['tasks'][selected]
554
+ selected = max(-1, selected - 1)
555
+ save_data(data)
556
+ save_undo_state()
557
+ elif key.lower() in ('n', 'p') and 0 <= selected < len(active['tasks']):
558
+ tasks = active['tasks']
559
+ task = tasks.pop(selected)
560
+ delta = 1 if key.lower() == 'n' else -1
561
+ target_date = active_date + timedelta(weeks=delta)
562
+ target_week = get_week_key(target_date)
563
+ if target_week not in data:
564
+ data[target_week] = {'title': 'Week title', 'tasks': []}
565
+ data[target_week]['tasks'].append(task)
566
+ selected = max(-1, min(selected, len(tasks) - 1))
567
+ save_data(data)
568
+ save_undo_state()
569
+
570
+ if __name__ == '__main__':
571
+ curses.wrapper(main)
572
+
573
+ def entry_point():
574
+ """Entry point for console script"""
575
+ curses.wrapper(main)
@@ -0,0 +1,247 @@
1
+ # Weekly Tasks Application Specification
2
+
3
+ ## Overview
4
+
5
+ The Weekly Tasks application is a terminal-based task management tool built with Python and the curses library. It provides a text-based user interface for managing weekly tasks with different completion states, allowing users to organize and track their tasks across multiple weeks.
6
+
7
+ ## Features
8
+
9
+ ### Core Functionality
10
+ - **Weekly Task Management**: Organize tasks by ISO week (YYYY-WW format)
11
+ - **Task States**: Three-state system (TO-DO, PENDING, COMPLETED) with visual indicators
12
+ - **Multi-Week Navigation**: View and navigate between previous, current, and next weeks
13
+ - **Task Operations**: Add, edit, delete, and reorder tasks
14
+ - **State Cycling**: Change task states forward and backward
15
+ - **Task Movement**: Move tasks between weeks
16
+
17
+ ### User Interface
18
+ - **Terminal-Based**: Uses curses for full-screen text interface
19
+ - **Color Coding**: Different colors for each task state (Red=TO-DO, Blue=PENDING, Green=COMPLETED)
20
+ - **Responsive Layout**: Adapts to terminal size with scrollable task lists
21
+ - **Word Wrapping**: Long task descriptions wrap properly for selected items
22
+ - **Visual Indicators**: Checkboxes and symbols for task states
23
+
24
+ ## Data Model
25
+
26
+ ### Storage
27
+ - **Location**: `~/.lolTasks/weekly_tasks.json`
28
+ - **Format**: JSON file with week-based structure
29
+
30
+ ### Structure
31
+ ```json
32
+ {
33
+ "2026-W05": {
34
+ "title": "Week of February 3-9, 2026",
35
+ "tasks": [
36
+ {
37
+ "text": "Complete project documentation",
38
+ "state": "TO-DO"
39
+ },
40
+ {
41
+ "text": "Review code changes",
42
+ "state": "PENDING"
43
+ }
44
+ ]
45
+ }
46
+ }
47
+ ```
48
+
49
+ ### Task States
50
+ - **TO-DO**: `[ ] ` - Red color, initial state
51
+ - **PENDING**: `[~] ` - Blue color, in-progress state
52
+ - **COMPLETED**: `[x] ` - Green color, finished state
53
+
54
+ ## User Interface Layout
55
+
56
+ ```
57
+ Previous Week (YYYY-WW – Title)
58
+ ─────────────────────────────────
59
+ [ ] Task 1
60
+ [~] Task 2
61
+ [x] Task 3
62
+
63
+ Current Week (YYYY-WW – Title)
64
+ ═════════════════════════════════
65
+ [ ] Selected task (highlighted)
66
+ [~] Task 2
67
+ [x] Task 3
68
+ ... more
69
+
70
+ Next Week (YYYY-WW – Title)
71
+ ─────────────────────────────────
72
+ [ ] Task 1
73
+ [~] Task 2
74
+
75
+ ↑↓/kj:Move/Reorder | r:Reorder | ←→:Week | Tab/S-Tab:State | I:Edit@start | a:Add | ⏎:Edit | d:Del | n/p:Shift | q:Quit
76
+ ```
77
+
78
+ ## Key Bindings
79
+
80
+ ### Navigation
81
+ - `↑/↓` or `k/j`: Move selection up/down
82
+ - `←/→` or `h/l`: Navigate to previous/next week
83
+ - `Tab`: Cycle task state forward (TO-DO → PENDING → COMPLETED)
84
+ - `Shift+Tab`: Cycle task state backward
85
+
86
+ ### Task Management
87
+ - `a`: Add new task after selected item
88
+ - `Enter`: Edit selected task (or week title if none selected)
89
+ - `I`: Edit task at beginning of line (vim-style)
90
+ - `d`: Delete selected task
91
+ - `r`: Toggle reorder mode for moving tasks up/down
92
+ - `Esc`: Exit edit mode (same as Enter - saves changes)
93
+
94
+ ### Global Actions
95
+ - `Ctrl + U`: Undo last action (add, delete, edit, reorder, move tasks)
96
+ - `Ctrl + R`: Redo last undone action
97
+ - `n`: Move task to next week
98
+ - `p`: Move task to previous week
99
+ - `q`: Quit application
100
+
101
+ ### Text Editing (when in edit mode)
102
+ - `Esc + U`: Undo last change (vim-style)
103
+ - `Esc + R`: Redo last undone change (vim-style)
104
+ - `Option + Left` (macOS): Skip to previous word
105
+ - `Option + Right` (macOS): Skip to next word
106
+ - `Ctrl + A`: Move to start of line
107
+ - `Ctrl + E`: Move to end of line
108
+ - `Left/Right`: Move cursor
109
+ - `Home/End`: Move to start/end of line
110
+ - `Backspace/Delete`: Delete characters
111
+ - `Enter/Esc`: Save and exit edit mode
112
+
113
+ ### Task Movement
114
+ - `n`: Move task to next week
115
+ - `p`: Move task to previous week
116
+
117
+ ### General
118
+ - `q`: Quit application
119
+
120
+ ## File Structure
121
+
122
+ ```
123
+ ~/.lolTasks/
124
+ ├── task.py # Main application script
125
+ ├── weekly_tasks.json # Task data storage
126
+ ├── weekly_tasks.json.backup # Backup file
127
+ └── __pycache__/ # Python bytecode cache
128
+ ```
129
+
130
+ ## Dependencies
131
+
132
+ ### Required Python Modules
133
+ - `curses`: Terminal user interface (built-in on Unix systems)
134
+ - `datetime`: Date and time handling (built-in)
135
+ - `json`: JSON data serialization (built-in)
136
+ - `os`: Operating system interface (built-in)
137
+ - `sys`: System-specific parameters (built-in)
138
+
139
+ ### System Requirements
140
+ - Unix-like operating system (Linux, macOS)
141
+ - Python 3.x
142
+ - Terminal with curses support
143
+
144
+ ## Installation and Usage
145
+
146
+ ### Installation
147
+ 1. Download `task.py` to a directory in PATH
148
+ 2. Make executable: `chmod +x task.py`
149
+ 3. Run: `./task.py`
150
+
151
+ ### First Run
152
+ - Application creates `~/.lolTasks/` directory automatically
153
+ - Initializes with current week data
154
+ - Shows help with `--help` or `-h` flag
155
+
156
+ ## Technical Implementation
157
+
158
+ ### Architecture
159
+ - **Single File Application**: All code in `task.py`
160
+ - **Event-Driven UI**: Main loop processes keyboard input
161
+ - **In-Memory Data**: Loads/saves JSON data on operations
162
+ - **State Management**: Tracks UI state (selection, modes, scroll)
163
+ - **Global Undo/Redo**: Maintains application-level history for all operations (limited to 20 states)
164
+ - **Edit History**: Maintains undo/redo buffer for text editing (limited to 50 entries)
165
+
166
+ ### Key Functions
167
+ - `main()`: Application entry point with curses wrapper
168
+ - `get_input()`: Safe line editing with cursor movement, undo/redo, and word navigation
169
+ - `draw_week_tasks()`: Render tasks with wrapping and selection
170
+ - `load_data()`/`save_data()`: JSON persistence
171
+ - `get_week_key()`: ISO week calculation
172
+ - `week_to_date()`: Convert week to date
173
+
174
+ ### Error Handling
175
+ - Graceful handling of terminal resize
176
+ - Safe file operations with directory creation
177
+ - Input validation for task operations
178
+ - Fallback for drawing operations that exceed screen bounds
179
+
180
+ ## Future Enhancements
181
+
182
+ Potential features for future versions:
183
+ - Task categories/tags
184
+ - Due dates within weeks
185
+ - Search functionality
186
+ - Export to different formats
187
+ - Synchronization with external services
188
+ - Customizable color schemes
189
+ - Keyboard shortcut customization
190
+
191
+ ## Lessons Learned
192
+
193
+ ### Undo/Redo Implementation
194
+
195
+ **Critical Timing of State Saving:**
196
+ - **Wrong approach**: Saving undo state *before* operations leads to broken redo functionality
197
+ - **Correct approach**: Save undo state *after* operations complete and data is persisted
198
+ - **Why**: Undo needs pre-operation state, redo needs post-operation state
199
+
200
+ **Operation Sequence Matters:**
201
+ ```python
202
+ # Incorrect (breaks redo):
203
+ save_undo_state() # Saves pre-operation state
204
+ modify_data()
205
+ save_data()
206
+
207
+ # Correct (enables proper undo/redo):
208
+ modify_data()
209
+ save_data()
210
+ save_undo_state() # Saves post-operation state
211
+ ```
212
+
213
+ **Data Source for State Saving:**
214
+ - **Wrong**: Loading from disk (`load_data()`) captures stale state
215
+ - **Correct**: Saving current in-memory data (`data.copy()`) captures actual state
216
+
217
+ **UI State Management:**
218
+ - Undo/redo operations must immediately reload all UI variables (`active`, `prev`, `nxt`)
219
+ - Selected index must be adjusted to remain valid after state restoration
220
+ - Avoid relying on main loop data reloading for immediate visual feedback
221
+
222
+ **Debugging Complex State:**
223
+ - Add temporary debug displays to show undo position and history length
224
+ - Implement debug keys to test individual components
225
+ - Use step-by-step verification: undo works → redo works → UI updates correctly
226
+
227
+ ### Terminal Key Binding Compatibility
228
+
229
+ **Escape Sequence Parsing:**
230
+ - **Challenge**: macOS Terminal sends complex escape sequences for modified keys
231
+ - **Solution**: Implement comprehensive CSI sequence parsing with modifier detection
232
+ - **Key Codes**: Handle both standard (\x1b[H/\x1b[F) and modified (\x1b[1;9D) sequences
233
+ - **Alternative**: Use standard Unix shortcuts (Ctrl+A/Ctrl+E) for better cross-platform compatibility
234
+
235
+ **Debugging Key Input:**
236
+ - Add temporary logging to capture actual key codes sent by terminal
237
+ - Test with different terminal applications (Terminal.app, iTerm2, etc.)
238
+ - Verify key bindings in terminal preferences match expected codes
239
+ - Consider standard Unix shortcuts (Ctrl+A/Ctrl+E) for better compatibility
240
+
241
+ **Cross-Terminal Compatibility:**
242
+ - Support multiple escape sequence formats for the same action
243
+ - Handle both standard curses key codes and raw escape sequences
244
+ - Document platform-specific key combinations in help and spec
245
+
246
+ **Key Insight**: Linear undo history with position tracking works well for simple applications, but requires careful state management to avoid corruption.</content>
247
+ <parameter name="filePath">/Users/lstephens/.lolTasks/task_spec.md