mars_base_10 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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