mars_base_10 0.3.0 → 0.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d66886ffab2da6f395505f68090aa88ea77f7e9bb17669c79a562d9298338227
4
- data.tar.gz: '084f5515249cad9b00649ccf84743e848e835cc3b234f95608efb6bcd5bf5ab7'
3
+ metadata.gz: fd093ec957e418592f791bb2dfc1e1e7efa56973fd553534278a6dcb818f3482
4
+ data.tar.gz: 478b185df1d0c2280788691e4bd61bfac817c200348e63bb29a7a36e05f1eb84
5
5
  SHA512:
6
- metadata.gz: 8c4284d80f4071dfde88bef0f7cf1dfa2f684675a8861b13527740f891bf67bb59e48a6cc95afe627425d1017cd6f355ba8075def3e144ac0066287d90c84ab3
7
- data.tar.gz: 46fdbcd3ddae7f293f21bd067c042555ce235daef318f4431edc2d30ea05ce92f0783bf96c7103455f3c41f15f1c2f2f94b8dc5382780d7a6ea4f2fb89e6e052
6
+ metadata.gz: e9044477c9eb149f2212f5dd5e7e44eb8d0fa247e0db7a382aa5256708ba53027e8c48c18f992b4c2c7dde36281cd5f725f86662283d71f713f50ed6a08662a2
7
+ data.tar.gz: ac4c86d4ab841a92e262138bdf6a290af01d93e65d1afe514d6076681bb217dc59bde4ec2e90ccd3e10d4936ae298387ae0d662565e3030e88658a1f0a78deb6
data/lib/.DS_Store ADDED
Binary file
@@ -11,7 +11,7 @@ module MarsBase10
11
11
  end
12
12
 
13
13
  def self.Default
14
- ActionBar.new actions: {'j': 'Move Down', 'k': 'Move Up', 'q': 'Quit'}
14
+ ActionBar.new actions: {'j': 'Move Down', 'k': 'Move Up', 'J': 'Page Down', 'K': 'Page Up', 'q': 'Quit'}
15
15
  end
16
16
 
17
17
  def actions_first_col
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'urbit/urbit'
2
+ require 'urbit'
3
3
 
4
4
  require_relative 'graph_rover'
5
5
  require_relative 'viewport'
@@ -14,66 +14,69 @@ module MarsBase10
14
14
  @viewport = viewport
15
15
  @viewport.controller = self
16
16
 
17
- @panes = []
17
+ self.wire_up_panes
18
+ self.viewport.action_bar = ActionBar.Default.add_action({'i': 'Inspect'})
19
+ self.viewport.activate pane: @graph_list_pane
20
+ self.resync
21
+ end
18
22
 
19
- # The graph list is a fixed width, variable height (full screen) pane on the left.
20
- @graph_list_pane = @viewport.add_pane width_pct: 0.3
21
- @graph_list_pane.viewing subject: @ship.graph_names
23
+ def active_node_index
24
+ @node_list_pane.current_subject_index
25
+ end
22
26
 
23
- # The node list is a variable width, fixed height pane in the upper right.
24
- @node_list_pane = @viewport.add_variable_width_pane at_col: @graph_list_pane.last_col,
25
- height_pct: 0.5
26
- @node_list_pane.viewing subject: @ship.node_list
27
+ def active_node
28
+ self.ship.fetch_node(resource: self.active_resource, index: self.active_node_index)
29
+ end
27
30
 
28
- # The single node viewer is a variable width, variable height pane in the lower right.
29
- @node_view_pane = @viewport.add_variable_both_pane at_row: @node_list_pane.last_row,
30
- at_col: @graph_list_pane.last_col
31
- @node_view_pane.viewing subject: @ship.node
31
+ def active_resource
32
+ @graph_list_pane.current_subject_index
33
+ end
32
34
 
33
- self.viewport.action_bar = ActionBar.Default.add_action({'i': 'Inspect'})
34
- self.viewport.activate pane: @graph_list_pane
35
- self.resync
35
+ def load_history
36
+ return 0 unless @node_list_pane == self.viewport.active_pane
37
+ new_content = self.ship.fetch_older_nodes(resource: self.active_resource, node: self.active_node)
38
+ @node_list_pane.subject.prepend_content(ary: new_content)
39
+ new_content.length
36
40
  end
37
41
 
38
42
  #
39
43
  # Called by a pane in this controller for bubbling a key press up
40
44
  #
41
45
  def send(key:)
46
+ resync_needed = true
42
47
  case key
43
48
  when 'd' # (D)ive
