mars_base_10 0.5.0 → 0.6.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: 9ea353d6a59cf0d1ad388a521fa07a121cd94942eb4556c23ee4a11603622141
4
- data.tar.gz: 6fd28ad9bd47d54172c4a3c6a0f64e5e4da244af6065ad8514f779cc388dc0c6
3
+ metadata.gz: b0d8748c4298354146d42d5f36cbba46c4957d2e4604b58465b365c980041089
4
+ data.tar.gz: b881836e892d29f0e195422690ecdb4913daf0f58daeed3170bb9b8d4eeb83c0
5
5
  SHA512:
6
- metadata.gz: 248ae50be666debca25a7c253822e941af1c3ba19ac484b6e21423526497c9777419b4b2417756a565bc890afb9dc817d0eb3060eb0b7db34bab0ae5c396ac58
7
- data.tar.gz: 20e525d483036e6a815f0e46a3529cdf6d1bee8e4a96bc8c32cc0d27b774331e13d1ae3c76177904da39b845224ed12cfa6b4dc3cb667e943704c75799cb7360
6
+ metadata.gz: 0c14c3247c13710a5a053da98bc140e14bbf501d1f4f5888aba12ca8fd54c3807352da004b82e891beaff9b0b6da286f8151e105989b0d3dad3f5705d331045d
7
+ data.tar.gz: 3323624dc9690b1eb539ada863729cc615c63743679a824dc21ab07e82fee61b875025c64f329df6802527884379f133a9e622a2159f11b1c406a9be245458ae
data/bin/mb10 CHANGED
@@ -2,10 +2,10 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # require "bundler/setup"
5
- # require "mars_base_10/comm_central"
5
+ # require "mars_base_10/mission_control"
6
6
 
7
7
  # begin
8
- # cc = MarsBase10::CommCentral.new
8
+ # cc = MarsBase10::MissionControl.new
9
9
  # cc.activate
10
10
  # ensure
11
11
  # cc.shutdown
data/lib/.DS_Store CHANGED
Binary file
@@ -60,8 +60,8 @@ module MarsBase10
60
60
  1
61
61
  end
62
62
 
63
- def remove_action(key)
64
- self.actions.delete_if {|k, v| k == key}
63
+ def remove_actions(keys)
64
+ self.actions.delete_if {|k, v| keys.include? k}
65
65
  self
66
66
  end
67
67
 
@@ -39,9 +39,9 @@ module MarsBase10
39
39
  invoke :help, ["launch"]
40
40
  else
41
41
  if (config)
42
- require_relative "comm_central"
42
+ require_relative "mission_control"
43
43
  begin
44
- cc = MarsBase10::CommCentral.new config_filename: config
44
+ cc = MarsBase10::MissionControl.new config_filename: config
45
45
  cc.activate
46
46
  ensure
47
47
  cc.shutdown
@@ -1,38 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'ship'
4
- require_relative 'stack'
5
- require_relative 'subject'
3
+ require_relative '../controller'
6
4
 
7
5
  module MarsBase10
8
- class GraphRover
9
- attr_reader :manager, :panes, :ship, :viewport
10
-
11
- def initialize(manager:, ship_connection:, viewport:)
12
- @manager = manager
13
- @ship = Ship.new connection: ship_connection
14
- @stack = Stack.new
15
- @viewport = viewport
16
- @viewport.controller = self
17
-
18
- self.wire_up_panes
19
- self.viewport.action_bar = ActionBar.Default.add_action({'i': 'Inspect'})
20
- self.viewport.activate pane: @graph_list_pane
21
- self.resync
22
- end
23
-
6
+ class GraphRover < Controller
24
7
  def active_node_index
25
8
  @node_list_pane.current_subject_index
26
9
  end
27
10
 
28
- def active_node
29
- self.ship.fetch_node(resource: self.active_resource, index: self.active_node_index)
30
- end
31
-
32
- def active_resource
33
- @graph_list_pane.current_subject_index
34
- end
35
-
36
11
  def load_history
37
12
  return 0 unless @node_list_pane == self.viewport.active_pane
38
13
  new_content = self.ship.fetch_older_nodes(resource: self.active_resource, node: self.active_node)
@@ -49,7 +24,8 @@ module MarsBase10
49
24
  when 'd' # (D)ive
50
25
  begin
51
26
  if @node_view_pane.subject.contents[4].include?('true')
52
- self.viewport.action_bar.add_action({'p': 'Pop Out'})
27
+ self.action_bar.add_action({'p': 'Pop Out'})
28
+ self.action_bar.remove_actions([:d, :g])
53
29
  @stack.push(self.active_resource)
54
30
  @node_list_pane.clear
55
31
  @node_list_pane.subject.contents = self.ship.fetch_node_children(resource: self.active_resource, index: self.active_node_index)
@@ -58,12 +34,12 @@ module MarsBase10
58
34
  when 'i' # (I)nspect
