intent 0.6.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,185 @@
1
+ module Intent
2
+ module Env
3
+ def self.documents_dir
4
+ File.expand_path(ENV['INTENT_DOCUMENTS_DIR'] || "").to_s
5
+ end
6
+
7
+ def self.inbox_dir
8
+ ENV['INTENT_INBOX_DIR']
9
+ end
10
+
11
+ def self.assets_dir
12
+ ENV['INTENT_ARCHIVE_DIR']
13
+ end
14
+
15
+ def self.projects_dir
16
+ ENV['INTENT_PROJECTS_DIR']
17
+ end
18
+ end
19
+
20
+ module Core
21
+ List = ::Todo::List
22
+ Record = ::Todo::Task
23
+
24
+ class Noun
25
+ def initialize(type, label, tags)
26
+ @type = type
27
+ @label = label
28
+ @props = lex_props(tags)
29
+ @tags = tags
30
+ end
31
+
32
+ private
33
+
34
+ def lex_props(tags)
35
+ p tags
36
+ end
37
+ end
38
+
39
+ class Projects
40
+ attr_reader :list
41
+
42
+ def initialize(db_path)
43
+ @list = List.new(db_path)
44
+ end
45
+
46
+ def all
47
+ list.by_not_done
48
+ end
49
+
50
+ def all_tokens
51
+ all.map { |project| project.projects.first }
52
+ end
53
+ end
54
+
55
+ class Inventory
56
+ attr_reader :list
57
+
58
+ def initialize(db_path)
59
+ @list = List.new(db_path)
60
+ end
61
+
62
+ def all
63
+ list.by_not_done
64
+ end
65
+
66
+ def folder_by_id(id)
67
+ all.find { |i| i.tags[:is] == 'folder' && i.tags[:id] == id }
68
+ end
69
+
70
+ def folders
71
+ all.filter { |i| i.tags[:is] == 'folder' }
72
+ end
73
+
74
+ def items_in(id)
75
+ all.filter { |i| i.tags[:in] == id }
76
+ end
77
+
78
+ def unassigned_folders
79
+ all.filter { |i| i.tags[:is] == 'folder' && i.projects.empty? }
80
+ end
81
+
82
+ def assigned_folders
83
+ all.filter { |i| i.tags[:is] == 'folder' && i.projects.any? }
84
+ end
85
+
86
+ def boxes
87
+ all.filter { |i| i.tags[:is] == 'box' }
88
+ end
89
+
90
+ def unassigned_boxes
91
+ all.filter { |i| i.tags[:is] == 'box' && i.projects.empty? }
92
+ end
93
+
94
+ def assigned_boxes
95
+ all.filter { |i| i.tags[:is] == 'box' && i.projects.any? }
96
+ end
97
+
98
+ def units_of(noun)
99
+ all.filter { |i| i.tags[:is] == 'unit' && i.tags[:type] == noun.to_s }
100
+ end
101
+
102
+ def add_unit!(description, type, sku)
103
+ record = Record.new("#{Date.today} #{description} is:unit type:#{type} sku:#{sku}")
104
+ @list.append(record)
105
+ @list.save!
106
+ end
107
+
108
+ def add_item!(description, id, type, sku, box=nil)
109
+ description << " in:#{box}" unless box.nil?
110
+ record = Record.new("#{Date.today} #{description} id:#{id} is:#{type} sku:#{sku}")
111
+ @list.append(record)
112
+ @list.save!
113
+ end
114
+
115
+ def add_folder!(description, id, sku, box=nil)
116
+ add_item!(description, id, :folder, sku, box)
117
+ end
118
+
119
+ def add_box!(description, id, sku)
120
+ add_item!(description, id, :box, sku)
121
+ end
122
+
123
+ def save!
124
+ @list.save!
125
+ end
126
+ end
127
+
128
+ class Inbox
129
+ attr_reader :list
130
+
131
+ def initialize(db_path)
132
+ @list = List.new(db_path)
133
+ end
134
+
135
+ def all
136
+ @list.by_not_done
137
+ end
138
+
139
+ def focused
140
+ @list.by_context('@focus').by_not_done
141
+ end
142
+
143
+ def focused_projects
144
+ focused.map { |t| t.projects }.flatten.uniq
145
+ end
146
+
147
+ def add_line!(line)
148
+ record = Record.new("#{Date.today} #{line}")
149
+ @list.prepend(record)
150
+ @list.save!
151
+ end
152
+ end
153
+
154
+ class Documents
155
+ attr_reader :projects
156
+ attr_reader :inventory
157
+ attr_reader :inbox
158
+
159
+ def initialize
160
+ @projects = Projects.new("#{Intent::Env.documents_dir}/projects.txt")
161
+ @inventory = Inventory.new("#{Intent::Env.documents_dir}/inventory.txt")
162
+ @inbox = Inbox.new("#{Intent::Env.documents_dir}/todo.txt")
163
+ end
164
+ end
165
+ end
166
+
167
+ class Dispatcher
168
+ def self.exec_command(command, args, output=STDOUT)
169
+ command = init_command(command).new
170
+ command.run(args, output)
171
+ end
172
+
173
+ def self.init_command(command)
174
+ case command
175
+ when :intent then return Commands::Intent
176
+ when :inventory then return Commands::Inventory
177
+ when :projects then return Commands::Projects
178
+ when :project then return Commands::Project
179
+ when :todo then return Commands::Todo
180
+ else
181
+ raise Commands::Errors::COMMAND_NOT_FOUND
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,3 @@
1
+ require 'pathname'
2
+
3
+ require 'intent/desktop/commands'
@@ -1,4 +1,4 @@
1
1
  require 'pathname'