44
49
  begin
45
50
  if @node_view_pane.subject.contents[4].include?('true')
46
51
  self.viewport.action_bar.add_action({'p': 'Pop Out'})
47
- resource = @graph_list_pane.current_subject
48
- node_index = @node_list_pane.current_subject
49
- @stack.push(resource)
52
+ @stack.push(self.active_resource)
50
53
  @node_list_pane.clear
51
- @node_list_pane.subject.contents = self.ship.fetch_node_children resource: resource, index: node_index
52
- @node_list_pane.index = 0
54
+ @node_list_pane.subject.contents = self.ship.fetch_node_children(resource: self.active_resource, index: self.active_node_index)
53
55
  end
54
56
  end
55
57
  when 'i' # (I)nspect
56
58
  begin
57
59
  self.viewport.activate pane: @node_list_pane
58
60
  self.viewport.action_bar = ActionBar.Default.add_action({'d': 'Dive In', 'g': 'Graph List'})
61
+ resync_needed = false
59
62
  end
60
63
  when 'g' # (G)raph View
61
64
  unless @graph_list_pane.active?
62
65
  self.viewport.activate pane: @graph_list_pane
66
+ # resync_needed = false
63
67
  end
64
68
  when 'p' # (P)op
65
69
  begin
66
70
  if (resource = @stack.pop)
67
71
  @node_list_pane.clear
68
72
  @node_list_pane.subject.contents = self.ship.fetch_node_list(resource: resource)
69
- @node_list_pane.index = 0
70
73
  end
71
74
  if (@stack.length == 0)
72
75
  self.viewport.action_bar.remove_action(:p)
73
76
  end
74
77
  end
75
78
  end
76
- self.resync
79
+ self.resync if resync_needed
77
80
  end
78
81
 
79
82
  def start
@@ -87,25 +90,24 @@ module MarsBase10
87
90
  private
88
91
 
89
92
  def resync
90
- self.resync_node_view(self.resync_node_list)
93
+ self.resync_node_list
94
+ self.resync_node_view
91
95
  end
92
96
 
93
97
  def resync_node_list
94
- resource = @graph_list_pane.current_subject
95
98
  if @graph_list_pane == self.viewport.active_pane
96
- @node_list_pane.subject.title = "Nodes of #{resource}"
97
99
  @node_list_pane.clear
98
- @node_list_pane.subject.first_row = 0
99
- @node_list_pane.subject.contents = self.ship.fetch_node_list resource: resource
100
+ @node_list_pane.subject.title = "Nodes of #{self.active_resource}"
101
+ @node_list_pane.subject.contents = self.ship.fetch_node_list(resource: self.active_resource)
100
102
  end
101
- resource
103
+ nil
102
104
  end
103
105
 
104
- def resync_node_view(resource)
105
- node_index = @node_list_pane.current_subject
106
- @node_view_pane.subject.title = "Node #{self.short_index node_index}"
106
+ def resync_node_view
107
+ @node_view_pane.subject.title = "Node #{self.short_index self.active_node_index}"
107
108
  @node_view_pane.clear
108
- @node_view_pane.subject.contents = self.ship.fetch_node_contents(resource: resource, index: node_index)
109
+ @node_view_pane.subject.contents = self.ship.fetch_node_contents(resource: self.active_resource, index: self.active_node_index)
110
+ nil
109
111
  end
110
112
 
111
113
  def short_index(index)
@@ -113,5 +115,21 @@ module MarsBase10
113
115
  tokens = index.split('.')
114
116
  "#{tokens[0]}..#{tokens[tokens.size - 2]}.#{tokens[tokens.size - 1]}"
115
117
  end
118
+
119
+ def wire_up_panes
120
+ @panes = []
121
+
122
+ # The graph list is a fixed width, variable height (full screen) pane on the left.
123
+ @graph_list_pane = @viewport.add_pane width_pct: 0.3
124
+ @graph_list_pane.view(subject: @ship.graph_names)
125
+
126
+ # The node list is a variable width, fixed height pane in the upper right.
127
+ @node_list_pane = @viewport.add_variable_width_pane at_col: @graph_list_pane.last_col, height_pct: 0.5
128
+ @node_list_pane.view(subject: @ship.empty_node_list)
129
+
130
+ # The single node viewer is a variable width, variable height pane in the lower right.
131
+ @node_view_pane = @viewport.add_variable_both_pane at_row: @node_list_pane.last_row, at_col: @graph_list_pane.last_col
132
+ @node_view_pane.view(subject: @ship.empty_node)
133
+ end
116
134
  end