59
35
  begin
60
36
  self.viewport.activate pane: @node_list_pane
61
- self.viewport.action_bar = ActionBar.Default.add_action({'d': 'Dive In', 'g': 'Graph List'})
37
+ self.action_bar.add_action({'d': 'Dive In', 'g': 'Graph List'})
62
38
  resync_needed = false
63
39
  end
64
40
  when 'g' # (G)raph View
65
- unless @graph_list_pane.active?
66
- self.viewport.activate pane: @graph_list_pane
41
+ unless @pane_1.active?
42
+ self.viewport.activate pane: @pane_1
67
43
  # resync_needed = false
68
44
  end
69
45
  when 'p' # (P)op
@@ -73,7 +49,7 @@ module MarsBase10
73
49
  @node_list_pane.subject.contents = self.ship.fetch_node_list(resource: resource)
74
50
  end
75
51
  if (@stack.length == 0)
76
- self.viewport.action_bar.remove_action(:p)
52
+ self.action_bar.remove_actions([:p])
77
53
  end
78
54
  end
79
55
  when 'X'
@@ -98,7 +74,7 @@ module MarsBase10
98
74
  end
99
75
 
100
76
  def resync_node_list
101
- if @graph_list_pane == self.viewport.active_pane
77
+ if @pane_1 == self.viewport.active_pane
102
78
  @node_list_pane.clear
103
79
  @node_list_pane.subject.title = "Nodes of #{self.active_resource}"
104
80
  @node_list_pane.subject.contents = self.ship.fetch_node_list(resource: self.active_resource)
@@ -123,16 +99,16 @@ module MarsBase10
123
99
  @panes = []
124
100
 
125
101
  # The graph list is a fixed width, variable height (full screen) pane on the left.
126
- @graph_list_pane = @viewport.add_pane width_pct: 0.3
127
- @graph_list_pane.view(subject: @ship.graph_names)
102
+ @pane_1 = @viewport.add_pane width_pct: 0.3
103
+ @pane_1.view(subject: @ship.graph_names)
128
104
 
129
105
  # The node list is a variable width, fixed height pane in the upper right.
130
- @node_list_pane = @viewport.add_variable_width_pane at_col: @graph_list_pane.last_col, height_pct: 0.5
106
+ @node_list_pane = @viewport.add_variable_width_pane at_col: @pane_1.last_col, height_pct: 0.5
131
107
  @node_list_pane.view(subject: @ship.empty_node_list)
132
108
 
133
109
  # The single node viewer is a variable width, variable height pane in the lower right.
134
- @node_view_pane = @viewport.add_variable_both_pane at_row: @node_list_pane.last_row, at_col: @graph_list_pane.last_col
110
+ @node_view_pane = @viewport.add_variable_both_pane at_row: @node_list_pane.last_row, at_col: @pane_1.last_col
135
111
  @node_view_pane.view(subject: @ship.empty_node)
136
112
  end
137
113
  end
