mars_base_10 0.4.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: fd093ec957e418592f791bb2dfc1e1e7efa56973fd553534278a6dcb818f3482
4
- data.tar.gz: 478b185df1d0c2280788691e4bd61bfac817c200348e63bb29a7a36e05f1eb84
3
+ metadata.gz: b0d8748c4298354146d42d5f36cbba46c4957d2e4604b58465b365c980041089
4
+ data.tar.gz: b881836e892d29f0e195422690ecdb4913daf0f58daeed3170bb9b8d4eeb83c0
5
5
  SHA512:
6
- metadata.gz: e9044477c9eb149f2212f5dd5e7e44eb8d0fa247e0db7a382aa5256708ba53027e8c48c18f992b4c2c7dde36281cd5f725f86662283d71f713f50ed6a08662a2
7
- data.tar.gz: ac4c86d4ab841a92e262138bdf6a290af01d93e65d1afe514d6076681bb217dc59bde4ec2e90ccd3e10d4936ae298387ae0d662565e3030e88658a1f0a78deb6
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
@@ -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', 'J': 'Page Down', 'K': 'Page Up', 'q': 'Quit'}
14
+ ActionBar.new actions: {'j': 'Move Down', 'k': 'Move Up', 'J': 'Page Down', 'K': 'Page Up', 'X': 'Switch App', 'q': 'Quit'}
15
15
  end
16
16
 
17
17
  def actions_first_col
@@ -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,37 +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 :panes, :ship, :viewport
10
-
11
- def initialize(ship_connection:, viewport:)
12
- @ship = Ship.new connection: ship_connection
13
- @stack = Stack.new
14
- @viewport = viewport
15
- @viewport.controller = self
16
-
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
22
-
6
+ class GraphRover < Controller
23
7
  def active_node_index
24
8
  @node_list_pane.current_subject_index
25
9
  end
26
10
 
27
- def active_node
28
- self.ship.fetch_node(resource: self.active_resource, index: self.active_node_index)
29
- end
30
-
31
- def active_resource
32
- @graph_list_pane.current_subject_index
33
- end
34
-
35
11
  def load_history
36
12
  return 0 unless @node_list_pane == self.viewport.active_pane
37
13
  new_content = self.ship.fetch_older_nodes(resource: self.active_resource, node: self.active_node)
@@ -48,7 +24,8 @@ module MarsBase10
48
24
  when 'd' # (D)ive
49
25
  begin
50
26
  if @node_view_pane.subject.contents[4].include?('true')
51
- 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])
52
29
  @stack.push(self.active_resource)
53
30
  @node_list_pane.clear
54
31
  @node_list_pane.subject.contents = self.ship.fetch_node_children(resource: self.active_resource, index: self.active_node_index)
@@ -57,12 +34,12 @@ module MarsBase10
57
34
  when 'i' # (I)nspect
58
35
  begin
59
36
  self.viewport.activate pane: @node_list_pane
60
- 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'})
61
38
  resync_needed = false
62
39
  end
63
40
  when 'g' # (G)raph View
64
- unless @graph_list_pane.active?
65
- self.viewport.activate pane: @graph_list_pane
41
+ unless @pane_1.active?
42
+ self.viewport.activate pane: @pane_1
66
43
  # resync_needed = false
67
44
  end
68
45
  when 'p' # (P)op
@@ -72,9 +49,11 @@ module MarsBase10
72
49
  @node_list_pane.subject.contents = self.ship.fetch_node_list(resource: resource)
73
50
  end
74
51
  if (@stack.length == 0)
75
- self.viewport.action_bar.remove_action(:p)
52
+ self.action_bar.remove_actions([:p])
76
53
  end
77
54
  end
55
+ when 'X'
56
+ self.manager.swap_controller
78
57
  end
79
58
  self.resync if resync_needed
80
59
  end
@@ -95,7 +74,7 @@ module MarsBase10
95
74
  end
96
75
 
97
76
  def resync_node_list
98
- if @graph_list_pane == self.viewport.active_pane
77
+ if @pane_1 == self.viewport.active_pane
99
78
  @node_list_pane.clear
100
79
  @node_list_pane.subject.title = "Nodes of #{self.active_resource}"