2
2
 
3
3
  require 'intent/projects/status'
4
- require 'intent/projects/manager'
4
+ require 'intent/projects/commands'
data/lib/intent/review.rb CHANGED
@@ -1 +1,10 @@
1
- require 'intent/review/manager'
1
+ require 'tty-box'
2
+ require 'tty-screen'
3
+ require 'tty-reader'
4
+ require 'tty-cursor'
5
+ require 'tty-pager'
6
+ require 'pastel'
7
+ require 'todo-txt'
8
+ require 'unicode_plot'
9
+
10
+ require 'intent/review/commands'
@@ -0,0 +1,6 @@
1
+ usage: inventory <verb>
2
+
3
+ Tools for managing a design archive and inventory ledger.
4
+
5
+ inventory list - list items in the inventory
6
+ inventory add <noun> - add a new item to the inventory
@@ -0,0 +1,7 @@
1
+ usage: project <task>
2
+ intent project <task>
3
+
4
+ Individual project tasks
5
+
6
+ project init - initializes a new project
7
+ project link - links an existing project
@@ -0,0 +1,9 @@
1
+ usage: projects <task>
2
+ intent projects <task>
3
+
4
+ A set of tasks for managing a network of plain text projects.
5
+
6
+ projects status - DEPRECATED parses tracked file status in local project directories
7
+ projects active - lists currently active projects
8
+ projects focus - initialise interactive focus navigator
9
+ projects sync - stores synchronised commit of current changes to projects
@@ -0,0 +1,15 @@
1
+ usage: todo <task>
2
+ intent todo <task>
3
+
4
+ A set of tasks for managing a plain text todo list.
5
+
6
+ todo list - list all items in the list
7
+ todo add - add a new task to the list
8
+ todo sample - randomly select a priority task"
9
+ todo projects - list all project tags in the list
10
+ todo contexts - list all context tags in the list
11
+ todo archive - archive completed tasks in the nearest `done.txt`
12
+ todo status - show completion status for all projects
13
+ todo sync - synchronize local changes with remote git repo
14
+ todo collect - collect open browser tabs as items for later reading
15
+ todo focus - block distractions and start focusing
data/lib/intent/todo.rb CHANGED
@@ -5,8 +5,10 @@ require 'fileutils'
5
5
  require 'logger'
6
6
  require 'git'
7
7
  require 'terminal-notifier'
8
+ require 'sorted_set'
8
9
  require 'ghost'
9
- require 'intent/todo/manager'
10
+
11
+ require 'intent/todo/commands'
10
12
 
11
13
  Todo.customize do |options|
12
14
  options.require_completed_on = false
