minder 0.1 → 0.2.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +35 -12
- data/README.md +10 -4
- data/bin/minder +9 -1
- data/db/migrate/001_create_tasks.rb +15 -0
- data/db/migrate/002_import_tasks.rb +15 -0
- data/lib/minder/application.rb +53 -5
- data/lib/minder/commands/delete_task.rb +9 -0
- data/lib/minder/database.rb +45 -0
- data/lib/minder/database_migrator.rb +13 -0
- data/lib/minder/filter_frame.rb +49 -0
- data/lib/minder/help_frame.rb +33 -0
- data/lib/minder/message_frame.rb +44 -20
- data/lib/minder/pomodoro_frame.rb +0 -1
- data/lib/minder/quick_add_frame.rb +0 -2
- data/lib/minder/scene.rb +46 -14
- data/lib/minder/search_frame.rb +53 -0
- data/lib/minder/task.rb +5 -12
- data/lib/minder/task_mapper.rb +11 -0
- data/lib/minder/task_recorder.rb +63 -8
- data/lib/minder/tasks.rb +9 -0
- data/lib/minder/version.rb +1 -1
- data/lib/minder.rb +3 -0
- data/minder.gemspec +5 -1
- metadata +97 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a57aa002981f78574ea362295234ca6e673c87bb
|
4
|
+
data.tar.gz: fee6f114376157724bd5371bb88dda9bf32fe1b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 409d1644bf65106d27ad1e8893f951bdf4062b85bab27958bfcec605384cc2071da58f0186c7352b2b5949499ce6d1c964d9ff20958445d8515eaaec9b7afb12
|
7
|
+
data.tar.gz: 66cbb70b7a152744008236769fa0e47c3996334162329b80285928235333324184d453fa5aed2d3f297f0932c936a21dce4224df6c785e55b98264386eddb2c2
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/Gemfile.lock
CHANGED
@@ -2,12 +2,22 @@ PATH
|
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
4
|
minder (0.1)
|
5
|
-
|
6
|
-
|
5
|
+
activesupport
|
6
|
+
curses (~> 1.0.0, >= 1.0.1)
|
7
|
+
rom (~> 0.7.0, >= 0.7)
|
8
|
+
rom-sql (~> 0.5.0, >= 0.5)
|
9
|
+
sqlite3
|
10
|
+
virtus (~> 1.0.0, >= 1.0.5)
|
7
11
|
|
8
12
|
GEM
|
9
13
|
remote: https://rubygems.org/
|
10
14
|
specs:
|
15
|
+
activesupport (4.2.1)
|
16
|
+
i18n (~> 0.7)
|
17
|
+
json (~> 1.7, >= 1.7.7)
|
18
|
+
minitest (~> 5.1)
|
19
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
20
|
+
tzinfo (~> 1.1)
|
11
21
|
axiom-types (0.1.1)
|
12
22
|
descendants_tracker (~> 0.0.4)
|
13
23
|
ice_nine (~> 0.11.0)
|
@@ -26,8 +36,11 @@ GEM
|
|
26
36
|
thread_safe (~> 0.3, >= 0.3.1)
|
27
37
|
diff-lcs (1.2.5)
|
28
38
|
equalizer (0.0.11)
|
39
|
+
i18n (0.7.0)
|
29
40
|
ice_nine (0.11.1)
|
41
|
+
json (1.8.2)
|
30
42
|
method_source (0.8.2)
|
43
|
+
minitest (5.7.0)
|
31
44
|
pry (0.10.1)
|
32
45
|
coderay (~> 1.1.0)
|
33
46
|
method_source (~> 0.8.1)
|
@@ -38,23 +51,34 @@ GEM
|
|
38
51
|
pry-stack_explorer (0.4.9.2)
|
39
52
|
binding_of_caller (>= 0.7)
|
40
53
|
pry (>= 0.9.11)
|
41
|
-
|
54
|
+
rom (0.7.1)
|
55
|
+
equalizer (~> 0.0, >= 0.0.9)
|
56
|
+
transproc (~> 0.2, >= 0.2.2)
|
57
|
+
rom-sql (0.5.1)
|
58
|
+
equalizer (~> 0.0, >= 0.0.9)
|
59
|
+
rom (~> 0.7, >= 0.7.0)
|
60
|
+
sequel (~> 4.18)
|
42
61
|
rspec (3.2.0)
|
43
62
|
rspec-core (~> 3.2.0)
|
44
63
|
rspec-expectations (~> 3.2.0)
|
45
64
|
rspec-mocks (~> 3.2.0)
|
46
|
-
rspec-core (3.2.
|
65
|
+
rspec-core (3.2.3)
|
47
66
|
rspec-support (~> 3.2.0)
|
48
|
-
rspec-expectations (3.2.
|
67
|
+
rspec-expectations (3.2.1)
|
49
68
|
diff-lcs (>= 1.2.0, < 2.0)
|
50
69
|
rspec-support (~> 3.2.0)
|
51
70
|
rspec-mocks (3.2.1)
|
52
71
|
diff-lcs (>= 1.2.0, < 2.0)
|
53
72
|
rspec-support (~> 3.2.0)
|
54
73
|
rspec-support (3.2.2)
|
74
|
+
sequel (4.22.0)
|
55
75
|
slop (3.6.0)
|
76
|
+
sqlite3 (1.3.10)
|
56
77
|
thread_safe (0.3.5)
|
57
78
|
timecop (0.7.3)
|
79
|
+
transproc (0.2.2)
|
80
|
+
tzinfo (1.2.2)
|
81
|
+
thread_safe (~> 0.1)
|
58
82
|
virtus (1.0.5)
|
59
83
|
axiom-types (~> 0.1)
|
60
84
|
coercible (~> 1.0)
|
@@ -65,11 +89,10 @@ PLATFORMS
|
|
65
89
|
ruby
|
66
90
|
|
67
91
|
DEPENDENCIES
|
68
|
-
bundler
|
92
|
+
bundler (~> 1)
|
69
93
|
minder!
|
70
|
-
pry
|
71
|
-
pry-byebug
|
72
|
-
pry-stack_explorer
|
73
|
-
|
74
|
-
|
75
|
-
timecop
|
94
|
+
pry (~> 0.10, >= 0.10)
|
95
|
+
pry-byebug (~> 3.1, >= 3.1.0)
|
96
|
+
pry-stack_explorer (~> 0.4.9)
|
97
|
+
rspec (~> 3.2, >= 3.2)
|
98
|
+
timecop (~> 0.7, >= 0.7.3)
|
data/README.md
CHANGED
@@ -11,21 +11,25 @@ At present it has these features:
|
|
11
11
|
soothing sounds of a "pomodoro" kitchen timer at the beginning and end of a
|
12
12
|
break.
|
13
13
|
- A todo log that allows starting, unstarting and completing a task.
|
14
|
-
- Stores a simple text log of task and pomodoro activity.
|
14
|
+
- Stores a simple text log of task and pomodoro activity (in `~/.minder/done.txt`).
|
15
15
|
|
16
16
|
Plans for the future include:
|
17
17
|
|
18
|
-
- Filtering by
|
18
|
+
- Filtering by [Getting Things Done](http://gettingthingsdone.com/)-style labels (like @context, +project, and #tag)
|
19
19
|
- Integration with a website blacklist tool like
|
20
20
|
[SelfControl](https://github.com/SelfControlApp/selfcontrol/).
|
21
21
|
- Keeping track of the number of pomodoros performed during a day.
|
22
22
|
- Desktop notifications for Linux and MacOS
|
23
|
-
- Prompts to summarize plans for the day and day's end progress.
|
23
|
+
- Prompts to summarize plans for the day and day's end progress, inspired by [Day One](http://dayoneapp.com/).
|
24
24
|
|
25
25
|
Audio files are from https://github.com/niftylettuce/pomodoro-timer
|
26
26
|
|
27
27
|
## Usage
|
28
28
|
|
29
|
+
Minder is designed to be run in a separate pane in a Tmux session (or in a split
|
30
|
+
inside a program like iTerm2). The tasks list will scroll if there is not enough
|
31
|
+
room to display all tasks.
|
32
|
+
|
29
33
|
There are three sections. You can switch between sections by pressing Tab. The
|
30
34
|
commands for each section only work when the section is focused.
|
31
35
|
|
@@ -37,7 +41,9 @@ commands for each section only work when the section is focused.
|
|
37
41
|
- Press 'x' to delete a task.
|
38
42
|
- Press 's' to start a task.
|
39
43
|
- Press 'u' to un-start a task.
|
40
|
-
-
|
44
|
+
- Press 'j' to do down the list and 'k' to go up.
|
45
|
+
- Press 'gg' to go to top of task list and 'G' to go to bottom of list.
|
46
|
+
- The Quick Add Task section. Enter text here to add a task to the tasks list.
|
41
47
|
at any time.
|
42
48
|
|
43
49
|
## License
|
data/bin/minder
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'minder'
|
4
|
+
require 'minder/database'
|
5
|
+
database = Database.new
|
6
|
+
DB = database.rom.repositories[:default]
|
7
|
+
|
4
8
|
require 'minder/application'
|
9
|
+
require 'minder/database_migrator'
|
10
|
+
|
11
|
+
migrator = Minder::DatabaseMigrator.new(database: database)
|
12
|
+
migrator.run
|
5
13
|
|
6
|
-
application = Minder::Application.new
|
14
|
+
application = Minder::Application.new(database: database)
|
7
15
|
application.run
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
create_table(:tasks) do
|
4
|
+
primary_key :id
|
5
|
+
String :description, null: false
|
6
|
+
Boolean :selected, default: false
|
7
|
+
Datetime :started_at
|
8
|
+
Datetime :completed_at
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
down do
|
13
|
+
drop_table(:tasks)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
next unless File.exists?(Minder::DOING_FILE)
|
4
|
+
|
5
|
+
tasks = ROM.env.repositories[:default].connection.from(:tasks)
|
6
|
+
tasks.delete
|
7
|
+
|
8
|
+
File.readlines(Minder::DOING_FILE).each do |line|
|
9
|
+
tasks.insert(description: line.strip)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
down do
|
14
|
+
end
|
15
|
+
end
|
data/lib/minder/application.rb
CHANGED
@@ -2,7 +2,7 @@ require 'minder/config'
|
|
2
2
|
require 'minder/pomodoro_runner'
|
3
3
|
require 'minder/task_recorder'
|
4
4
|
require 'minder/scene'
|
5
|
-
|
5
|
+
require 'active_support'
|
6
6
|
require 'curses'
|
7
7
|
require 'fileutils'
|
8
8
|
|
@@ -11,10 +11,17 @@ module Minder
|
|
11
11
|
attr_accessor :config,
|
12
12
|
:scene,
|
13
13
|
:pomodoro_frame,
|
14
|
+
:help_frame,
|
15
|
+
:search_frame,
|
16
|
+
:filter_frame,
|
14
17
|
:message_frame,
|
15
18
|
:quick_add_frame
|
16
19
|
|
17
|
-
|
20
|
+
attr_reader :database
|
21
|
+
|
22
|
+
def initialize(config: Minder::Config.new(CONFIG_LOCATION),
|
23
|
+
database: Database.new)
|
24
|
+
@database = database
|
18
25
|
self.config = config
|
19
26
|
config.load
|
20
27
|
FileUtils.mkdir_p(File.join(ENV['HOME'], '.minder'))
|
@@ -35,12 +42,21 @@ module Minder
|
|
35
42
|
options = { pomodoro_runner: pomodoro_runner, task_manager: task_recorder }
|
36
43
|
|
37
44
|
self.pomodoro_frame = PomodoroFrame.new(options)
|
45
|
+
self.help_frame = HelpFrame.new(options)
|
46
|
+
help_frame.hide
|
47
|
+
self.filter_frame = FilterFrame.new(options)
|
48
|
+
filter_frame.hide
|
49
|
+
self.search_frame = SearchFrame.new(options)
|
50
|
+
search_frame.hide
|
38
51
|
self.message_frame = MessageFrame.new(options)
|
39
52
|
self.quick_add_frame = QuickAddFrame.new(options)
|
40
53
|
quick_add_frame.focus
|
41
54
|
|
42
55
|
scene.frames << pomodoro_frame
|
43
56
|
scene.frames << message_frame
|
57
|
+
scene.frames << help_frame
|
58
|
+
scene.frames << filter_frame
|
59
|
+
scene.frames << search_frame
|
44
60
|
scene.frames << quick_add_frame
|
45
61
|
|
46
62
|
scene.frames.each do |frame|
|
@@ -83,7 +99,7 @@ module Minder
|
|
83
99
|
end
|
84
100
|
|
85
101
|
def task_recorder
|
86
|
-
@task_recorder ||= TaskRecorder.new
|
102
|
+
@task_recorder ||= TaskRecorder.new(database: database)
|
87
103
|
end
|
88
104
|
|
89
105
|
def handle_event(event, data = {})
|
@@ -91,9 +107,9 @@ module Minder
|
|
91
107
|
|
92
108
|
case event
|
93
109
|
when :started_work
|
94
|
-
message_frame.
|
110
|
+
message_frame.minimize
|
95
111
|
when :completed_work
|
96
|
-
message_frame.
|
112
|
+
message_frame.unminimize
|
97
113
|
when :continue
|
98
114
|
pomodoro_runner.continue
|
99
115
|
when :editor
|
@@ -119,6 +135,38 @@ module Minder
|
|
119
135
|
task_recorder.select_last_task
|
120
136
|
when :select_first_task
|
121
137
|
task_recorder.select_first_task
|
138
|
+
when :help
|
139
|
+
message_frame.hide
|
140
|
+
help_frame.unhide
|
141
|
+
scene.focus_frame(help_frame)
|
142
|
+
when :hide_help
|
143
|
+
help_frame.hide
|
144
|
+
message_frame.unhide
|
145
|
+
scene.focus_frame(message_frame)
|
146
|
+
when :open_filter
|
147
|
+
filter_frame.unhide
|
148
|
+
scene.focus_frame(filter_frame)
|
149
|
+
when :submit_filter
|
150
|
+
filter_frame.hide if data[:text] == ''
|
151
|
+
scene.focus_frame(message_frame)
|
152
|
+
when :update_filter
|
153
|
+
task_recorder.filter(data[:text])
|
154
|
+
when :search
|
155
|
+
search_frame.unhide
|
156
|
+
search_frame.begin_search
|
157
|
+
scene.focus_frame(search_frame)
|
158
|
+
when :submit_search
|
159
|
+
search_frame.hide
|
160
|
+
scene.focus_frame(message_frame)
|
161
|
+
task_recorder.search(data[:text])
|
162
|
+
task_recorder.select_search_result
|
163
|
+
when :next_search
|
164
|
+
task_recorder.next_search
|
165
|
+
when :previous_search
|
166
|
+
task_recorder.previous_search
|
167
|
+
when :escape_search
|
168
|
+
search_frame.hide
|
169
|
+
scene.focus_frame(message_frame)
|
122
170
|
end
|
123
171
|
|
124
172
|
scene.redraw
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "sqlite3"
|
2
|
+
require 'rom'
|
3
|
+
require 'rom-sql'
|
4
|
+
|
5
|
+
class Database
|
6
|
+
attr_reader :rom
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
ROM.setup(:sql, Minder::DATABASE_LOCATION)
|
10
|
+
require 'minder/task'
|
11
|
+
require 'minder/task_mapper'
|
12
|
+
require 'minder/tasks'
|
13
|
+
|
14
|
+
ROM.commands(:tasks) do
|
15
|
+
define(:delete)
|
16
|
+
define(:update)
|
17
|
+
end
|
18
|
+
|
19
|
+
@rom = ROM.finalize.env
|
20
|
+
end
|
21
|
+
|
22
|
+
def tasks
|
23
|
+
rom.relation(:tasks).as(:entity).to_a
|
24
|
+
end
|
25
|
+
|
26
|
+
def tasks_filtered_by(text)
|
27
|
+
rom.relation(:tasks).as(:entity).filtered_by(text).to_a
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_task(description)
|
31
|
+
rom.relations.tasks.insert(description: description)
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete_task(task)
|
35
|
+
rom.command(:tasks).delete.by_id(task.id).call
|
36
|
+
end
|
37
|
+
|
38
|
+
def start_task(task)
|
39
|
+
rom.command(:tasks).update.by_id(task.id).call(started_at: Time.now)
|
40
|
+
end
|
41
|
+
|
42
|
+
def unstart_task(task)
|
43
|
+
rom.command(:tasks).update.by_id(task.id).call(started_at: nil)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'minder/frame'
|
2
|
+
|
3
|
+
module Minder
|
4
|
+
class FilterFrame < Frame
|
5
|
+
attr_reader :filter_string
|
6
|
+
|
7
|
+
def initialize(*)
|
8
|
+
super
|
9
|
+
@filter_string = ''
|
10
|
+
end
|
11
|
+
|
12
|
+
def desired_height
|
13
|
+
3
|
14
|
+
end
|
15
|
+
|
16
|
+
def template
|
17
|
+
<<-TEXT
|
18
|
+
Filter: #{filter_string}
|
19
|
+
TEXT
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_cursor_position
|
23
|
+
window.setpos(1, filter_string.length + 9)
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle_char_keypress(key)
|
27
|
+
return unless key
|
28
|
+
|
29
|
+
@filter_string += key
|
30
|
+
refresh
|
31
|
+
changed
|
32
|
+
notify_observers(:update_filter, { text: filter_string })
|
33
|
+
end
|
34
|
+
|
35
|
+
def handle_non_char_keypress(key)
|
36
|
+
if key == 10
|
37
|
+
changed
|
38
|
+
notify_observers(:submit_filter, { text: filter_string })
|
39
|
+
end
|
40
|
+
|
41
|
+
@filter_string.chop! if key == 127
|
42
|
+
|
43
|
+
refresh
|
44
|
+
changed
|
45
|
+
notify_observers(:update_filter, { text: filter_string })
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'minder/frame'
|
2
|
+
|
3
|
+
module Minder
|
4
|
+
class HelpFrame < Frame
|
5
|
+
def template
|
6
|
+
<<-TEXT
|
7
|
+
Commands: any key to dismiss
|
8
|
+
|
9
|
+
(s) start task
|
10
|
+
(u) un-start task
|
11
|
+
(d) mark task as done
|
12
|
+
(x) delete task
|
13
|
+
(e) open tasks in text editor
|
14
|
+
(G) go to bottom of list
|
15
|
+
(gg) go to top of list
|
16
|
+
(/) Search among tasks
|
17
|
+
(n) Next search result
|
18
|
+
(N) Previous search result
|
19
|
+
(?) to view this text
|
20
|
+
TEXT
|
21
|
+
end
|
22
|
+
|
23
|
+
def desired_height
|
24
|
+
template.split("\n").length + 2
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_keypress(key)
|
28
|
+
return unless key
|
29
|
+
changed
|
30
|
+
notify_observers(:hide_help)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/minder/message_frame.rb
CHANGED
@@ -7,23 +7,36 @@ module Minder
|
|
7
7
|
def initialize(*)
|
8
8
|
super
|
9
9
|
self.height = desired_height
|
10
|
+
@minimized = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def minimize
|
14
|
+
@minimized = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def unminimize
|
18
|
+
@minimized = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def minimized?
|
22
|
+
@minimized
|
10
23
|
end
|
11
24
|
|
12
25
|
def template
|
13
|
-
if
|
26
|
+
if minimized?
|
27
|
+
minimized_message
|
28
|
+
elsif task_manager.tasks?
|
14
29
|
doing_message
|
15
30
|
else
|
16
31
|
prompt_message
|
17
32
|
end
|
18
33
|
end
|
19
34
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
#window.refresh(scroll_offset, 0, 3, 0, height + 2, width)
|
26
|
-
#end
|
35
|
+
def minimized_message
|
36
|
+
<<-TEXT
|
37
|
+
Space to see tasks
|
38
|
+
TEXT
|
39
|
+
end
|
27
40
|
|
28
41
|
def prompt_message
|
29
42
|
<<-TEXT
|
@@ -35,7 +48,7 @@ TEXT
|
|
35
48
|
|
36
49
|
def header_text
|
37
50
|
<<-TEXT
|
38
|
-
Tasks
|
51
|
+
Tasks ? to see commands
|
39
52
|
|
40
53
|
TEXT
|
41
54
|
end
|
@@ -61,6 +74,8 @@ TEXT
|
|
61
74
|
end
|
62
75
|
|
63
76
|
def desired_height
|
77
|
+
return 3 if minimized?
|
78
|
+
|
64
79
|
header_text_lines.length + tasks_text_lines.length + 3
|
65
80
|
end
|
66
81
|
|
@@ -72,22 +87,17 @@ TEXT
|
|
72
87
|
tasks_text_lines[scroll_offset..(allocated_tasks_height + scroll_offset - 4)].join("\n")
|
73
88
|
end
|
74
89
|
|
75
|
-
def blank_line
|
76
|
-
" " * (width - 2) + "\n"
|
77
|
-
end
|
78
|
-
|
79
90
|
def doing_message
|
80
91
|
header_text +
|
81
|
-
offset_tasks_text
|
82
|
-
#blank_lines
|
83
|
-
end
|
84
|
-
|
85
|
-
def blank_lines
|
86
|
-
blank_line * scroll_offset
|
92
|
+
offset_tasks_text
|
87
93
|
end
|
88
94
|
|
89
95
|
def set_cursor_position
|
90
|
-
|
96
|
+
if minimized?
|
97
|
+
window.setpos(1, 20)
|
98
|
+
else
|
99
|
+
window.setpos(3 + task_manager.selected_task_index - scroll_offset, 3)
|
100
|
+
end
|
91
101
|
end
|
92
102
|
|
93
103
|
def scroll_offset
|
@@ -108,6 +118,15 @@ TEXT
|
|
108
118
|
when 's' then :start_task
|
109
119
|
when 'u' then :unstart_task
|
110
120
|
when 'G' then :select_last_task
|
121
|
+
when 'e' then :editor
|
122
|
+
when '?' then :help
|
123
|
+
when '/' then :search
|
124
|
+
when 'm'
|
125
|
+
minimize
|
126
|
+
:redraw
|
127
|
+
when 'n' then :next_search
|
128
|
+
when 'N' then :previous_search
|
129
|
+
when 'f' then :open_filter
|
111
130
|
when 'g'
|
112
131
|
@keypress_memory ||= []
|
113
132
|
@keypress_memory << 'g'
|
@@ -115,6 +134,11 @@ TEXT
|
|
115
134
|
@keypress_memory = []
|
116
135
|
:select_first_task
|
117
136
|
end
|
137
|
+
when ' '
|
138
|
+
if minimized?
|
139
|
+
unminimize
|
140
|
+
:redraw
|
141
|
+
end
|
118
142
|
end
|
119
143
|
|
120
144
|
changed
|
data/lib/minder/scene.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
require 'ostruct'
|
2
|
+
require 'minder/help_frame'
|
3
|
+
require 'minder/search_frame'
|
4
|
+
require 'minder/filter_frame'
|
2
5
|
require 'minder/pomodoro_frame'
|
3
6
|
require 'minder/message_frame'
|
4
7
|
require 'minder/quick_add_frame'
|
@@ -34,37 +37,66 @@ module Minder
|
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
40
|
+
def focus_frame(frame)
|
41
|
+
focused_frame.unfocus
|
42
|
+
frame.focus
|
43
|
+
end
|
44
|
+
|
37
45
|
def refresh
|
38
46
|
frames.map(&:refresh)
|
39
47
|
end
|
40
48
|
|
41
49
|
def resize_frames
|
42
50
|
frames.map(&:resize)
|
51
|
+
first_frame = frames.first
|
52
|
+
|
53
|
+
first_frame.move(0, 0)
|
54
|
+
next_height = first_frame.height
|
43
55
|
|
44
|
-
|
56
|
+
other_frames = (frames.reject(&:hidden?) - [main_frame]).compact
|
57
|
+
if main_frame
|
45
58
|
|
46
|
-
|
47
|
-
|
48
|
-
|
59
|
+
other_height = other_frames.reduce(0) do |num, frame|
|
60
|
+
num += frame.height
|
61
|
+
num
|
62
|
+
end
|
63
|
+
|
64
|
+
available_height = Curses.lines - other_height
|
49
65
|
|
50
|
-
|
51
|
-
|
52
|
-
if proposed_height < second_frame.desired_height
|
53
|
-
second_frame.height = proposed_height
|
66
|
+
if available_height > main_frame.desired_height
|
67
|
+
main_frame.height = main_frame.desired_height
|
54
68
|
else
|
55
|
-
|
69
|
+
main_frame.height = available_height
|
56
70
|
end
|
57
|
-
|
58
|
-
|
71
|
+
main_frame.move(next_height, 0)
|
72
|
+
|
73
|
+
next_height += main_frame.height
|
59
74
|
end
|
60
75
|
|
61
|
-
|
62
|
-
|
76
|
+
(other_frames - [first_frame]).each do |frame|
|
77
|
+
frame.move(next_height, 0)
|
78
|
+
next_height += frame.height
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def main_frame
|
83
|
+
return if message_frame.hidden? && help_frame.hidden?
|
84
|
+
|
85
|
+
if message_frame.hidden?
|
86
|
+
help_frame
|
63
87
|
else
|
64
|
-
|
88
|
+
message_frame
|
65
89
|
end
|
66
90
|
end
|
67
91
|
|
92
|
+
def message_frame
|
93
|
+
@message_frame ||= frames.find { |frame| frame.is_a?(MessageFrame) }
|
94
|
+
end
|
95
|
+
|
96
|
+
def help_frame
|
97
|
+
@help_frame ||= frames.find { |frame| frame.is_a?(HelpFrame) }
|
98
|
+
end
|
99
|
+
|
68
100
|
def redraw
|
69
101
|
refresh
|
70
102
|
resize_frames
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'minder/frame'
|
2
|
+
|
3
|
+
module Minder
|
4
|
+
class SearchFrame < Frame
|
5
|
+
attr_reader :search_string
|
6
|
+
|
7
|
+
def initialize(*)
|
8
|
+
super
|
9
|
+
@search_string = ''
|
10
|
+
end
|
11
|
+
|
12
|
+
def desired_height
|
13
|
+
3
|
14
|
+
end
|
15
|
+
|
16
|
+
def template
|
17
|
+
<<-TEXT
|
18
|
+
/#{search_string}
|
19
|
+
TEXT
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_cursor_position
|
23
|
+
window.setpos(1, search_string.length + 2)
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle_char_keypress(key)
|
27
|
+
return unless key
|
28
|
+
|
29
|
+
@search_string += key
|
30
|
+
refresh
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_non_char_keypress(key)
|
34
|
+
case key
|
35
|
+
when 27
|
36
|
+
changed
|
37
|
+
notify_observers(:escape_search)
|
38
|
+
when 127
|
39
|
+
@search_string.chop!
|
40
|
+
refresh
|
41
|
+
when 10
|
42
|
+
changed
|
43
|
+
notify_observers(:submit_search, { text: search_string })
|
44
|
+
refresh
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def begin_search
|
49
|
+
@search_string = ''
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
data/lib/minder/task.rb
CHANGED
@@ -4,21 +4,14 @@ module Minder
|
|
4
4
|
class Task
|
5
5
|
include Virtus.model
|
6
6
|
|
7
|
+
attribute :id, Integer
|
7
8
|
attribute :description, String
|
8
|
-
attribute :selected, Boolean
|
9
|
-
attribute :
|
10
|
-
|
11
|
-
def start
|
12
|
-
self.started = true
|
13
|
-
end
|
14
|
-
|
15
|
-
def unstart
|
16
|
-
self.description.gsub!(/\* /, '')
|
17
|
-
self.started = false
|
18
|
-
end
|
9
|
+
attribute :selected, Boolean
|
10
|
+
attribute :started_at, DateTime
|
11
|
+
attribute :completed_at, DateTime
|
19
12
|
|
20
13
|
def started?
|
21
|
-
|
14
|
+
!!started_at
|
22
15
|
end
|
23
16
|
|
24
17
|
def to_s
|
data/lib/minder/task_recorder.rb
CHANGED
@@ -3,16 +3,36 @@ require 'fileutils'
|
|
3
3
|
|
4
4
|
module Minder
|
5
5
|
class TaskRecorder
|
6
|
-
attr_accessor :
|
7
|
-
:lines
|
6
|
+
attr_accessor :lines
|
8
7
|
|
9
|
-
|
8
|
+
attr_reader :search_results,
|
9
|
+
:database,
|
10
|
+
:tasks
|
11
|
+
|
12
|
+
def initialize(database:)
|
13
|
+
@database = database
|
10
14
|
@selected_task_index = 0
|
15
|
+
@selected_search_result = 0
|
16
|
+
@search_results = []
|
17
|
+
@filter = ''
|
11
18
|
reload
|
12
19
|
end
|
13
20
|
|
21
|
+
def filter(text)
|
22
|
+
@tasks = nil
|
23
|
+
@filter = text
|
24
|
+
end
|
25
|
+
|
14
26
|
def tasks
|
15
|
-
@tasks ||=
|
27
|
+
@tasks ||= fetch_filtered_tasks
|
28
|
+
end
|
29
|
+
|
30
|
+
def unfiltered_tasks
|
31
|
+
database.tasks
|
32
|
+
end
|
33
|
+
|
34
|
+
def fetch_filtered_tasks
|
35
|
+
database.tasks_filtered_by(@filter)
|
16
36
|
end
|
17
37
|
|
18
38
|
def tasks?
|
@@ -23,6 +43,7 @@ module Minder
|
|
23
43
|
File.open(DOING_FILE, 'a') do |file|
|
24
44
|
file.write("#{task}\n")
|
25
45
|
end
|
46
|
+
database.add_task(task)
|
26
47
|
reload
|
27
48
|
end
|
28
49
|
|
@@ -51,7 +72,7 @@ module Minder
|
|
51
72
|
end
|
52
73
|
|
53
74
|
def delete_task
|
54
|
-
|
75
|
+
database.delete_task(selected_task)
|
55
76
|
@tasks = nil
|
56
77
|
write_file_with_backup
|
57
78
|
reload
|
@@ -83,7 +104,7 @@ module Minder
|
|
83
104
|
|
84
105
|
def write_file(path)
|
85
106
|
File.open(path, 'w') do |file|
|
86
|
-
|
107
|
+
unfiltered_tasks.each do |task|
|
87
108
|
line = task.to_s
|
88
109
|
line = "* #{line}" if task.started?
|
89
110
|
file.write("#{line}\n")
|
@@ -92,14 +113,14 @@ module Minder
|
|
92
113
|
end
|
93
114
|
|
94
115
|
def start_task
|
95
|
-
selected_task
|
116
|
+
database.start_task(selected_task)
|
96
117
|
write_file_with_backup
|
97
118
|
add_to_done_file("Started: #{selected_task.description}")
|
98
119
|
reload
|
99
120
|
end
|
100
121
|
|
101
122
|
def unstart_task
|
102
|
-
selected_task
|
123
|
+
database.unstart_task(selected_task)
|
103
124
|
write_file_with_backup
|
104
125
|
add_to_done_file("Un-started: #{selected_task.description}")
|
105
126
|
reload
|
@@ -116,5 +137,39 @@ module Minder
|
|
116
137
|
def select_first_task
|
117
138
|
@selected_task_index = 0
|
118
139
|
end
|
140
|
+
|
141
|
+
def search(text)
|
142
|
+
@search_results = tasks.select do |task|
|
143
|
+
task.description.downcase.include?(text.downcase)
|
144
|
+
end
|
145
|
+
@selected_search_result = 0
|
146
|
+
end
|
147
|
+
|
148
|
+
def select_search_result(search_index = 0)
|
149
|
+
return if search_results.empty?
|
150
|
+
|
151
|
+
@selected_task_index = tasks.find_index do |task|
|
152
|
+
task.description == search_results[search_index].description
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def next_search
|
157
|
+
@selected_search_result += 1
|
158
|
+
|
159
|
+
if @selected_search_result > search_results.length - 1
|
160
|
+
@selected_search_result = 0
|
161
|
+
end
|
162
|
+
select_search_result(@selected_search_result)
|
163
|
+
end
|
164
|
+
|
165
|
+
def previous_search
|
166
|
+
@selected_search_result -= 1
|
167
|
+
|
168
|
+
if @selected_search_result < 0
|
169
|
+
@selected_search_result = search_results.length - 1
|
170
|
+
end
|
171
|
+
|
172
|
+
select_search_result(@selected_search_result)
|
173
|
+
end
|
119
174
|
end
|
120
175
|
end
|
data/lib/minder/tasks.rb
ADDED
data/lib/minder/version.rb
CHANGED
data/lib/minder.rb
CHANGED
@@ -5,6 +5,9 @@ module Minder
|
|
5
5
|
ASSETS_LOCATION = File.expand_path(File.dirname(__FILE__) + '/../assets')
|
6
6
|
DOING_FILE = File.join(ENV["HOME"], '.minder', 'doing.txt')
|
7
7
|
DONE_FILE = File.join(ENV["HOME"], '.minder', 'done.txt')
|
8
|
+
MIGRATIONS_PATH = File.expand_path(File.dirname(__FILE__) + '/../')
|
9
|
+
DATABASE_LOCATION = "sqlite://#{ENV['HOME']}/.minder/database.sqlite3"
|
10
|
+
|
8
11
|
def self.formatted_time(seconds)
|
9
12
|
minutes = (seconds / 60).to_i
|
10
13
|
seconds = (seconds % 60).round
|
data/minder.gemspec
CHANGED
@@ -18,8 +18,12 @@ Gem::Specification.new do |spec|
|
|
18
18
|
|
19
19
|
spec.add_runtime_dependency 'curses', '~> 1.0', '>= 1.0.1'
|
20
20
|
spec.add_runtime_dependency 'virtus', '~> 1.0', '>= 1.0.5'
|
21
|
+
spec.add_runtime_dependency 'rom', '~> 0.7', '>= 0.7'
|
22
|
+
spec.add_runtime_dependency 'rom-sql', '~> 0.5', '>= 0.5'
|
23
|
+
spec.add_runtime_dependency 'sqlite3', '~> 1.3', '>= 1.3.10'
|
24
|
+
spec.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.1'
|
21
25
|
|
22
|
-
spec.add_development_dependency "bundler", '~>
|
26
|
+
spec.add_development_dependency "bundler", '~> 1'
|
23
27
|
spec.add_development_dependency "rspec", '~> 3.2', '>= 3.2'
|
24
28
|
spec.add_development_dependency "timecop", '~> 0.7', '>= 0.7.3'
|
25
29
|
spec.add_development_dependency "pry", '~> 0.10', '>= 0.10'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.2.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph Method
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: curses
|
@@ -50,20 +50,100 @@ dependencies:
|
|
50
50
|
- - ">="
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: 1.0.5
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rom
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0.7'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.7'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.7'
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0.7'
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: rom-sql
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0.5'
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.5'
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.5'
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0.5'
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: sqlite3
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '1.3'
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.3.10
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '1.3'
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 1.3.10
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: activesupport
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '4.2'
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 4.2.1
|
123
|
+
type: :runtime
|
124
|
+
prerelease: false
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '4.2'
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 4.2.1
|
53
133
|
- !ruby/object:Gem::Dependency
|
54
134
|
name: bundler
|
55
135
|
requirement: !ruby/object:Gem::Requirement
|
56
136
|
requirements:
|
57
137
|
- - "~>"
|
58
138
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
139
|
+
version: '1'
|
60
140
|
type: :development
|
61
141
|
prerelease: false
|
62
142
|
version_requirements: !ruby/object:Gem::Requirement
|
63
143
|
requirements:
|
64
144
|
- - "~>"
|
65
145
|
- !ruby/object:Gem::Version
|
66
|
-
version: '
|
146
|
+
version: '1'
|
67
147
|
- !ruby/object:Gem::Dependency
|
68
148
|
name: rspec
|
69
149
|
requirement: !ruby/object:Gem::Requirement
|
@@ -166,6 +246,7 @@ executables:
|
|
166
246
|
extensions: []
|
167
247
|
extra_rdoc_files: []
|
168
248
|
files:
|
249
|
+
- ".gitignore"
|
169
250
|
- ".rspec"
|
170
251
|
- ".ruby-version"
|
171
252
|
- Gemfile
|
@@ -175,11 +256,18 @@ files:
|
|
175
256
|
- assets/done.wav
|
176
257
|
- assets/start.wav
|
177
258
|
- bin/minder
|
259
|
+
- db/migrate/001_create_tasks.rb
|
260
|
+
- db/migrate/002_import_tasks.rb
|
178
261
|
- lib/minder.rb
|
179
262
|
- lib/minder/application.rb
|
180
263
|
- lib/minder/break_period.rb
|
264
|
+
- lib/minder/commands/delete_task.rb
|
181
265
|
- lib/minder/config.rb
|
266
|
+
- lib/minder/database.rb
|
267
|
+
- lib/minder/database_migrator.rb
|
268
|
+
- lib/minder/filter_frame.rb
|
182
269
|
- lib/minder/frame.rb
|
270
|
+
- lib/minder/help_frame.rb
|
183
271
|
- lib/minder/idle_period.rb
|
184
272
|
- lib/minder/message_frame.rb
|
185
273
|
- lib/minder/period.rb
|
@@ -188,8 +276,11 @@ files:
|
|
188
276
|
- lib/minder/pomodoro_runner.rb
|
189
277
|
- lib/minder/quick_add_frame.rb
|
190
278
|
- lib/minder/scene.rb
|
279
|
+
- lib/minder/search_frame.rb
|
191
280
|
- lib/minder/task.rb
|
281
|
+
- lib/minder/task_mapper.rb
|
192
282
|
- lib/minder/task_recorder.rb
|
283
|
+
- lib/minder/tasks.rb
|
193
284
|
- lib/minder/timer.rb
|
194
285
|
- lib/minder/version.rb
|
195
286
|
- minder.gemspec
|
@@ -215,9 +306,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
215
306
|
version: '0'
|
216
307
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
217
308
|
requirements:
|
218
|
-
- - "
|
309
|
+
- - ">"
|
219
310
|
- !ruby/object:Gem::Version
|
220
|
-
version:
|
311
|
+
version: 1.3.1
|
221
312
|
requirements: []
|
222
313
|
rubyforge_project:
|
223
314
|
rubygems_version: 2.4.5
|