intent 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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
+