138
- end
114
+ end # Module Mars::Base::10
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../controller'
4
+
5
+ module MarsBase10
6
+ class GroupRoom < Controller
7
+ def active_subject(pane:)
8
+ pane.current_subject_index
9
+ end
10
+
11
+ def load_history
12
+ return 0 unless @pane_3 == self.viewport.active_pane
13
+ new_content = self.ship.fetch_older_nodes(resource: self.active_resource, node: self.active_node)
14
+ @pane_3.subject.prepend_content(ary: new_content)
15
+ new_content.length
16
+ end
17
+
18
+ #
19
+ # Called by a pane in this controller for bubbling a key press up
20
+ #
21
+ def send(key:)
22
+ resync_needed = true
23
+ case key
24
+ when 'g' # (G)raph View
25
+ unless @pane_1.active?
26
+ self.action_bar.remove_actions([:r])
27
+ self.viewport.activate pane: @pane_1
28
+ end
29
+ when 'i' # (I)nspect
30
+ begin
31
+ resync_needed = false
32
+ self.action_bar.add_action({'g': 'Group List'})
33
+ self.action_bar.add_action({'r': 'Read Channel'})
34
+ self.viewport.activate pane: @pane_3
35
+ end
36
+ when 'r' # (r)ead -> go to the Social Lounge
37
+ if @pane_3.active?
38
+ resync_needed = false
39
+ self.action_bar.remove_actions([:g, :r])
40
+ opts = {
41
+ group_title: self.active_subject(pane: @pane_1),
42
+ channel_title: self.active_subject(pane: @pane_3)
43
+ }
44
+ self.manager.assign(controller_class: SocialLounge, options: opts)
45
+ end
46
+ when 'X'
47
+ resync_needed = false
48
+ self.manager.swap_controller
49
+ end
50
+ self.resync if resync_needed
51
+ end
52
+
53
+ private
54
+
55
+ def resync
56
+ self.resync_node_list
57
+ self.resync_node_view
58
+ end
59
+
60
+ def resync_node_list
61
+ if @pane_1 == self.viewport.active_pane
62
+ group_title = self.active_subject(pane: @pane_1)
63
+ @pane_2.clear
64
+ @pane_2.subject.title = "#{group_title}"
65
+ @pane_2.subject.contents = self.ship.fetch_group(group_title: group_title)
66
+
67
+ @pane_3.clear
68
+ @pane_3.subject.title = "Channels of #{self.active_subject(pane: @pane_1)}"
69
+ @pane_3.subject.contents = self.ship.fetch_group_channels(group_title: self.active_subject(pane: @pane_1))
70
+ end
71
+ nil
72
+ end
73
+
74
+ def resync_node_view
75
+ channel_title = self.active_subject(pane: @pane_3)
76
+ @pane_4.subject.title = "#{channel_title}"
77
+ @pane_4.clear
78
+ @pane_4.subject.contents = self.ship.fetch_channel_props(group_title: self.active_subject(pane: @pane_1), channel_title: channel_title)
79
+ nil
80
+ end
81
+
82
+ def wire_up_panes
83
+ @panes = []
84
+
85
+ # Pane #1 is the Group list, It is a fixed height and width in the upper left corner.
86
+ @pane_1 = @viewport.add_pane height_pct: 0.5, width_pct: 0.5
87
+ # if @ship.group_names.empty?
88
+ # @pane_1.view(subject: @ship.graph_names)
89
+ # else
90
+ @pane_1.view(subject: @ship.group_names)
91
+ # end
92
+
93
+ # Pane 2 displays the properties of the selected Group. It is variable height in the bottom left corner.
94
+ @pane_2 = @viewport.add_variable_height_pane at_row: @pane_1.last_row, width_pct: 0.5
95
+ @pane_2.view(subject: @ship.empty_node)
96
+ @pane_2.highlight = false
97
+
98
+ # Pane 3 is the Channel list. It is a variable width, fixed height pane in the upper right.
99
+ @pane_3 = @viewport.add_variable_width_pane at_col: @pane_1.last_col, height_pct: 0.5
100
+ @pane_3.view(subject: @ship.empty_node_list)
101
+
102
+ # The single node viewer is a variable width, variable height pane in the lower right.
103
+ @pane_4 = @viewport.add_variable_both_pane at_row: @pane_3.last_row, at_col: @pane_1.last_col
104
+ @pane_4.view(subject: @ship.empty_node)
105
+ @pane_4.highlight = false
106
+ end
107
+ end
108
+ end # Module Mars::Base::10
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../controller'
4
+
5
+ module MarsBase10
6
+ class SocialLounge < Controller
7
+ attr_accessor :channel
8
+
9
+ def initialize(manager:, ship_connection:, viewport:, options: {})
10
+ @index_ary = []
11
+ @group_title = options[:group_title]
12
+ @channel_title = options[:channel_title]
13
+ super(manager: manager, ship_connection: ship_connection, viewport: viewport)
14
+ end
15
+
16
+ def active_channel
17
+ if self.channel.nil?
18
+ self.channel = self.ship.fetch_channel(group_title: @group_title, channel_title: @channel_title)
19
+ end
20
+ self.channel
21
+ end
22
+
23
+ def active_node_index
24
+ @index_ary[@pane_1.index]
25
+ end
26
+
27
+ def active_resource
28
+ self.active_channel.resource # '~winter-paches/top-shelf-6391'
29
+ end
30
+
31
+ def active_subject(pane:)
32
+ pane.current_subject_index
33
+ end
34
+
35
+ def fetch_channel_messages
36
+ if @pane_1 == self.viewport.active_pane
37
+ @pane_1.clear
38
+ @pane_1.subject.title = "Messages in #{self.active_channel.title}"
39
+ @pane_1.subject.contents = self.load_messages(count: @pane_1.last_visible_row)
40
+ end
41
+ nil
42
+ end
43
+
44
+ def load_history
45
+ return 0 unless @pane_1 == self.viewport.active_pane
46
+ messages = self.node_indexes_to_messages(node_indexes: self.ship.fetch_older_nodes(resource: self.active_resource, node: self.active_node, count: @pane_1.last_visible_row))
47
+ @pane_1.subject.prepend_content(ary: messages)
48
+ messages.length
49
+ end
50
+
51
+ def load_messages(count:)
52
+ self.node_indexes_to_messages(node_indexes: self.ship.fetch_node_list(resource: self.active_resource, count: count))
53
+ end
54
+
55
+ # This will eventually be a native part of the Airlock API
56
+ def message(node:)
57
+ "~#{node.to_h[:author]} #{self.message_content(node_content: node.to_h[:contents])}"
58
+ end
59
+
60
+ def message_content(node_content:)
61
+ n0 = node_content[0]
62
+ if n0["reference"]
63
+ # http://localhost:8080/apps/landscape/perma/group/~winter-paches/the-great-north/graph/~winter-paches/top-shelf-6391/170141184505732100235824355185168220160
64
+ ref = n0["reference"]["graph"]
65
+ # grup = ref["group"].split('/').last
66
+ # graf = ref["graph"].split('/').last
67
+ return "=> #{node_content[2]["mention"]} -> #{node_content[3]["text"]} (#{ref["index"]})"
68
+ end
69
+ n0["text"] || n0["url"]
70
+ end
71
+
72
+ def node_indexes_to_messages(node_indexes:)
73
+ @index_ary = node_indexes + @index_ary
74
+ messages = node_indexes.map do |i|
75
+ # Can't use fetch_node_contents because we're formatting differently now.
76
+ # This will eventually be a native part of the Airlock API
77
+ self.message(node: self.ship.fetch_node(resource: self.active_resource, index: i))
78
+ end
79
+ end
80
+
81
+ #
82
+ # Called by a pane in this controller for bubbling a key press up
83
+ #
84
+ def send(key:)
85
+ case key
86
+ when 'g' # (G)raph View
87
+ unless @pane_1.active?
88
+ self.viewport.activate pane: @pane_1
89
+ self.action_bar.remove_actions([:g])
90
+ end
91
+ when 'i' # (I)nspect
92
+ begin
93
+ self.viewport.activate pane: @pane_3
94
+ self.action_bar.add_action({'g': 'Group List'})
95
+ end
96
+ when 'X'
97
+ self.manager.swap_controller
98
+ end
99
+ end
100
+
101
+ def show
102
+ self.fetch_channel_messages
103
+ end
104
+
105
+ private
106
+
107
+ def wire_up_panes
108
+ @panes = []
109
+ # Pane #1 is the Chat Channel Reader. It takes up the entire Viewport. (For now?)
110
+ @pane_1 = self.viewport.add_pane height_pct: 1.0, width_pct: 1.0
111
+ @pane_1.view(subject: self.ship.empty_node_list)
112
+ end
113
+ end
114
+ end # Module Mars::Base::10
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ship'
4
+ require_relative 'stack'
5
+ require_relative 'subject'
6
+
7
+ module MarsBase10
8
+ class Controller
9
+ attr_reader :manager, :panes, :ship, :viewport
10
+
11
+ def initialize(manager:, ship_connection:, viewport:, options: {})
12
+ @manager = manager
13
+ @ship = Ship.new connection: ship_connection
14
+ @stack = Stack.new
15
+ @viewport = viewport
16
+ @viewport.controller = self
17
+
18
+ self.wire_up_panes
19
+ self.action_bar = ActionBar.Default.add_action({'i': 'Inspect'})
20
+ self.viewport.activate pane: @pane_1
21
+ self.show
22
+ end
23
+
24
+ def action_bar
25
+ self.viewport.action_bar
26
+ end
27
+
28
+ def active_node
29
+ self.ship.fetch_node(resource: self.active_resource, index: self.active_node_index)
30
+ end
31
+
32
+ def active_resource
33
+ @pane_1.current_subject_index
34
+ end
35
+
36
+ def show
37
+ self.resync
38
+ end
39
+
40
+ def start
41
+ self.viewport.open
42
+ end
43
+
44
+ def stop
45
+ self.viewport.close
46
+ end
47
+
48
+ private
49
+
50
+ def action_bar=(an_action_bar)
51
+ self.viewport.action_bar = an_action_bar
52
+ end
53
+ end
54
+ end
@@ -1,19 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
  require 'urbit'