117
135
  end
@@ -10,11 +10,13 @@ module MarsBase10
10
10
  @top_row = at_row
11
11
  @left_edge_col = at_col
12
12
  @height_pct = height_pct
13
- @index = 0
14
- @latch = -1
13
+ @index = 1
14
+ @latch = ''
15
+ @latch_depth = 0
15
16
  @subject = nil
16
17
  @win = nil
17
18
  @viewport = viewport
19
+ @visible_content_shift = 0
18
20
  @width_pct = width_pct
19
21
  end
20
22
 
@@ -23,6 +25,7 @@ module MarsBase10
23
25
  end
24
26
 
25
27
  def clear
28
+ self.reset
26
29
  self.prepare_for_writing_contents
27
30
  (0..(self.last_row - 1)).each do |item|
28
31
  self.window.setpos(self.draw_row, self.draw_col)
@@ -32,19 +35,35 @@ module MarsBase10
32
35
  end
33
36
  end
34
37
 
35
- def current_subject
36
- self.subject.at index: self.index
38
+ def current_subject_index
39
+ self.subject.at(index: self.index)
37
40
  end
38
41
 
39
42
  def draw
40
43
  self.prepare_for_writing_contents
41
44
 
42
- (0..(self.max_contents_rows - 1)).each do |item|
43
- self.window.setpos(self.draw_row, self.draw_col)
44
- # The string here is the gutter followed by the window contents. improving the gutter is tbd.
45
- self.window.attron(Curses::A_REVERSE) if item == self.index
46
- self.window.addstr("#{"%02d" % item} #{self.subject.at index: item}")
47
- self.window.attroff(Curses::A_REVERSE) # if item == self.index
45
+ first_draw_row = self.first_row
46
+ last_draw_row = self.last_drawable_row
47
+
48
+ (first_draw_row..last_draw_row).each do |index|
49
+ self.draw_line
50
+ item_index = index + @visible_content_shift
51
+ self.window.attron(Curses::A_REVERSE) if item_index == self.index
52
+
53
+ if self.subject.line_length_at(index: item_index) > self.last_col
54
+ chunks = self.subject.line_at(index: item_index).chars.each_slice(self.last_col).map(&:join)
55
+ chunks.each do |c|
56
+ self.window.addstr(c)
57
+ self.draw_row += 1
58
+ self.draw_line
59
+ last_draw_row -= 1 # pull the last drawing row in 1 for each new line added.
60
+ end
61
+ self.draw_row -= 1
62
+ else
63
+ self.window.addstr("#{self.subject.line_at(index: item_index)}")
64
+ end
65
+
66
+ self.window.attroff(Curses::A_REVERSE) if item_index == self.index
48
67
  self.window.clrtoeol
49
68
  self.draw_row += 1
50
69
  end
@@ -59,9 +78,13 @@ module MarsBase10
59
78
  self.window.attroff(Curses.color_pair(1) | Curses::A_BOLD) if self.active?
60
79
  end
61
80
 
81
+ def draw_line
82
+ self.window.setpos(self.draw_row, self.draw_col)
83
+ end
84
+
62
85
  def draw_title
63
86
  self.window.setpos(0, 2)
64
- self.window.addstr(" #{self.subject.title} (#{self.subject.rows} total) ")
87
+ self.window.addstr(" #{self.subject.title} (#{self.max_contents_rows} total) ")
65
88
  end
66
89
 
67
90
  def first_col
@@ -73,7 +96,7 @@ module MarsBase10
73
96
  end
74
97
 
75
98
  def gutter_width
76
- 4
99
+ self.subject.index_width
77
100
  end
78
101
 
79
102
  #
@@ -83,6 +106,10 @@ module MarsBase10
83
106
  [(self.viewport.max_cols * self.width_pct).floor, self.min_column_width].max
84
107
  end
85
108
 
109
+ def last_drawable_row
110
+ [self.last_visible_row, self.max_contents_rows].min
111
+ end
112
+
86
113
  #
87
114
  # This is the _relative_ last row, e.g. the height of the pane in columns.
88
115
  #
@@ -90,19 +117,23 @@ module MarsBase10
90
117
  (self.viewport.max_rows * self.height_pct).floor
91
118
  end
92
119
 
120
+ def last_visible_row
121
+ self.last_row - 2
122
+ end
123
+
93
124
  #
94
125
  # The pane is latched if it has consumed 1 key 0-9 and is awaiting the next key.
95
126
  #