@@ -0,0 +1,80 @@
1
+ module Intent
2
+ module UI
3
+ # Shim to convert between Pastel to Paint gems
4
+ # without needing to edit existing call sites.
5
+ #
6
+ # It might be helpful to normalize this convention with an API
7
+ # anyway as we generally want to use the 8/16 colour defaults
8
+ # as they pick up user terminal customisation properly, whereas
9
+ # going full 256 or 24 bit colour means generic RGB values are likely
10
+ # to look shit on customised terminal backgrounds.
11
+ #
12
+ # This way we get the benefits of *mostly* sticking to the terminal
13
+ # defaults, while extending the range of colours with a few carefully
14
+ # chosen values.
15
+ class TermColor
16
+ def initialize
17
+ @decoration_scope = []
18
+ end
19
+
20
+ def decorate(text, *args)
21
+ decoration_scope.push(*args)
22
+ return_decorator(text)
23
+ end
24
+
25
+ def bold(text=nil)
26
+ decoration_scope.push(:bold)
27
+ return_decorator(text)
28
+ end
29
+
30
+ def red(text=nil)
31
+ decoration_scope.push(:red)
32
+ return_decorator(text)
33
+ end
34
+
35
+ def green(text)
36
+ decoration_scope.push(:green)
37
+ return_decorator(text)
38
+ end
39
+
40
+ def blue(text)
41
+ decoration_scope.push(:blue)
42
+ return_decorator(text)
43
+ end
44
+
45
+ def yellow(text)
46
+ decoration_scope.push(:yellow)
47
+ return_decorator(text)
48
+ end
49
+
50
+ def cyan(text)
51
+ decoration_scope.push(:cyan)
52
+ return_decorator(text)
53
+ end
54
+
55
+ def orange(text)
56
+ decoration_scope.push('orange')
57
+ return_decorator(text)
58
+ end
59
+
60
+ def brown(text)
61
+ decoration_scope.push('tan')
62
+ return_decorator(text)
63
+ end
64
+
65
+ private
66
+
67
+ attr_reader :decoration_scope
68
+
69
+ def return_decorator(text)
70
+ if text.nil?
71
+ self
72
+ else
73
+ decorated = Paint[text, *decoration_scope]
74
+ decoration_scope.clear
75
+ decorated
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,21 @@
1
+ module Intent
2
+ module Verbs
3
+ class Add
4
+ def initialize(documents)
5
+ @documents = documents
6
+ end
7
+
8
+ def invoke_prepend(scope, noun)
9
+ ledger = @documents.send(scope)
10
+ ledger.prepend(new Todo::Task(noun.todo_s))
11
+ ledger.save!
12
+ end
13
+
14
+ def invoke_append(ledger, noun)
15
+ ledger = @documents.send(scope)
16
+ ledger.append(new Todo::Task(noun.todo_s))
17
+ ledger.save!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module Intent
2
+ module Verbs
3
+ class Cite
4
+ attr_reader :documents
5
+ attr_reader :projects
6
+ attr_reader :contexts
7
+ attr_reader :bibliography
8
+
9
+ def initialize(documents)
10
+ @documents = documents
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ module Intent
2
+ module Verbs
3
+ class Install
4
+ attr_reader :documents
5
+
6
+ def initialize(documents)
7
+ @documents = documents
8
+ end
9
+
10
+ def run
11
+ # Preprequisite checks
12
+ # return env_not_detected! unless @documents.env_detected?
13
+ # return already_installed! if @documents.install_detected?
14
+
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,131 @@
1
+ module Intent
2
+ module Review
3
+ class Commands < Intent::CommandDispatcher
4
+ def run(args, output=STDOUT)
5
+ if args.empty?
6
+ launch_densityplot
7
+ else
8
+ print_help(output)
9
+ end
10
+ end
11
+
12
+ def launch_densityplot
13
+ UnicodePlot.barplot(data: {'calyx': 20, 'fictive': 50}, title: "Projects").render
14
+ end
15
+
16
+ def launch_ui_loop
17
+ list = Todo::List.new(ENV['TODO_TXT'])
18
+
19
+ box = TTY::Box.frame(
20
+ width: TTY::Screen.width,
21
+ height: TTY::Screen.height-1,
22
+ align: :center,
23
+ border: :thick,
24
+ style: {
25
+ fg: :bright_yellow,
26
+ border: {
27
+ fg: :white,
28
+ }
29
+ }
30
+ )
31
+
32
+ focus_index = 0
33
+
34
+ def truncate(str, width)
35
+ return str unless str.length >= width
36
+
37
+ "#{str.slice(0, width-3)}..."
38
+ end
39
+
40
+ def draw_card(top, item, focus)
41
+
42
+ style = unless top == focus
43
+ { fg: :white, border: { fg: :white } }
44
+ else
45
+ { fg: :bright_white, border: { fg: :bright_magenta, bg: :black } }
46
+ end
47
+
48
+ title = if top == focus
49
+ {bottom_right: "[x: close][enter: launch]"}
50
+ else
51
+ {}
52
+ end
53
+
54
+ #pastel = Pastel.new
55
+ width = 90
56
+
57
+ TTY::Box.frame(width: width, height: 4, left: 4, title: title, border: :thick, style: style) do
58
+ "\s#{truncate(item[:title], 88)}\n\s#{truncate(item[:href], 88)}"
59
+ end
60
+ end
61
+
62
+ items_per_page = (TTY::Screen.height-1) / 4
63
+
64
+ items = list.by_not_done.by_context("@reading").slice(0, items_per_page).map do |item|
65
+ { title: item.text, href: item.text }
66
+ end
67
+
68
+ $cursor = TTY::Cursor
69
+ print $cursor.hide
70
+
71
+ def draw_list(items, focus)
72
+ buffer = []
73
+ items.each_with_index do |item, i|
74
+ buffer << draw_card(i, item, focus)
75
+ end
76
+ result = buffer.join("")
77
+
78
+ print $cursor.clear_screen + $cursor.move_to(0,0)
79
+ print result
80
+ end
81
+
82
+ draw_list(items, focus_index)
83
+
84
+ reader = TTY::Reader.new(interrupt: :exit)
85
+
86
+ reader.on(:keyctrl_x, :keyescape) do
87
+ print $cursor.clear_screen + $cursor.move_to(0,0)
88
+ print $cursor.show
89
+ exit
90
+ end
91
+
92
+ reader.on(:keytab, :keydown) do
93
+ if focus_index == items.count - 1
94
+ focus_index = 0
95
+ else
96
+ focus_index += 1
97
+ end
98
+ draw_list(items, focus_index)
99
+ end
100
+
101
+ reader.on(:keyup) do
102
+ if focus_index == 0
103
+ focus_index = items.count - 1
104
+ else
105
+ focus_index -= 1
106
+ end
107
+ draw_list(items, focus_index)
108
+ end
109
+
110
+ reader.on(:keypress) do |key|
111
+ if key.value == "x"
112
+ items.delete_at(focus_index)
113
+ focus_index -= 1 if focus_index >= items.count
114
+ draw_list(items, focus_index)
115
+ end
116
+ end
117
+
118
+ reader.on(:keyenter) do
119
+ `chrome-cli open #{items[focus_index][:href]}`
120
+ end
121
+
122
+ loop do
123
+ reader.read_line(echo: false)
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+
131
+
@@ -4,10 +4,10 @@ module Intent
4
4
  List = ::Todo::List