3
3
 
4
- require_relative 'graph_rover'
5
- require_relative 'group_room'
4
+ require_relative 'controller/group_room'
5
+ require_relative 'controller/graph_rover'
6
+ require_relative 'controller/social_lounge'
6
7
  require_relative 'viewport'
7
8
 
8
9
  module MarsBase10
9
10
  class Error < StandardError; end
10
11
 
11
- class CommCentral
12
+ class MissionControl
13
+ attr_accessor :controller
14
+ attr_reader :ship
15
+
12
16
  def initialize(config_filename:)
13
- @viewport = Viewport.new
14
17
  @ship = Urbit.connect(config_file: config_filename)
15
18
  @ship.login
16
19
  sleep 2 # This is temporary for now, we need a way to know that the subscription callbacks have finished.
20
+ @viewport = Viewport.new
17
21
  @controller = GroupRoom.new manager: self, ship_connection: @ship, viewport: @viewport
18
22
  end
19
23
 
@@ -21,28 +25,24 @@ module MarsBase10
21
25
  self.controller.start
22
26
  end
23
27
 
24
- def swap_controller
25
- self.controller.stop
26
- if GroupRoom == self.controller.class
27
- @controller = GraphRover.new manager: self, ship_connection: self.ship, viewport: @viewport
28
- else
29
- @controller = GroupRoom.new manager: self, ship_connection: self.ship, viewport: @viewport
30
- end
31
- self.controller.start
28
+ def assign(controller_class:, options: {})
29
+ @viewport.dispose_panes
30
+ self.controller = controller_class.send(:new, {manager: self, ship_connection: self.ship, viewport: @viewport, options: options})
32
31
  end