96
127
  def latched?
97
- self.latch != -1
128
+ @latch_depth > 0
98
129
  end
99
130
 
100
131
  def max_contents_rows
101
- self.subject.rows
132
+ self.subject.item_index_range.last
102
133
  end
103
134
 
104
135
  def min_column_width
105
- self.gutter_width + self.subject.cols + self.right_pad
136
+ self.gutter_width + self.subject.max_content_width + self.right_pad
106
137
  end
107
138
 
108
139
  def prepare_for_writing_contents
@@ -121,17 +152,27 @@ module MarsBase10
121
152
  key = self.window.getch.to_s
122
153
  case key
123
154
  when 'j'
124
- self.set_row(self.index + 1)
155
+ self.index = self.set_row(self.index + 1)
125
156
  when 'k'
126
- self.set_row(self.index - 1)
157
+ self.index = self.set_row(self.index - 1)
158
+ when 'J'
159
+ self.index = self.set_row([self.index + (self.last_visible_row), self.max_contents_rows].min)
160
+ when 'K'
161
+ self.index = self.set_row(self.index - (self.last_visible_row))
127
162
  when 'q'
128
163
  exit 0
129
164
  when ('0'..'9')
130
165
  if self.latched?
131
- self.set_row((self.latch * 10) + key.to_i)
132
- self.latch = -1
166
+ self.latch << key
167
+ @latch_depth += 1
168
+ if @latch_depth == self.gutter_width
169
+ self.index = self.set_row(self.latch.to_i)
170
+ self.latch = ""
171
+ @latch_depth = 0
172
+ end
133
173
  else
134
- self.latch = key.to_i
174
+ self.latch = key
175
+ @latch_depth = 1
135
176
  end
136
177
  end
137
178
 
@@ -139,38 +180,54 @@ module MarsBase10
139
180
  self.viewport.controller.send key: key
140
181
  end
141
182
 
183
+ def reset
184
+ @index = 1
185
+ @latch = ''
186
+ @latch_depth = 0
187
+ @visible_content_shift = 0
188
+ end
189
+
142
190
  def right_pad
143
191
  2
144
192
  end
145
193
 
194
+ def scroll_to_row(index)
195
+ if index > self.index
196
+ # Scrolling down
197
+ @visible_content_shift = [(index - self.last_drawable_row), 0].max
198
+ else
199
+ # Scrolling up
200
+ @visible_content_shift = [(index - self.first_row), 0].max
201
+ end
202
+ [index, 1].max
203
+ end
204
+
146
205
  #
147
206
  # this is a no-op if the index is out of range
148
207
  #
149
208
  def set_row(i)
150
- self.subject.scroll_limit = [self.last_row - 1, self.max_contents_rows].min
151
-
152
- if (i < 0)
153
- self.subject.scroll_up
154
- i = 0
155
- end
209
+ return i if self.visible_content_range.include?(i)
156
210
 
157
211
  # If we've reached the end of the content, it's a no-op.
158
- if (i >= self.max_contents_rows)
159
- i -= 1
160
- end
212
+ current_index = self.index
213
+ return self.max_contents_rows if (i > self.max_contents_rows)
161
214
 
162
- if (i >= self.last_row - 2)
163
- self.subject.scroll_down
164
- i -= 1
215
+ # Check if we have tried to move "above" the visible screen limit (i = 1) and retrieve more items, if possible.
216
+ if (i < 1)
217
+ i = [self.viewport.controller.load_history, 1].max
165
218
  end
166
219
 
167
- self.index = i # if (i <= self.max_contents_rows) && (i >= 0)
220
+ return self.scroll_to_row(i)
168
221
  end
169
222
 
170
- def viewing(subject:)
223
+ def view(subject:)
171
224
  @subject = subject
172
225
  end
173
226
 
227
+ def visible_content_range
228
+ ((self.first_row + @visible_content_shift)..(self.last_drawable_row + @visible_content_shift))
229
+ end
230
+
174
231
  def window
175
232
  return @win if @win
176
233
  @win = Curses::Window.new(self.last_row, self.last_col, self.top_row, self.left_edge_col)
@@ -8,18 +8,18 @@ module MarsBase10
8
8
  @ship = connection
9
9
  end
10
10
 
11
- def graph_names
12
- Subject.new title: 'Graphs', contents: @ship.graph_names
13
- end
14
-
15
- def node
11
+ def empty_node
16
12
  Subject.new title: 'Node', contents: []
17
13
  end
18
14
 
