foruiman 0.1.2
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 +7 -0
- data/CHANGELOG.md +37 -0
- data/LICENSE +19 -0
- data/README.md +102 -0
- data/bin/foruiman +6 -0
- data/docs/ARCHITECTURE.md +84 -0
- data/docs/ASSUMPTIONS.md +23 -0
- data/docs/COMPATIBILITY.md +27 -0
- data/docs/RAILS.md +54 -0
- data/docs/UPSTREAM.md +25 -0
- data/docs/terminal-preview.png +0 -0
- data/lib/foruiman/ansi.rb +147 -0
- data/lib/foruiman/cli.rb +83 -0
- data/lib/foruiman/diagnostics.rb +27 -0
- data/lib/foruiman/engine.rb +403 -0
- data/lib/foruiman/env.rb +35 -0
- data/lib/foruiman/log_store.rb +31 -0
- data/lib/foruiman/output.rb +76 -0
- data/lib/foruiman/plain.rb +22 -0
- data/lib/foruiman/process.rb +19 -0
- data/lib/foruiman/procfile.rb +81 -0
- data/lib/foruiman/ring.rb +53 -0
- data/lib/foruiman/tui/application.rb +105 -0
- data/lib/foruiman/tui/keyboard.rb +66 -0
- data/lib/foruiman/tui/log_formatter.rb +47 -0
- data/lib/foruiman/tui/renderer.rb +306 -0
- data/lib/foruiman/tui/state.rb +34 -0
- data/lib/foruiman/tui/terminal.rb +44 -0
- data/lib/foruiman/tui/text.rb +45 -0
- data/lib/foruiman/tui/theme.rb +71 -0
- data/lib/foruiman/tui/viewport.rb +56 -0
- data/lib/foruiman/version.rb +5 -0
- data/lib/foruiman.rb +11 -0
- metadata +116 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "unicode/display_width"
|
|
4
|
+
require_relative "../ansi"
|
|
5
|
+
|
|
6
|
+
module Foruiman::TUI
|
|
7
|
+
module Text
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def width(text)
|
|
11
|
+
Unicode::DisplayWidth.of(text.gsub(Foruiman::ANSI::SGR, ""), emoji: :all)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def clip(text, columns, ellipsis: false)
|
|
15
|
+
return "" unless columns.positive?
|
|
16
|
+
return text if width(text) <= columns
|
|
17
|
+
|
|
18
|
+
limit = ellipsis ? columns - 1 : columns
|
|
19
|
+
used = 0
|
|
20
|
+
output = +""
|
|
21
|
+
text.scan(/\e\[[0-9;:]*m|\X/).each do |token|
|
|
22
|
+
if token.start_with?("\e[")
|
|
23
|
+
output << token
|
|
24
|
+
next
|
|
25
|
+
end
|
|
26
|
+
cells = Unicode::DisplayWidth.of(token, emoji: :all)
|
|
27
|
+
break if used + cells > limit
|
|
28
|
+
|
|
29
|
+
output << token
|
|
30
|
+
used += cells
|
|
31
|
+
end
|
|
32
|
+
output << "…" if ellipsis
|
|
33
|
+
output
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def pad(text, columns)
|
|
37
|
+
clipped = clip(text, columns)
|
|
38
|
+
clipped + (" " * [columns - width(clipped), 0].max)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def clean(text)
|
|
42
|
+
Foruiman::ANSI::Decoder.new.feed(text, eof: true).gsub(Foruiman::ANSI::SGR, "").tr("\n", " ")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../ansi"
|
|
4
|
+
|
|
5
|
+
module Foruiman::TUI
|
|
6
|
+
# Use the terminal's palette and default background, so its theme owns the
|
|
7
|
+
# actual colors (and can change them while Foruiman is running).
|
|
8
|
+
class Theme
|
|
9
|
+
COLORS = {
|
|
10
|
+
text: 39, muted: 90, faint: 90, border: 90, accent: 36,
|
|
11
|
+
green: 32, red: 31, amber: 33, cyan: 36, blue: 34, mauve: 35
|
|
12
|
+
}.freeze
|
|
13
|
+
PROCESS_COLORS = %i[cyan amber mauve green blue red].freeze
|
|
14
|
+
STATUS_COLORS = {
|
|
15
|
+
pending: :faint, running: :green, restarting: :amber, stopping: :amber,
|
|
16
|
+
stopped: :muted, exited: :muted, failed: :red
|
|
17
|
+
}.freeze
|
|
18
|
+
STATUS_MARKS = {
|
|
19
|
+
pending: "○", running: "●", restarting: "↻", stopping: "◌",
|
|
20
|
+
stopped: "■", exited: "✓", failed: "×"
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
attr_reader :enabled
|
|
24
|
+
|
|
25
|
+
def initialize(env: ENV)
|
|
26
|
+
@enabled = env.fetch("NO_COLOR", "").empty? && env["TERM"] != "dumb"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def base
|
|
30
|
+
enabled ? "\e[39m\e[49m" : ""
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def paint(text, color = :text, selected: false, bold: false)
|
|
34
|
+
emphasis = "#{"\e[7m" if selected}#{"\e[1m" if bold}"
|
|
35
|
+
return emphasis.empty? ? text : "#{emphasis}#{text}#{reset}" unless enabled
|
|
36
|
+
|
|
37
|
+
defaults = selected ? base : "#{code(color)}\e[49m"
|
|
38
|
+
"#{defaults}#{emphasis}#{text}#{reset}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def reset
|
|
42
|
+
enabled ? Foruiman::ANSI::RESET + base : Foruiman::ANSI::RESET
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def process(index)
|
|
46
|
+
PROCESS_COLORS[index % PROCESS_COLORS.size]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# A child reset restores the UI's defaults. Explicit child colors, including
|
|
50
|
+
# background colors, still win; they cannot bleed into borders or later rows.
|
|
51
|
+
def log(text, color: :text)
|
|
52
|
+
return text.gsub(Foruiman::ANSI::SGR, "") unless enabled
|
|
53
|
+
|
|
54
|
+
defaults = "#{code(color)}\e[49m"
|
|
55
|
+
styles = Foruiman::ANSI::Styles.new
|
|
56
|
+
content = text.gsub(Foruiman::ANSI::SGR) do |sequence|
|
|
57
|
+
styles.apply(sequence)
|
|
58
|
+
Foruiman::ANSI::RESET + defaults + styles.prefix
|
|
59
|
+
end
|
|
60
|
+
defaults + content + reset
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def code(color)
|
|
66
|
+
return "" unless enabled
|
|
67
|
+
|
|
68
|
+
"\e[#{COLORS.fetch(color)}m"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Foruiman::TUI
|
|
4
|
+
class Viewport
|
|
5
|
+
attr_reader :following, :anchor
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@following = true
|
|
9
|
+
@anchor = nil
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def rows(buffer, height)
|
|
13
|
+
height = [height, 0].max
|
|
14
|
+
start = top(buffer, height)
|
|
15
|
+
Array.new([height, buffer.size - start].min) { |offset| buffer[start + offset] }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def scroll(delta, buffer, height)
|
|
19
|
+
index = top(buffer, height)
|
|
20
|
+
@following = false
|
|
21
|
+
set_anchor(index + delta, buffer, height)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def home(buffer)
|
|
25
|
+
@following = false
|
|
26
|
+
@anchor = buffer[0]&.sequence
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def follow
|
|
30
|
+
@following = true
|
|
31
|
+
@anchor = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def toggle(buffer, height)
|
|
35
|
+
following ? scroll(0, buffer, height) : follow
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def top(buffer, height)
|
|
41
|
+
last = [buffer.size - height, 0].max
|
|
42
|
+
return last if following
|
|
43
|
+
|
|
44
|
+
index = buffer.index_of(anchor) || 0
|
|
45
|
+
index = [index, last].min
|
|
46
|
+
@anchor = buffer[index]&.sequence
|
|
47
|
+
index
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def set_anchor(index, buffer, height)
|
|
51
|
+
last = [buffer.size - height, 0].max
|
|
52
|
+
index = index.clamp(0, last)
|
|
53
|
+
@anchor = buffer[index]&.sequence
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/foruiman.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: foruiman
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Foruiman contributors
|
|
8
|
+
- David Dollar
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: thor
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.3'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '2'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '1.3'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: unicode-display_width
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '3.1'
|
|
40
|
+
- - "<"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '4'
|
|
43
|
+
type: :runtime
|
|
44
|
+
prerelease: false
|
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '3.1'
|
|
50
|
+
- - "<"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '4'
|
|
53
|
+
description: Run a Procfile in a terminal interface or a plain stream. Derived from
|
|
54
|
+
Foreman.
|
|
55
|
+
executables:
|
|
56
|
+
- foruiman
|
|
57
|
+
extensions: []
|
|
58
|
+
extra_rdoc_files: []
|
|
59
|
+
files:
|
|
60
|
+
- CHANGELOG.md
|
|
61
|
+
- LICENSE
|
|
62
|
+
- README.md
|
|
63
|
+
- bin/foruiman
|
|
64
|
+
- docs/ARCHITECTURE.md
|
|
65
|
+
- docs/ASSUMPTIONS.md
|
|
66
|
+
- docs/COMPATIBILITY.md
|
|
67
|
+
- docs/RAILS.md
|
|
68
|
+
- docs/UPSTREAM.md
|
|
69
|
+
- docs/terminal-preview.png
|
|
70
|
+
- lib/foruiman.rb
|
|
71
|
+
- lib/foruiman/ansi.rb
|
|
72
|
+
- lib/foruiman/cli.rb
|
|
73
|
+
- lib/foruiman/diagnostics.rb
|
|
74
|
+
- lib/foruiman/engine.rb
|
|
75
|
+
- lib/foruiman/env.rb
|
|
76
|
+
- lib/foruiman/log_store.rb
|
|
77
|
+
- lib/foruiman/output.rb
|
|
78
|
+
- lib/foruiman/plain.rb
|
|
79
|
+
- lib/foruiman/process.rb
|
|
80
|
+
- lib/foruiman/procfile.rb
|
|
81
|
+
- lib/foruiman/ring.rb
|
|
82
|
+
- lib/foruiman/tui/application.rb
|
|
83
|
+
- lib/foruiman/tui/keyboard.rb
|
|
84
|
+
- lib/foruiman/tui/log_formatter.rb
|
|
85
|
+
- lib/foruiman/tui/renderer.rb
|
|
86
|
+
- lib/foruiman/tui/state.rb
|
|
87
|
+
- lib/foruiman/tui/terminal.rb
|
|
88
|
+
- lib/foruiman/tui/text.rb
|
|
89
|
+
- lib/foruiman/tui/theme.rb
|
|
90
|
+
- lib/foruiman/tui/viewport.rb
|
|
91
|
+
- lib/foruiman/version.rb
|
|
92
|
+
homepage: https://github.com/Nuzair46/foruiman
|
|
93
|
+
licenses:
|
|
94
|
+
- MIT
|
|
95
|
+
metadata:
|
|
96
|
+
rubygems_mfa_required: 'true'
|
|
97
|
+
source_code_uri: https://github.com/Nuzair46/foruiman
|
|
98
|
+
changelog_uri: https://github.com/Nuzair46/foruiman/blob/main/CHANGELOG.md
|
|
99
|
+
rdoc_options: []
|
|
100
|
+
require_paths:
|
|
101
|
+
- lib
|
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
|
+
requirements:
|
|
104
|
+
- - ">="
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: '3.2'
|
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '0'
|
|
112
|
+
requirements: []
|
|
113
|
+
rubygems_version: 4.0.16
|
|
114
|
+
specification_version: 4
|
|
115
|
+
summary: A Procfile supervisor with process tabs, bounded logs, and isolated restarts
|
|
116
|
+
test_files: []
|