33
32
 
34
33
  def shutdown
35
34
  self.controller.stop
36
35
  end
37
36
 
38
- private
39
-
40
- def controller
41
- @controller
42
- end
43
-
44
- def ship
45
- @ship
37
+ def swap_controller
38
+ if GroupRoom == self.controller.class
39
+ cls = GraphRover
40
+ elsif GraphRover == self.controller.class
41
+ cls = SocialLounge
42
+ else
43
+ cls = GroupRoom
44
+ end
45
+ self.assign(controller_class: cls)
46
46
  end
47
47
  end
48
48
  end
@@ -3,22 +3,24 @@ require 'curses'
3
3
 
4
4
  module MarsBase10
5
5
  class Pane
6
- attr_accessor :draw_row, :draw_col, :highlight, :index, :latch, :subject
6
+ attr_accessor :cur_draw_row, :cur_draw_col, :extended_lines, :highlight, :index, :latch, :subject
7
7
  attr_reader :height_pct, :left_edge_col, :top_row, :viewport, :width_pct
8
8
 
9
9
  def initialize(viewport:, at_row:, at_col:, height_pct: 1, width_pct: 1)
10
- @top_row = at_row
11
- @left_edge_col = at_col
12
- @height_pct = height_pct
13
- @highlight = true
14
- @index = 1
15
- @latch = ''
16
- @latch_depth = 0
17
- @subject = nil
18
- @win = nil
19
- @viewport = viewport
20
- @visible_content_shift = 0
21
- @width_pct = width_pct
10
+ @first_visible_index = 1
11
+ @height_pct = height_pct
12
+ @highlight = false
13
+ @index = 1
14
+ @last_visible_index = 0
15
+ @latch = ''
16
+ @latch_depth = 0
17
+ @left_edge_col = at_col
18
+ @subject = nil
19
+ @top_row = at_row
20
+ @win = nil
21
+ @viewport = viewport
22
+ # @visible_content_shift = 0
23
+ @width_pct = width_pct
22
24
  end
23
25
 
24
26
  def active?
@@ -29,13 +31,17 @@ module MarsBase10
29
31
  self.reset
30
32
  self.prepare_for_writing_contents
31
33
  (0..(self.last_row - 1)).each do |item|
32
- self.window.setpos(self.draw_row, self.draw_col)
34
+ self.window.setpos(self.cur_draw_row, self.cur_draw_col)
33
35
  self.window.addstr("")
34
36
  self.window.clrtoeol
35
- self.draw_row += 1
37
+ self.cur_draw_row += 1
36
38
  end
37
39
  end
38
40
 
41
+ def current_item_index
42
+ (self.cur_draw_row - self.extended_lines) + self.visible_content_shift
43
+ end
44
+
39
45
  def current_subject_index
40
46
  self.subject.at(index: self.index)
41
47
  end
@@ -43,33 +49,42 @@ module MarsBase10
43
49
  def draw
44
50
  self.prepare_for_writing_contents
45
51
 
46
- first_draw_row = self.first_row
47
- last_draw_row = self.last_drawable_row
48
-
49
- (first_draw_row..last_draw_row).each do |index|
50
- self.draw_line
51
- item_index = index + @visible_content_shift
52
- self.window.attron(Curses::A_REVERSE) if (item_index == self.index) && self.highlight
52
+ while self.cur_draw_row <= self.last_drawable_row
53
+ if self.current_item_index <= self.subject.item_index_range.last
54
+ if (self.current_item_index == self.index) && !self.highlight
55
+ self.window.attron(Curses::A_REVERSE)
56
+ self.highlight = true
57
+ end
53
58
 
54
- if self.subject.line_length_at(index: item_index) > self.last_col
55
- chunks = self.subject.line_at(index: item_index).chars.each_slice(self.last_col - 2).map(&:join)
56
- chunks.each do |c|
57
- self.window.addstr(c)
58
- self.draw_row += 1
59
+ if self.subject.line_length_at(index: self.current_item_index) > self.last_col
60
+ chunks = self.subject.line_at(index: self.current_item_index).chars.each_slice(self.last_col - 2).map(&:join)
61
+ chunks.each do |c|
62
+ self.draw_line
63
+ self.window.addstr("#{c}")
64
+ self.cur_draw_row += 1
65
+ self.extended_lines += 1
66
+ end
67
+ self.extended_lines -= 1
68
+ else
59
69
  self.draw_line