101
80
  @node_list_pane.subject.contents = self.ship.fetch_node_list(resource: self.active_resource)
@@ -120,16 +99,16 @@ module MarsBase10
120
99
  @panes = []
121
100
 
122
101
  # 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)
102
+ @pane_1 = @viewport.add_pane width_pct: 0.3
103
+ @pane_1.view(subject: @ship.graph_names)
125
104
 
126
105
  # 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
106
+ @node_list_pane = @viewport.add_variable_width_pane at_col: @pane_1.last_col, height_pct: 0.5
128
107
  @node_list_pane.view(subject: @ship.empty_node_list)
129
108
 
130
109
  # 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
110
+ @node_view_pane = @viewport.add_variable_both_pane at_row: @node_list_pane.last_row, at_col: @pane_1.last_col
132
111
  @node_view_pane.view(subject: @ship.empty_node)
133
112
  end
134
113
  end
135
- 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
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ require 'urbit'
3
+
4
+ require_relative 'controller/group_room'
5
+ require_relative 'controller/graph_rover'
6
+ require_relative 'controller/social_lounge'
7
+ require_relative 'viewport'
8
+
9
+ module MarsBase10
10
+ class Error < StandardError; end
11
+
12
+ class MissionControl
13
+ attr_accessor :controller
14
+ attr_reader :ship
15
+
16
+ def initialize(config_filename:)
17
+ @ship = Urbit.connect(config_file: config_filename)
18
+ @ship.login
19
+ sleep 2 # This is temporary for now, we need a way to know that the subscription callbacks have finished.
20
+ @viewport = Viewport.new
21
+ @controller = GroupRoom.new manager: self, ship_connection: @ship, viewport: @viewport
22
+ end
23
+
24
+ def activate
25
+ self.controller.start
26
+ end
27
+
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})
31
+ end
32
+
33
+ def shutdown
34
+ self.controller.stop
35
+ end
36
+
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
+ end
47
+ end
48
+ end
@@ -3,21 +3,24 @@ require 'curses'
3
3
 
4
4
  module MarsBase10
5
5
  class Pane
6
- attr_accessor :draw_row, :draw_col, :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
- @index = 1
14
- @latch = ''
15
- @latch_depth = 0
16
- @subject = nil
17
- @win = nil
18
- @viewport = viewport
19
- @visible_content_shift = 0
20
- @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
21
24
  end
22
25
 
23
26
  def active?
@@ -28,13 +31,17 @@ module MarsBase10
28
31
  self.reset
29
32
  self.prepare_for_writing_contents
30
33
  (0..(self.last_row - 1)).each do |item|
31
- self.window.setpos(self.draw_row, self.draw_col)
34
+ self.window.setpos(self.cur_draw_row, self.cur_draw_col)
32
35
  self.window.addstr("")
33
36
  self.window.clrtoeol
34
- self.draw_row += 1
37
+ self.cur_draw_row += 1
35
38
  end
36
39
  end
37
40
 
41
+ def current_item_index
42
+ (self.cur_draw_row - self.extended_lines) + self.visible_content_shift
43
+ end
44
+
38
45
  def current_subject_index
39
46
  self.subject.at(index: self.index)
40
47
  end
@@ -42,33 +49,42 @@ module MarsBase10
42
49
  def draw
43
50
  self.prepare_for_writing_contents
44
51
 
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
+ 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
52
58
 
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
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
58
69
  self.draw_line
59
- 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
60
77
  end
61
- self.draw_row -= 1
62
78
  else
63
- self.window.addstr("#{self.subject.line_at(index: item_index)}")
79
+ self.draw_line
80
+ self.window.addstr(" ")
81
+ self.cur_draw_row += 1
64
82
  end
65
-
66
- self.window.attroff(Curses::A_REVERSE) if item_index == self.index
67
83
  self.window.clrtoeol
68
- self.draw_row += 1
69
84
  end
70
-
71
85
  self.draw_border
86
+
87
+ @last_visible_index = self.current_item_index - 1 # Subtract one b/c we have already incremented before loop
72
88
  end
73
89
 
74
90
  def draw_border
@@ -79,7 +95,7 @@ module MarsBase10
79
95
  end
80
96
 
81
97
  def draw_line
