trmnl_preview 0.2.0 → 0.3.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/CHANGELOG.md +7 -0
- data/docs/preview.png +0 -0
- data/exe/trmnlp +2 -0
- data/lib/trmnl_preview/app.rb +38 -82
- data/lib/trmnl_preview/cmd/build.rb +25 -0
- data/lib/trmnl_preview/cmd/usage.rb +1 -0
- data/lib/trmnl_preview/context.rb +102 -0
- data/lib/trmnl_preview/liquid_filters.rb +6 -4
- data/lib/trmnl_preview/version.rb +1 -1
- data/lib/trmnl_preview.rb +3 -4
- data/web/public/black-case.jpg +0 -0
- data/web/public/clear-case.jpg +0 -0
- data/web/public/white-case.jpg +0 -0
- data/web/views/index.erb +113 -0
- metadata +8 -3
- data/views/index.erb +0 -42
- /data/{views → web/views}/render_view.erb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3944c7a74517407dbb4ee683c628111668149c622be67a4ba3d69a2655d8b48e
|
4
|
+
data.tar.gz: e6c9d4f7472e4b56d818884ba6ca33307706891da62c40121398036507568fcd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fd0a512a23f1439cc4030543f92272682917d1024ae8ef6eed7449f51d7677add6a0974ed4783b56ad088729ac53a7343e0c00d9026c83d762fb9f825406eb2
|
7
|
+
data.tar.gz: 70c1ba02c0df2d7e427fc079f0bee439fdeea0c120f348d562f734e10f10b210c11db34fbc99c76894afa5622714445efb1fee17fa44029080ba22c216462b1d
|
data/CHANGELOG.md
CHANGED
data/docs/preview.png
CHANGED
Binary file
|
data/exe/trmnlp
CHANGED
@@ -5,6 +5,8 @@ require_relative '../lib/trmnl_preview'
|
|
5
5
|
case ARGV[0].to_s.downcase
|
6
6
|
when 'serve'
|
7
7
|
require_relative '../lib/trmnl_preview/cmd/serve'
|
8
|
+
when 'build'
|
9
|
+
require_relative '../lib/trmnl_preview/cmd/build'
|
8
10
|
else
|
9
11
|
require_relative '../lib/trmnl_preview/cmd/usage'
|
10
12
|
end
|
data/lib/trmnl_preview/app.rb
CHANGED
@@ -1,97 +1,53 @@
|
|
1
|
-
|
2
|
-
require 'liquid'
|
3
|
-
require 'open-uri'
|
1
|
+
|
4
2
|
require 'sinatra'
|
5
3
|
require 'sinatra/base'
|
6
|
-
require 'toml-rb'
|
7
|
-
|
8
|
-
require_relative 'liquid_filters'
|
9
|
-
|
10
|
-
class TRMNLPreview::App < Sinatra::Base
|
11
|
-
# Constants
|
12
|
-
VIEWS = %w{full half_horizontal half_vertical quadrant}
|
13
4
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
5
|
+
require_relative 'context'
|
6
|
+
|
7
|
+
module TRMNLPreview
|
8
|
+
class App < Sinatra::Base
|
9
|
+
# Sinatra settings
|
10
|
+
set :views, File.join(File.dirname(__FILE__), '..', '..', 'web', 'views')
|
11
|
+
set :public_folder, File.join(File.dirname(__FILE__), '..', '..', 'web', 'public')
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
super
|
15
|
+
|
16
|
+
begin
|
17
|
+
@context = Context.new(settings.user_dir)
|
18
|
+
rescue StandardError => e
|
19
|
+
puts e.message
|
20
|
+
exit 1
|
21
|
+
end
|
24
22
|
|
25
|
-
|
26
|
-
puts "No config.toml found in #{settings.user_dir}"
|
27
|
-
exit 1
|
28
|
-
end
|
29
|
-
|
30
|
-
unless Dir.exist?(@user_views_dir)
|
31
|
-
puts "No views found at #{@user_views_dir}"
|
32
|
-
exit 1
|
23
|
+
@context.poll_data if @context.strategy == 'polling'
|
33
24
|
end
|
34
25
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
strategy = @config['strategy']
|
39
|
-
|
40
|
-
unless ['polling', 'webhook'].include?(strategy)
|
41
|
-
puts "Invalid strategy: #{strategy} (must be 'polling' or 'webhook')"
|
42
|
-
exit 1
|
26
|
+
post '/webhook' do
|
27
|
+
@context.set_data(request.body.read)
|
28
|
+
"OK"
|
43
29
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
if strategy == 'polling'
|
49
|
-
if url.nil?
|
50
|
-
puts "URL is required for polling strategy"
|
51
|
-
exit 1
|
52
|
-
end
|
53
|
-
|
54
|
-
print "Fetching #{url}... "
|
55
|
-
payload = URI.open(url, polling_headers).read
|
56
|
-
File.write(@data_json_path, payload)
|
57
|
-
puts "got #{payload.size} bytes"
|
58
|
-
end
|
59
|
-
|
60
|
-
@liquid_environment = Liquid::Environment.build do |env|
|
61
|
-
env.register_filter(TRMNLPreview::LiquidFilters)
|
30
|
+
|
31
|
+
get '/' do
|
32
|
+
redirect '/full'
|
62
33
|
end
|
63
|
-
end
|
64
34
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
"OK"
|
69
|
-
end
|
70
|
-
|
71
|
-
get '/' do
|
72
|
-
redirect '/full'
|
73
|
-
end
|
74
|
-
|
75
|
-
VIEWS.each do |view|
|
76
|
-
get "/#{view}" do
|
77
|
-
@view = view
|
78
|
-
erb :index
|
35
|
+
get '/poll' do
|
36
|
+
@context.poll_data
|
37
|
+
redirect back
|
79
38
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
39
|
+
|
40
|
+
VIEWS.each do |view|
|
41
|
+
get "/#{view}" do
|
42
|
+
@view = view
|
43
|
+
erb :index
|
85
44
|
end
|
86
45
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
data = { data: data } if data.is_a?(Array) # per TRMNL docs, bare array is wrapped in 'data' key
|
93
|
-
|
94
|
-
user_template.render(data)
|
46
|
+
get "/render/#{view}" do
|
47
|
+
@view = view
|
48
|
+
erb :render_view do
|
49
|
+
@context.render_template(view)
|
50
|
+
end
|
95
51
|
end
|
96
52
|
end
|
97
53
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'optionparser'
|
2
|
+
|
3
|
+
require_relative '../context'
|
4
|
+
|
5
|
+
OptionParser.new do |opts|
|
6
|
+
opts.banner = "Usage: trmnlp build [directory]"
|
7
|
+
end.parse!
|
8
|
+
|
9
|
+
root = ARGV[1] || Dir.pwd
|
10
|
+
begin
|
11
|
+
context = TRMNLPreview::Context.new(root)
|
12
|
+
rescue StandardError => e
|
13
|
+
puts e.message
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
|
17
|
+
context.poll_data
|
18
|
+
|
19
|
+
TRMNLPreview::VIEWS.each do |view|
|
20
|
+
output_path = File.join(context.temp_dir, "#{view}.html")
|
21
|
+
puts "Creating #{output_path}..."
|
22
|
+
File.write(output_path, context.render_full_page(view))
|
23
|
+
end
|
24
|
+
|
25
|
+
puts "Done!"
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'json'
|
4
|
+
require 'liquid'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'toml-rb'
|
7
|
+
|
8
|
+
require_relative 'liquid_filters'
|
9
|
+
|
10
|
+
module TRMNLPreview
|
11
|
+
class Context
|
12
|
+
attr_reader :strategy, :temp_dir
|
13
|
+
|
14
|
+
def initialize(root)
|
15
|
+
config_path = File.join(root, 'config.toml')
|
16
|
+
@user_views_dir = File.join(root, 'views')
|
17
|
+
@temp_dir = File.join(root, 'tmp')
|
18
|
+
@data_json_path = File.join(@temp_dir, 'data.json')
|
19
|
+
|
20
|
+
@liquid_environment = Liquid::Environment.build do |env|
|
21
|
+
env.register_filter(LiquidFilters)
|
22
|
+
end
|
23
|
+
|
24
|
+
unless File.exist?(config_path)
|
25
|
+
raise "No config.toml found in #{root}"
|
26
|
+
end
|
27
|
+
|
28
|
+
unless Dir.exist?(@user_views_dir)
|
29
|
+
raise "No views found at #{@user_views_dir}"
|
30
|
+
end
|
31
|
+
|
32
|
+
config = TomlRB.load_file(config_path)
|
33
|
+
@strategy = config['strategy']
|
34
|
+
@url = config['url']
|
35
|
+
@polling_headers = config['polling_headers'] || {}
|
36
|
+
|
37
|
+
unless ['polling', 'webhook'].include?(@strategy)
|
38
|
+
raise "Invalid strategy: #{strategy} (must be 'polling' or 'webhook')"
|
39
|
+
end
|
40
|
+
|
41
|
+
FileUtils.mkdir_p(@temp_dir)
|
42
|
+
end
|
43
|
+
|
44
|
+
def user_data
|
45
|
+
data = JSON.parse(File.read(@data_json_path))
|
46
|
+
data = { data: data } if data.is_a?(Array) # per TRMNL docs, bare array is wrapped in 'data' key
|
47
|
+
data
|
48
|
+
end
|
49
|
+
|
50
|
+
def poll_data
|
51
|
+
if @url.nil?
|
52
|
+
raise "URL is required for polling strategy"
|
53
|
+
end
|
54
|
+
|
55
|
+
print "Fetching #{@url}... "
|
56
|
+
|
57
|
+
if @url.match?(/^https?:\/\//)
|
58
|
+
payload = URI.open(@url, @polling_headers).read
|
59
|
+
else
|
60
|
+
payload = File.read(@url)
|
61
|
+
end
|
62
|
+
|
63
|
+
File.write(@data_json_path, payload)
|
64
|
+
puts "got #{payload.size} bytes"
|
65
|
+
|
66
|
+
user_data
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_data(payload)
|
70
|
+
File.write(@data_json_path, payload)
|
71
|
+
end
|
72
|
+
|
73
|
+
def view_path(view)
|
74
|
+
File.join(@user_views_dir, "#{view}.liquid")
|
75
|
+
end
|
76
|
+
|
77
|
+
def render_template(view)
|
78
|
+
path = view_path(view)
|
79
|
+
unless File.exist?(path)
|
80
|
+
return "Missing plugin template: views/#{view}.liquid"
|
81
|
+
end
|
82
|
+
|
83
|
+
user_template = Liquid::Template.parse(File.read(path), environment: @liquid_environment)
|
84
|
+
user_template.render(user_data)
|
85
|
+
end
|
86
|
+
|
87
|
+
def render_full_page(view)
|
88
|
+
page_erb_template = File.read(File.join(__dir__, '..', '..', 'web', 'views', 'render_view.erb'))
|
89
|
+
|
90
|
+
ERB.new(page_erb_template).result(ERBBinding.new(view).get_binding do
|
91
|
+
render_template(view)
|
92
|
+
end)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
class ERBBinding
|
98
|
+
def initialize(view) = @view = view
|
99
|
+
def get_binding = binding
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
|
-
module TRMNLPreview
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module TRMNLPreview
|
2
|
+
module LiquidFilters
|
3
|
+
def number_with_delimiter(number)
|
4
|
+
# TODO: Replace with ActiveSupport's number_with_delimiter
|
5
|
+
number.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
6
|
+
end
|
5
7
|
end
|
6
8
|
end
|
data/lib/trmnl_preview.rb
CHANGED
@@ -2,10 +2,9 @@
|
|
2
2
|
|
3
3
|
module TRMNLPreview; end
|
4
4
|
|
5
|
-
|
5
|
+
require_relative "trmnl_preview/context"
|
6
6
|
require_relative "trmnl_preview/version"
|
7
7
|
|
8
8
|
module TRMNLPreview
|
9
|
-
|
10
|
-
|
11
|
-
end
|
9
|
+
VIEWS = %w{full half_horizontal half_vertical quadrant}
|
10
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
data/web/views/index.erb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>TRMNL Preview</title>
|
7
|
+
<style>
|
8
|
+
body {
|
9
|
+
font-family: sans-serif;
|
10
|
+
margin: 10px;
|
11
|
+
}
|
12
|
+
main {
|
13
|
+
display: flex;
|
14
|
+
flex-direction: column;
|
15
|
+
width: fit-content;
|
16
|
+
}
|
17
|
+
menu {
|
18
|
+
padding: 0;
|
19
|
+
margin: 0;
|
20
|
+
display: flex;
|
21
|
+
justify-content: space-between;
|
22
|
+
}
|
23
|
+
menu a {
|
24
|
+
padding: 0.5em 1em;
|
25
|
+
background: #ddd;
|
26
|
+
border-radius: 0.5em;
|
27
|
+
display: inline-block;
|
28
|
+
text-decoration: none;
|
29
|
+
color: black;
|
30
|
+
}
|
31
|
+
menu a:hover {
|
32
|
+
background: #ccc;
|
33
|
+
}
|
34
|
+
menu a.active {
|
35
|
+
background: #333;
|
36
|
+
color: white;
|
37
|
+
}
|
38
|
+
.case {
|
39
|
+
width: 1000px;
|
40
|
+
height: 680px;
|
41
|
+
position: relative;
|
42
|
+
}
|
43
|
+
iframe {
|
44
|
+
position: absolute;
|
45
|
+
border: none;
|
46
|
+
left: 98px;
|
47
|
+
top: 65px;
|
48
|
+
filter: grayscale(100%);
|
49
|
+
}
|
50
|
+
.case .case-overlay {
|
51
|
+
position: absolute;
|
52
|
+
width: 100%;
|
53
|
+
height: 100%;
|
54
|
+
top: 0;
|
55
|
+
left: 0;
|
56
|
+
background-size: cover;
|
57
|
+
mix-blend-mode: multiply;
|
58
|
+
pointer-events: none;
|
59
|
+
}
|
60
|
+
.case--none .case-overlay { background: none; }
|
61
|
+
.case--white .case-overlay { background-image: url('white-case.jpg') }
|
62
|
+
.case--black .case-overlay { background-image: url('black-case.jpg') }
|
63
|
+
.case--clear .case-overlay { background-image: url('clear-case.jpg') }
|
64
|
+
.case--none iframe {
|
65
|
+
border: 1px solid black;
|
66
|
+
}
|
67
|
+
</style>
|
68
|
+
<script>
|
69
|
+
function updateCase() {
|
70
|
+
const value = document.querySelector('.select-case').value;
|
71
|
+
document.querySelector('.case').className = `case case--${value}`;
|
72
|
+
localStorage.setItem('trmnlp-case', value);
|
73
|
+
}
|
74
|
+
|
75
|
+
document.addEventListener('DOMContentLoaded', () => {
|
76
|
+
const caseValue = localStorage.getItem('trmnlp-case') || 'black';
|
77
|
+
|
78
|
+
const select = document.querySelector('.select-case');
|
79
|
+
select.value = caseValue;
|
80
|
+
select.addEventListener('change', () => { updateCase() });
|
81
|
+
|
82
|
+
updateCase();
|
83
|
+
});
|
84
|
+
</script>
|
85
|
+
</head>
|
86
|
+
<body>
|
87
|
+
<main>
|
88
|
+
<menu>
|
89
|
+
<div>
|
90
|
+
<a class="<%= 'active' if @view == 'full' %>" href="/full">Full</a>
|
91
|
+
<a class="<%= 'active' if @view == 'half_horizontal' %>" href="/half_horizontal">Half Horizontal</a>
|
92
|
+
<a class="<%= 'active' if @view == 'half_vertical' %>" href="/half_vertical">Half Vertical</a>
|
93
|
+
<a class="<%= 'active' if @view == 'quadrant' %>" href="/quadrant">Quadrant</a>
|
94
|
+
</div>
|
95
|
+
<div>
|
96
|
+
<select class="select-case">
|
97
|
+
<option value="none">None</option>
|
98
|
+
<option value="white">White</option>
|
99
|
+
<option value="black" selected>Black</option>
|
100
|
+
<option value="clear">Clear</option>
|
101
|
+
</select>
|
102
|
+
|
103
|
+
<a href="/poll">Poll</a>
|
104
|
+
</div>
|
105
|
+
</menu>
|
106
|
+
|
107
|
+
<div class="case case--black">
|
108
|
+
<iframe src="/render/<%= @view %>" width="800" height="480"></iframe>
|
109
|
+
<div class="case-overlay"></div>
|
110
|
+
</div>
|
111
|
+
</main>
|
112
|
+
</body>
|
113
|
+
</html>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trmnl_preview
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rockwell Schrock
|
@@ -96,13 +96,18 @@ files:
|
|
96
96
|
- exe/trmnlp
|
97
97
|
- lib/trmnl_preview.rb
|
98
98
|
- lib/trmnl_preview/app.rb
|
99
|
+
- lib/trmnl_preview/cmd/build.rb
|
99
100
|
- lib/trmnl_preview/cmd/serve.rb
|
100
101
|
- lib/trmnl_preview/cmd/usage.rb
|
102
|
+
- lib/trmnl_preview/context.rb
|
101
103
|
- lib/trmnl_preview/liquid_filters.rb
|
102
104
|
- lib/trmnl_preview/version.rb
|
103
105
|
- trmnl_preview.gemspec
|
104
|
-
-
|
105
|
-
-
|
106
|
+
- web/public/black-case.jpg
|
107
|
+
- web/public/clear-case.jpg
|
108
|
+
- web/public/white-case.jpg
|
109
|
+
- web/views/index.erb
|
110
|
+
- web/views/render_view.erb
|
106
111
|
homepage: https://github.com/schrockwell/trmnl_preview
|
107
112
|
licenses:
|
108
113
|
- MIT
|
data/views/index.erb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html lang="en">
|
3
|
-
<head>
|
4
|
-
<meta charset="UTF-8">
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
-
<title>TRMNL Preview</title>
|
7
|
-
<style>
|
8
|
-
body {
|
9
|
-
font-family: sans-serif;
|
10
|
-
background: #eee;
|
11
|
-
}
|
12
|
-
.menu {
|
13
|
-
margin-bottom: 1em;
|
14
|
-
}
|
15
|
-
.menu a {
|
16
|
-
padding: 0.5em 1em;
|
17
|
-
background: #ddd;
|
18
|
-
border-radius: 0.5em;
|
19
|
-
display: inline-block;
|
20
|
-
text-decoration: none;
|
21
|
-
color: black;
|
22
|
-
}
|
23
|
-
.menu a:hover {
|
24
|
-
background: #ccc;
|
25
|
-
}
|
26
|
-
.menu a.active {
|
27
|
-
background: #333;
|
28
|
-
color: white;
|
29
|
-
}
|
30
|
-
</style>
|
31
|
-
</head>
|
32
|
-
<body>
|
33
|
-
<div class="menu">
|
34
|
-
<a class="<%= 'active' if @view == 'full' %>" href="/full">Full</a>
|
35
|
-
<a class="<%= 'active' if @view == 'half_horizontal' %>" href="/half_horizontal">Half Horizontal</a>
|
36
|
-
<a class="<%= 'active' if @view == 'half_vertical' %>" href="/half_vertical">Half Vertical</a>
|
37
|
-
<a class="<%= 'active' if @view == 'quadrant' %>" href="/quadrant">Quadrant</a>
|
38
|
-
</div>
|
39
|
-
|
40
|
-
<iframe src="/render/<%= @view %>" width="800" height="480"></iframe>
|
41
|
-
</body>
|
42
|
-
</html>
|
File without changes
|