60
- last_draw_row -= 1 # pull the last drawing row in 1 for each new line added.
70
+ self.window.addstr("#{self.subject.line_at(index: self.current_item_index)}")
71
+ self.cur_draw_row += 1
72
+ end
73
+
74
+ if self.highlight
75
+ self.window.attroff(Curses::A_REVERSE)
76
+ self.highlight = false
61
77
  end
62
- self.draw_row -= 1
63
78
  else
64
- self.window.addstr("#{self.subject.line_at(index: item_index)}")
79
+ self.draw_line
80
+ self.window.addstr(" ")
81
+ self.cur_draw_row += 1
65
82
  end
66
-
67
- self.window.attroff(Curses::A_REVERSE) if (item_index == self.index) && self.highlight
68
83
  self.window.clrtoeol
69
- self.draw_row += 1
70
84
  end
71
-
72
85
  self.draw_border
86
+
87
+ @last_visible_index = self.current_item_index - 1 # Subtract one b/c we have already incremented before loop
73
88
  end
74
89
 
75
90
  def draw_border
@@ -80,7 +95,7 @@ module MarsBase10
80
95
  end
81
96
 
82
97
  def draw_line
83
- self.window.setpos(self.draw_row, self.draw_col)
98
+ self.window.setpos(self.cur_draw_row, self.cur_draw_col)
84
99
  end
85
100
 
86
101
  def draw_title
@@ -88,11 +103,11 @@ module MarsBase10
88
103
  self.window.addstr(" #{self.subject.title} (#{self.max_contents_rows} total) ")
89
104
  end
90
105
 
91
- def first_col
106
+ def first_drawable_col
92
107
  1
93
108
  end
94
109
 
95
- def first_row
110
+ def first_drawable_row
96
111
  1
97
112
  end
98
113
 
@@ -104,7 +119,8 @@ module MarsBase10
104
119
  # This is the _relative_ last column, e.g. the width of the pane in columns.
105
120
  #
106
121
  def last_col
107
- [(self.viewport.max_cols * self.width_pct).floor, self.min_column_width].max
122
+ # [(self.viewport.max_cols * self.width_pct).floor, self.min_column_width].max
123
+ (self.viewport.max_cols * self.width_pct).floor
108
124
  end
109
125
 
110
126
  def last_drawable_row
@@ -112,12 +128,15 @@ module MarsBase10
112
128
  end
113
129
 
114
130
  #
115
- # This is the _relative_ last row, e.g. the height of the pane in columns.
131
+ # This is the _relative_ last row, e.g. the height of the pane in rows
116
132
  #
117
133
  def last_row
118
134
  (self.viewport.max_rows * self.height_pct).floor
119
135
  end
120
136
 
137
+ #
138
+ # This is the height of the pane minus the border
139
+ #
121
140
  def last_visible_row
122
141
  self.last_row - 2
123
142
  end
@@ -138,8 +157,11 @@ module MarsBase10
138
157
  end
139
158
 
140
159
  def prepare_for_writing_contents
141
- self.draw_row = self.first_row
142
- self.draw_col = self.first_col
160
+ self.cur_draw_row = self.first_drawable_row
161
+ self.cur_draw_col = self.first_drawable_col
162
+ self.extended_lines = 0
163
+ @first_visible_index = self.cur_draw_row + self.visible_content_shift
164
+ @last_visible_index = self.subject.item_count if @last_visible_index < @first_visible_index
143
165
  end
144
166
 
145
167
  #
@@ -159,7 +181,7 @@ module MarsBase10
159
181
  when 'J'
160
182
  self.index = self.set_row([self.index + (self.last_visible_row), self.max_contents_rows].min)
161
183
  when 'K'
162
- self.index = self.set_row(self.index - (self.last_visible_row))
184
+ self.index = self.set_row(self.index - self.visible_content_range.count)
163
185
  when 'q'
164
186
  exit 0
165
187
  when ('0'..'9')
@@ -185,7 +207,7 @@ module MarsBase10
185
207
  @index = 1
186
208
  @latch = ''
187
209
  @latch_depth = 0
188
- @visible_content_shift = 0
210
+ # @visible_content_shift = 0
189
211
  end
190
212
 
191
213
  def right_pad
@@ -193,12 +215,19 @@ module MarsBase10
193
215
  end
194
216
 
195
217
  def scroll_to_row(index)
218
+ jump = [(index - self.index), (self.max_contents_rows - self.visible_content_range.last)].min
196
219
  if index > self.index
197
220
  # Scrolling down
198
- @visible_content_shift = [(index - self.last_drawable_row), 0].max
221
+ if index > self.visible_content_range.last
222
+ @first_visible_index = @first_visible_index + jump
223
+ @last_visible_index = @last_visible_index + jump
224
+ end
199
225
  else
200
226
  # Scrolling up
201
- @visible_content_shift = [(index - self.first_row), 0].max
227
+ if index < self.visible_content_range.first
228
+ @first_visible_index = [(@first_visible_index + jump), 1].max
229
+ @last_visible_index = @last_visible_index + jump
230
+ end
202
231
  end