82
- self.window.setpos(self.draw_row, self.draw_col)
98
+ self.window.setpos(self.cur_draw_row, self.cur_draw_col)
83
99
  end
84
100
 
85
101
  def draw_title
@@ -87,11 +103,11 @@ module MarsBase10
87
103
  self.window.addstr(" #{self.subject.title} (#{self.max_contents_rows} total) ")
88
104
  end
89
105
 
90
- def first_col
106
+ def first_drawable_col
91
107
  1
92
108
  end
93
109
 
94
- def first_row
110
+ def first_drawable_row
95
111
  1
96
112
  end
97
113
 
@@ -103,7 +119,8 @@ module MarsBase10
103
119
  # This is the _relative_ last column, e.g. the width of the pane in columns.
104
120
  #
105
121
  def last_col
106
- [(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
107
124
  end
108
125
 
109
126
  def last_drawable_row
@@ -111,12 +128,15 @@ module MarsBase10
111
128
  end
112
129
 
113
130
  #
114
- # 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
115
132
  #
116
133
  def last_row
117
134
  (self.viewport.max_rows * self.height_pct).floor
118
135
  end
119
136
 
137
+ #
138
+ # This is the height of the pane minus the border
139
+ #
120
140
  def last_visible_row
121
141
  self.last_row - 2
122
142
  end
@@ -137,8 +157,11 @@ module MarsBase10
137
157
  end
138
158
 
139
159
  def prepare_for_writing_contents
140
- self.draw_row = self.first_row
141
- 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
142
165
  end
143
166
 
144
167
  #
@@ -158,7 +181,7 @@ module MarsBase10
158
181
  when 'J'
159
182
  self.index = self.set_row([self.index + (self.last_visible_row), self.max_contents_rows].min)
160
183
  when 'K'
161
- self.index = self.set_row(self.index - (self.last_visible_row))
184
+ self.index = self.set_row(self.index - self.visible_content_range.count)
162
185
  when 'q'
163
186
  exit 0
164
187
  when ('0'..'9')
@@ -184,7 +207,7 @@ module MarsBase10
184
207
  @index = 1
185
208
  @latch = ''
186
209
  @latch_depth = 0
187
- @visible_content_shift = 0
210
+ # @visible_content_shift = 0
188
211
  end
189
212
 
190
213
  def right_pad
@@ -192,12 +215,19 @@ module MarsBase10
192
215
  end
193
216
 
194
217
  def scroll_to_row(index)
218
+ jump = [(index - self.index), (self.max_contents_rows - self.visible_content_range.last)].min
195
219
  if index > self.index
196
220
  # Scrolling down
197
- @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
198
225
  else
199
226
  # Scrolling up
200
- @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
201
231
  end
202
232
  [index, 1].max
203
233
  end
@@ -214,7 +244,12 @@ module MarsBase10
214
244
 
215
245
  # Check if we have tried to move "above" the visible screen limit (i = 1) and retrieve more items, if possible.
216
246
  if (i < 1)
247
+ if i < 0
248
+ target_index = self.index
249
+ self.index = 1
250
+ end
217
251
  i = [self.viewport.controller.load_history, 1].max
252
+ i = target_index if target_index
218
253
  end
219
254
 
220
255
  return self.scroll_to_row(i)
@@ -225,7 +260,12 @@ module MarsBase10
225
260
  end
226
261
 
227
262
  def visible_content_range
228
- ((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
229
269
  end
230
270
 
231
271
  def window
@@ -244,6 +284,16 @@ module MarsBase10
244
284
  end
245
285
  end
246
286
 
287
+ class VariableHeightPane < Pane
288
+ def last_col
289
+ (self.viewport.max_cols * self.width_pct).floor
290
+ end
291
+
292
+ def last_row
293
+ self.viewport.max_rows - self.top_row
294
+ end
295
+ end
296
+
247
297
  class VariableWidthPane < Pane
248
298
  def last_col
249
299
  self.viewport.max_cols - self.left_edge_col
@@ -20,6 +20,51 @@ module MarsBase10
20
20
  Subject.new title: 'Graphs', contents: @ship.graph_names
21
21
  end
22
22
 
23
+ def group_names
24
+ Subject.new title: 'Groups', contents: (@ship.groups.map {|g| g.to_list})
25
+ end
26
+
27
+ def fetch_channel(group_title:, channel_title:)
28
+ if (group = @ship.groups[title: group_title])
29
+ if (channel = group.graphs.select {|c| channel_title == c.title unless c.nil?}.first)
30
+ return channel
31
+ end
32
+ end
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."]
51
+ end
52
+
53
+ def fetch_group(group_title:)
54
+ if (group = @ship.groups[title: group_title])
55
+ # This is the equivalent of node.to_pretty_array
56
+ return group.to_h.each.map {|k, v| "#{k}#{(' ' * [(18 - k.length), 0].max)}#{v}"}
57
+ end
58
+ ["Group not found."]
59
+ end
60
+
61
+ def fetch_group_channels(group_title:)
62
+ if (group = @ship.groups[title: group_title])
63
+ return group.graphs.map {|g| g.nil? ? "Unnamed" : g.title}
64
+ end
65
+ ["No Channels Available."]
66
+ end
67
+
23
68
  def fetch_node(resource:, index:)
24
69
  @ship.graph(resource: resource).node(index: index)
25
70
  end
@@ -33,12 +78,12 @@ module MarsBase10
33
78
  n.to_pretty_array
34
79
  end
35
80
 
36
- def fetch_node_list(resource:)
37
- @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
38
83
  end
39
84
 
40
- def fetch_older_nodes(resource:, node:)
41
- @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
42
87
  end
43
88
  end
44
89
  end
@@ -27,6 +27,10 @@ module MarsBase10
27
27
  @contents = a_contents_array
28
28
  end
29
29
 
30
+ def empty?
31
+ self.contents.empty?
32
+ end
33
+
30
34
  def index_width
31
35
  [self.item_count.to_s.length, 2].max
32
36
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MarsBase10
4
- VERSION = "0.4.0"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -70,15 +70,6 @@ module MarsBase10
70
70
  @active_pane = p
71
71
  end
72
72
 
73
- def add_variable_width_pane(at_row: self.min_row, at_col: self.min_col, height_pct:)
74
- p = VariableWidthPane.new viewport: self,
75
- at_row: at_row,
76
- at_col: at_col,
77
- height_pct: height_pct
78
- @panes << p
79
- p
80
- end
81
-
82
73
  #
83
74
  # Adds a new variable width drawable area (VariableBothPane) to the
84
75
  # right-hand side of the viewport.
@@ -95,10 +86,33 @@ module MarsBase10
95
86
  p
96
87
  end
97
88
 
89
+ def add_variable_height_pane(at_row:, width_pct:)
90
+ p = VariableHeightPane.new viewport: self,
91
+ at_row: at_row,
92
+ at_col: self.min_col,
93
+ width_pct: width_pct
94
+ @panes << p
95
+ p
96
+ end
97
+
98
+ def add_variable_width_pane(at_row: self.min_row, at_col: self.min_col, height_pct:)
99
+ p = VariableWidthPane.new viewport: self,
100
+ at_row: at_row,
101
+ at_col: at_col,
102
+ height_pct: height_pct
103
+ @panes << p
104
+ p
105
+ end
106
+
98
107
  def close
99
108
  Curses.close_screen
100
109
  end
101
110
 
111
+ def dispose_panes
112
+ @active_pane = nil
113
+ @panes = []
114
+ end
115
+
102
116
  def max_cols
103
117
  self.win.maxx
104
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.4.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-05-10 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,8 +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
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
155
158
  - lib/mars_base_10/pane.rb
156
159
  - lib/mars_base_10/ship.rb
157
160
  - lib/mars_base_10/stack.rb
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'urbit'
3
-
4
- require_relative 'graph_rover'
5
- require_relative 'viewport'
6
-
7
- module MarsBase10
8
- class Error < StandardError; end
9
-
10
- class CommCentral
11
- def initialize(config_filename:)
12
- @viewport = Viewport.new
13
- @rover = GraphRover.new ship_connection: Urbit.connect(config_file: config_filename),
14
- viewport: @viewport
15
- end
16
-
17
- def activate
18
- self.rover.start
19
- end
20
-
21
- def shutdown
22
- self.rover.stop
23
- end
24
-
25
- private
26
-
27
- def rover
28
- @rover
29
- end
30
- end
31
- end