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 +4 -4
- data/bin/mb10 +2 -2
- data/lib/.DS_Store +0 -0
- data/lib/mars_base_10/action_bar.rb +2 -2
- data/lib/mars_base_10/cli.rb +2 -2
- data/lib/mars_base_10/{graph_rover.rb → controller/graph_rover.rb} +14 -38
- data/lib/mars_base_10/controller/group_room.rb +108 -0
- data/lib/mars_base_10/controller/social_lounge.rb +114 -0
- data/lib/mars_base_10/controller.rb +54 -0
- data/lib/mars_base_10/{comm_central.rb → mission_control.rb} +20 -20
- data/lib/mars_base_10/pane.rb +85 -46
- data/lib/mars_base_10/ship.rb +23 -16
- data/lib/mars_base_10/version.rb +1 -1
- data/lib/mars_base_10/viewport.rb +5 -0
- metadata +7 -5
- data/lib/mars_base_10/group_room.rb +0 -128
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0d8748c4298354146d42d5f36cbba46c4957d2e4604b58465b365c980041089
|
4
|
+
data.tar.gz: b881836e892d29f0e195422690ecdb4913daf0f58daeed3170bb9b8d4eeb83c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
5
|
+
# require "mars_base_10/mission_control"
|
6
6
|
|
7
7
|
# begin
|
8
|
-
# cc = MarsBase10::
|
8
|
+
# cc = MarsBase10::MissionControl.new
|
9
9
|
# cc.activate
|
10
10
|
# ensure
|
11
11
|
# cc.shutdown
|
data/lib/.DS_Store
CHANGED
Binary file
|
data/lib/mars_base_10/cli.rb
CHANGED
@@ -39,9 +39,9 @@ module MarsBase10
|
|
39
39
|
invoke :help, ["launch"]
|
40
40
|
else
|
41
41
|
if (config)
|
42
|
-
require_relative "
|
42
|
+
require_relative "mission_control"
|
43
43
|
begin
|
44
|
-
cc = MarsBase10::
|
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 '
|
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.
|
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.
|
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 @
|
66
|
-
self.viewport.activate 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.
|
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 @
|
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
|
-
@
|
127
|
-
@
|
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: @
|
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: @
|
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 '
|
5
|
-
require_relative '
|
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
|
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
|
25
|
-
|
26
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
data/lib/mars_base_10/pane.rb
CHANGED
@@ -3,22 +3,24 @@ require 'curses'
|
|
3
3
|
|
4
4
|
module MarsBase10
|
5
5
|
class Pane
|
6
|
-
attr_accessor :
|
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
|
-
@
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@latch
|
16
|
-
@latch_depth
|
17
|
-
@
|
18
|
-
@
|
19
|
-
@
|
20
|
-
@
|
21
|
-
@
|
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.
|
34
|
+
self.window.setpos(self.cur_draw_row, self.cur_draw_col)
|
33
35
|
self.window.addstr("")
|
34
36
|
self.window.clrtoeol
|
35
|
-
self.
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
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.
|
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.
|
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
|
106
|
+
def first_drawable_col
|
92
107
|
1
|
93
108
|
end
|
94
109
|
|
95
|
-
def
|
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
|
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.
|
142
|
-
self.
|
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 -
|
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
|
-
|
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
|
-
|
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.
|
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
|
data/lib/mars_base_10/ship.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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:
|
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:
|
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
|
data/lib/mars_base_10/version.rb
CHANGED
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
|
+
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-
|
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/
|
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
|