203
232
  [index, 1].max
204
233
  end
@@ -215,7 +244,12 @@ module MarsBase10
215
244
 
216
245
  # Check if we have tried to move "above" the visible screen limit (i = 1) and retrieve more items, if possible.
217
246
  if (i < 1)
247
+ if i < 0
248
+ target_index = self.index
249
+ self.index = 1
250
+ end
218
251
  i = [self.viewport.controller.load_history, 1].max
252
+ i = target_index if target_index
219
253
  end
220
254
 
221
255
  return self.scroll_to_row(i)
@@ -226,7 +260,12 @@ module MarsBase10
226
260
  end
227
261
 
228
262
  def visible_content_range
229
- ((self.first_row + @visible_content_shift)..(self.last_drawable_row + @visible_content_shift))
263
+ # ((self.first_drawable_row + @visible_content_shift)..(self.last_drawable_row + @visible_content_shift))
264
+ (@first_visible_index..@last_visible_index)
265
+ end
266
+
267
+ def visible_content_shift
268
+ self.visible_content_range.first - self.first_drawable_row
230
269
  end
231
270
 
232
271
  def window
@@ -27,20 +27,27 @@ module MarsBase10
27
27
  def fetch_channel(group_title:, channel_title:)
28
28
  if (group = @ship.groups[title: group_title])
29
29
  if (channel = group.graphs.select {|c| channel_title == c.title unless c.nil?}.first)
30
- # What we are calling a channel here is really a graph in the urbit-ruby bridge.
31
- # This is the equivalent of node.to_pretty_array
32
- props = {
33
- title: channel.title,
34
- description: channel.description,
35
- creator: channel.creator,
36
- host_ship: channel.host_ship,
37
- resource: channel.resource,
38
- type: channel.type
39
- }
40
- return props.each.map {|k, v| "#{k}#{(' ' * [(18 - k.length), 0].max)}#{v}"}
30
+ return channel
41
31
  end
42
32
  end
43
- ["Group not found."]
33
+ nil
34
+ end
35
+
36
+ def fetch_channel_props(group_title:, channel_title:)
37
+ if (channel = self.fetch_channel(group_title: group_title, channel_title: channel_title))
38
+ # What we are calling a channel here is really a graph in the urbit-ruby bridge.
39
+ # This is the equivalent of node.to_pretty_array
40
+ props = {
41
+ title: channel.title,
42
+ description: channel.description,
43
+ creator: channel.creator,
44
+ host_ship: channel.host_ship,
45
+ resource: channel.resource,
46
+ type: channel.type
47
+ }
48
+ return props.each.map {|k, v| "#{k}#{(' ' * [(18 - k.length), 0].max)}#{v}"}
49
+ end
50
+ ["Channel not found."]
44
51
  end
45
52
 
46
53
  def fetch_group(group_title:)
@@ -71,12 +78,12 @@ module MarsBase10
71
78
  n.to_pretty_array
72
79
  end
73
80
 
74
- def fetch_node_list(resource:)
75
- @ship.graph(resource: resource).newest_nodes(count: 60).map {|node| node.index}.sort
81
+ def fetch_node_list(resource:, count: 60)
82
+ @ship.graph(resource: resource).newest_nodes(count: count).map {|node| node.index}.sort
76
83
  end
77
84
 
78
- def fetch_older_nodes(resource:, node:)
79
- @ship.graph(resource: resource).older_sibling_nodes(node: node, count: 60).map {|node| node.index}.sort
85
+ def fetch_older_nodes(resource:, node:, count: 60)
86
+ @ship.graph(resource: resource).older_sibling_nodes(node: node, count: count).map {|node| node.index}.sort
80
87
  end
81
88
  end
82
89
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MarsBase10
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -108,6 +108,11 @@ module MarsBase10
108
108
  Curses.close_screen
109
109
  end
110
110
 
111
+ def dispose_panes
112
+ @active_pane = nil
113
+ @panes = []
114
+ end
115
+
111
116
  def max_cols
112
117
  self.win.maxx
113
118
  end
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.5.0
4
+ version: 0.6.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: 2022-06-08 00:00:00.000000000 Z
11
+ date: 2022-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: curses
@@ -150,9 +150,11 @@ files:
150
150
  - lib/mars_base_10.rb
151
151
  - lib/mars_base_10/action_bar.rb
152
152
  - lib/mars_base_10/cli.rb
153
- - lib/mars_base_10/comm_central.rb
154
- - lib/mars_base_10/graph_rover.rb
155
- - lib/mars_base_10/group_room.rb
153
+ - lib/mars_base_10/controller.rb
154
+ - lib/mars_base_10/controller/graph_rover.rb
155
+ - lib/mars_base_10/controller/group_room.rb
156
+ - lib/mars_base_10/controller/social_lounge.rb
157
+ - lib/mars_base_10/mission_control.rb
156
158
  - lib/mars_base_10/pane.rb