5
5
  Syntax = ::Todo::Syntax
6
6
 
7
- class Manager
7
+ class Commands < Intent::CommandDispatcher
8
8
  include Syntax
9
9
 
10
- def self.run(args, output=STDOUT)
10
+ def run(args, output=STDOUT)
11
11
  if args.empty?
12
12
  print_help(output)
13
13
  else
@@ -183,20 +183,7 @@ module Intent
183
183
  end
184
184
 
185
185
  def self.print_help(output)
186
- output.puts "usage: todo"
187
- output.puts
188
- output.puts "A set of tasks for managing a plain text todo list."
189
- output.puts
190
- output.puts "todo list - list all items in the list"
191
- output.puts "todo add - add a new task to the list"
192
- output.puts "todo sample - randomly select a priority task"
193
- output.puts "todo projects - list all project tags in the list"
194
- output.puts "todo contexts - list all context tags in the list"
195
- output.puts "todo archive - archive completed tasks in the nearest `done.txt`"
196
- output.puts "todo status - show completion status for all projects"
197
- output.puts "todo sync - synchronize local changes with remote git repo"
198
- output.puts "todo collect - collect open browser tabs as items for later reading"
199
- output.puts "todo focus - block distractions and start focusing"
186
+ output.puts File.read('./todo.help.txt')
200
187
  end
201
188
  end
202
189
  end
@@ -1,3 +1,3 @@
1
1
  module Intent
2
- VERSION = '0.6.0'
2
+ VERSION = '0.7.1'
3
3
  end
data/lib/intent.rb CHANGED
@@ -1,4 +1,23 @@
1
- require 'intent/todo'
2
- require 'intent/review'
3
- require 'intent/projects'
1
+ #require 'random'
2
+ require 'todo-txt'
3
+ require 'pastel'
4
+ require 'paint'
5
+ require 'tty-prompt'
6
+ require 'tty-table'
7
+ require 'tty-tree'
8
+ require 'nanoid'
9
+ require 'gem_ext/todo-txt'
10
+
11
+ Todo.customize do |options|
12
+ options.require_completed_on = false
13
+ end
14
+
4
15
  require 'intent/version'
16
+ require 'intent/core'
17
+ require 'intent/commands'
18
+ require 'intent/ui/term_color'
19
+ # require 'intent/todo'
20
+ # require 'intent/review'
21
+ # require 'intent/projects'
22
+ # require 'intent/desktop'
23
+