19
- def node_list
15
+ def empty_node_list
20
16
  Subject.new title: 'Node List', contents: []
21
17
  end
22
18
 
19
+ def graph_names
20
+ Subject.new title: 'Graphs', contents: @ship.graph_names
21
+ end
22
+
23
23
  def fetch_node(resource:, index:)
24
24
  @ship.graph(resource: resource).node(index: index)
25
25
  end
@@ -34,7 +34,11 @@ module MarsBase10
34
34
  end
35
35
 
36
36
  def fetch_node_list(resource:)
37
- @ship.graph(resource: resource).newest_nodes(count: 20).map {|node| node.index}.sort
37
+ @ship.graph(resource: resource).newest_nodes(count: 60).map {|node| node.index}.sort
38
+ end
39
+
40
+ def fetch_older_nodes(resource:, node:)
41
+ @ship.graph(resource: resource).older_sibling_nodes(node: node, count: 60).map {|node| node.index}.sort
38
42
  end
39
43
  end
40
44
  end
@@ -2,22 +2,21 @@
2
2
 
3
3
  module MarsBase10
4
4
  class Subject
5
- attr_accessor :first_row, :scroll_limit, :title
5
+ attr_accessor :current_item, :scroll_limit, :title
6
6
 
7
7
  def initialize(title: 'Untitled', contents:)
8
- @contents = contents
9
- @first_row = 0
10
- @title = title
8
+ @contents = contents
9
+ @title = title
11
10
  end
12
11
 
13
- # Returns the item at: the index: relative to the first_row.
14
- def at(index:)
15
- self.contents[self.first_row + index]
12
+ def prepend_content(ary:)
13
+ self.contents = ary + self.contents
16
14
  end
17
15
 
18
- def cols
19
- return @cols if @cols
20
- @cols = @contents.inject(0) {|a, n| n.length > a ? n.length : a}
16
+ # Returns the item at: the index: relative to the current_item.
17
+ def at(index:)
18
+ index = [1, index].max
19
+ self.contents[index - 1]
21
20
  end
22
21
 
23
22
  def contents
@@ -25,22 +24,33 @@ module MarsBase10
25
24
  end
26
25
 
27
26
  def contents=(a_contents_array)
28
- @rows = nil
29
- $cols = nil
30
27
  @contents = a_contents_array
31
28
  end
32
29
 
33
- def rows
34
- return @rows if @rows
35
- @rows = @contents.size
30
+ def index_width
31
+ [self.item_count.to_s.length, 2].max
32
+ end
33
+
34
+ def line_at(index:)
35
+ # The string here is the gutter followed by the window contents. improving the gutter is tbd.
36
+ "#{"%0#{self.index_width}d" % (index)} #{self.at(index: index)}"
37
+ end
38
+
39
+ def line_length_at(index:)
40
+ return 0 if self.at(index: index).nil?
41
+ (self.at(index: index)).length
42
+ end
43
+
44
+ def item_count
45
+ @contents.size
36
46
  end
37
47
 
38
- def scroll_down
39
- self.first_row = [self.first_row + 1, (self.rows - self.scroll_limit)].min
48
+ def item_index_range
49
+ 1..self.item_count
40
50
  end
41
51
 
42
- def scroll_up
43
- self.first_row = [self.first_row - 1, 0].max
52
+ def max_content_width
53
+ @contents.inject(0) {|a, n| n.length > a ? n.length : a}
44
54
  end
45
55
  end
46
56
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MarsBase10
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/mars_base_10.gemspec CHANGED
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_dependency "sorted_set", "~> 1.0"
33
33
  spec.add_dependency "thor", "~> 1.1"
34
34
  spec.add_dependency "tty-font", "~> 0.5"
35
- spec.add_dependency "urbit-api", "~> 0.2.1"
35
+ spec.add_dependency "urbit-api", "~> 0.5"
36
36
 
37
37
  spec.add_development_dependency "pry", "~> 0.13"
38
38
  spec.add_development_dependency "rspec", "~> 3.10"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mars_base_10
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daryl Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-18 00:00:00.000000000 Z
11
+ date: 2022-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: curses
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.2.1
89
+ version: '0.5'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.2.1
96
+ version: '0.5'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: pry
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -146,6 +146,7 @@ extensions: []
146
146
  extra_rdoc_files: []
147
147
  files:
148
148
  - bin/mb10
149
+ - lib/.DS_Store
149
150
  - lib/mars_base_10.rb
150
151
  - lib/mars_base_10/action_bar.rb
151
152
  - lib/mars_base_10/cli.rb