157
159
  - lib/mars_base_10/ship.rb
158
160
  - lib/mars_base_10/stack.rb
@@ -1,128 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'ship'
4
- require_relative 'stack'
5
- require_relative 'subject'
6
-
7
- module MarsBase10
8
- class GroupRoom
9
- attr_reader :manager, :panes, :ship, :viewport
10
-
11
- def initialize(manager:, ship_connection:, viewport:)
12
- @manager = manager
13
- @ship = Ship.new connection: ship_connection
14
- @stack = Stack.new
15
- @viewport = viewport
16
- @viewport.controller = self
17
-
18
- self.wire_up_panes
19
- self.viewport.action_bar = ActionBar.Default.add_action({'i': 'Inspect'})
20
- self.viewport.activate pane: @pane_1
21
- self.resync
22
- end
23
-
24
- def active_node
25
- self.ship.fetch_node(resource: self.active_resource, index: self.active_node_index)
26
- end
27
-
28
- def active_resource
29
- @pane_1.current_subject_index
30
- end
31
-
32
- def active_subject(pane:)
33
- pane.current_subject_index
34
- end
35
-
36
- def load_history
37
- return 0 unless @pane_3 == self.viewport.active_pane
38
- new_content = self.ship.fetch_older_nodes(resource: self.active_resource, node: self.active_node)
39
- @pane_3.subject.prepend_content(ary: new_content)
40
- new_content.length
41
- end
42
-
43
- #
44
- # Called by a pane in this controller for bubbling a key press up
45
- #
46
- def send(key:)
47
- resync_needed = true
48
- case key
49
- when 'g' # (G)raph View
50
- unless @pane_1.active?
51
- self.viewport.activate pane: @pane_1
52
- end
53
- when 'i' # (I)nspect
54
- begin
55
- self.viewport.activate pane: @pane_3
56
- self.viewport.action_bar = ActionBar.Default.add_action({'g': 'Group List'})
57
- resync_needed = false
58
- end
59
- when 'X'
60
- self.manager.swap_controller
61
- end
62
- self.resync if resync_needed
63
- end
64
-
65
- def start
66
- self.viewport.open
67
- end
68
-
69
- def stop
70
- self.viewport.close
71
- end
72
-
73
- private
74
-
75
- def resync
76
- self.resync_node_list
77
- self.resync_node_view
78
- end
79
-
80
- def resync_node_list
81
- if @pane_1 == self.viewport.active_pane
82
- group_title = self.active_subject(pane: @pane_1)
83
- @pane_2.clear
84
- @pane_2.subject.title = "#{group_title}"
85
- @pane_2.subject.contents = self.ship.fetch_group(group_title: group_title)
86
-
87
- @pane_3.clear
88
- @pane_3.subject.title = "Channels of #{self.active_subject(pane: @pane_1)}"
89
- @pane_3.subject.contents = self.ship.fetch_group_channels(group_title: self.active_subject(pane: @pane_1))
90
- end
91
- nil
92
- end
93
-
94
- def resync_node_view
95
- channel_title = self.active_subject(pane: @pane_3)
96
- @pane_4.subject.title = "#{channel_title}"
97
- @pane_4.clear
98
- @pane_4.subject.contents = self.ship.fetch_channel(group_title: self.active_subject(pane: @pane_1), channel_title: channel_title)
99
- nil
100
- end
101
-
102
- def wire_up_panes
103
- @panes = []
104
-
105
- # Pane #1 is the Group list, It is a fixed height and width in the upper left corner.
106
- @pane_1 = @viewport.add_pane height_pct: 0.5, width_pct: 0.5
107
- # if @ship.group_names.empty?
108
- # @pane_1.view(subject: @ship.graph_names)
109
- # else
110
- @pane_1.view(subject: @ship.group_names)
111
- # end
112
-
113
- # Pane 2 displays the properties of the selected Group. It is variable height in the bottom left corner.
114
- @pane_2 = @viewport.add_variable_height_pane at_row: @pane_1.last_row, width_pct: 0.5
115
- @pane_2.view(subject: @ship.empty_node)
116
- @pane_2.highlight = false
117
-
118
- # The node list is a variable width, fixed height pane in the upper right.
119
- @pane_3 = @viewport.add_variable_width_pane at_col: @pane_1.last_col, height_pct: 0.5
120
- @pane_3.view(subject: @ship.empty_node_list)
121
-
122
- # The single node viewer is a variable width, variable height pane in the lower right.
123
- @pane_4 = @viewport.add_variable_both_pane at_row: @pane_3.last_row, at_col: @pane_1.last_col
124
- @pane_4.view(subject: @ship.empty_node)
125
- @pane_4.highlight = false
126
- end
127
- end
128
- end