trmnl_preview 0.2.0 → 0.3.1
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/README.md +3 -0
- data/config.example.toml +3 -0
- data/docs/preview.png +0 -0
- data/exe/trmnlp +2 -0
- data/lib/trmnl_preview/app.rb +55 -75
- data/lib/trmnl_preview/cmd/build.rb +25 -0
- data/lib/trmnl_preview/cmd/usage.rb +1 -0
- data/lib/trmnl_preview/context.rb +129 -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/trmnl_preview.gemspec +2 -0
- data/web/public/black-case.jpg +0 -0
- data/web/public/clear-case.jpg +0 -0
- data/web/public/live-render.js +23 -0
- data/web/public/white-case.jpg +0 -0
- data/web/views/index.erb +113 -0
- data/{views → web/views}/render_view.erb +5 -0
- metadata +37 -3
- data/views/index.erb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a39d955b3dbcc869cde5e0210671a94dfa7af0ea1403fb71c883534d85b6eac
|
4
|
+
data.tar.gz: f0159db40a383ce3149071fd4826bdb9035abab379d442e8a93012a35f6cce30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cccd5ba2af16c057717ae4f0acb6843fad3cd9a7f305109817c5d195f278dd1102c39a20cbf21abbbae0e18395b41ac8e5b314f5c10ae763503cc3e877f1403b
|
7
|
+
data.tar.gz: 19eb007c4d077a3078cde135ca200873f5d050abe167306b878b3f7374e052c282d3cb348e806486ce62fa0d6c0913efb1b4feaf100e213ad16d6e5167426ed9
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -4,6 +4,8 @@ A basic self-hosted web server to ease the development and sharing of [TRMNL](ht
|
|
4
4
|
|
5
5
|
[Liquid](https://shopify.github.io/liquid/) templates are rendered locally as HTML, leveraging the [TRMNL Design System](https://usetrmnl.com/framework). This server does NOT generate a rendered BMP file. Hence, this is just a _preview_ of the final rendered dashboard.
|
6
6
|
|
7
|
+
The server watches the filesystem for changes to the Liquid templates, seamlessly updating the preview without the need to refresh.
|
8
|
+
|
7
9
|

|
8
10
|
|
9
11
|
## Creating a Plugin
|
@@ -53,6 +55,7 @@ When the strategy is "webhook", payloads can be POSTed to the `/webhook` endpoin
|
|
53
55
|
|
54
56
|
- `strategy` - Either "polling" or "webhook"
|
55
57
|
- `url` - The URL from which to fetch JSON data (polling strategy only)
|
58
|
+
- `live_render` - Set to `false` to disable automatic rendering when Liquid templates change (default `true`)
|
56
59
|
- `[polling_headers]` - A section of headers to append to the HTTP poll request (polling strategy only)
|
57
60
|
|
58
61
|
## Contributing
|
data/config.example.toml
CHANGED
@@ -5,6 +5,9 @@ strategy = "polling"
|
|
5
5
|
# Poll URL (required for polling strategy)
|
6
6
|
url = "https://example.com/data.json"
|
7
7
|
|
8
|
+
# Automatically re-render the view when Liquid templates change (default: true)
|
9
|
+
live_render = true
|
10
|
+
|
8
11
|
# Polling headers (optional, for polling strategy)
|
9
12
|
[polling_headers]
|
10
13
|
authorization = "bearer 123"
|
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,77 @@
|
|
1
|
-
|
2
|
-
require '
|
3
|
-
require 'open-uri'
|
1
|
+
|
2
|
+
require 'faye/websocket'
|
4
3
|
require 'sinatra'
|
5
4
|
require 'sinatra/base'
|
6
|
-
require 'toml-rb'
|
7
5
|
|
8
|
-
require_relative '
|
6
|
+
require_relative 'context'
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
module TRMNLPreview
|
9
|
+
class App < Sinatra::Base
|
10
|
+
# Sinatra settings
|
11
|
+
set :views, File.join(File.dirname(__FILE__), '..', '..', 'web', 'views')
|
12
|
+
set :public_folder, File.join(File.dirname(__FILE__), '..', '..', 'web', 'public')
|
13
|
+
|
14
|
+
def initialize(*args)
|
15
|
+
super
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
begin
|
18
|
+
@context = Context.new(settings.user_dir)
|
19
|
+
rescue StandardError => e
|
20
|
+
puts e.message
|
21
|
+
exit 1
|
22
|
+
end
|
19
23
|
|
20
|
-
|
21
|
-
@user_views_dir = File.join(settings.user_dir, 'views')
|
22
|
-
@temp_dir = File.join(settings.user_dir, 'tmp')
|
23
|
-
@data_json_path = File.join(@temp_dir, 'data.json')
|
24
|
+
@context.poll_data if @context.strategy == 'polling'
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
@live_render_clients = VIEWS.each_with_object({}) { |view, hash| hash[view] = [] }
|
27
|
+
@context.on_view_change do |view|
|
28
|
+
@live_render_clients[view].each do |ws|
|
29
|
+
ws.send(@context.render_template(view))
|
30
|
+
end
|
31
|
+
end
|
28
32
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
+
|
34
|
+
post '/webhook' do
|
35
|
+
@context.set_data(request.body.read)
|
36
|
+
"OK"
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/' do
|
40
|
+
redirect '/full'
|
33
41
|
end
|
34
42
|
|
35
|
-
|
43
|
+
get '/live_render/:view' do
|
44
|
+
ws = Faye::WebSocket.new(request.env)
|
45
|
+
view = params['view']
|
36
46
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
unless ['polling', 'webhook'].include?(strategy)
|
41
|
-
puts "Invalid strategy: #{strategy} (must be 'polling' or 'webhook')"
|
42
|
-
exit 1
|
43
|
-
end
|
44
|
-
|
45
|
-
url = @config['url']
|
46
|
-
polling_headers = @config['polling_headers'] || {}
|
47
|
-
|
48
|
-
if strategy == 'polling'
|
49
|
-
if url.nil?
|
50
|
-
puts "URL is required for polling strategy"
|
51
|
-
exit 1
|
47
|
+
ws.on(:open) do |event|
|
48
|
+
@live_render_clients[view] << ws
|
52
49
|
end
|
53
50
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
puts "got #{payload.size} bytes"
|
58
|
-
end
|
51
|
+
ws.on(:close) do |event|
|
52
|
+
@live_render_clients[view].delete(ws)
|
53
|
+
end
|
59
54
|
|
60
|
-
|
61
|
-
env.register_filter(TRMNLPreview::LiquidFilters)
|
55
|
+
ws.rack_response
|
62
56
|
end
|
63
|
-
end
|
64
57
|
|
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
|
58
|
+
get '/poll' do
|
59
|
+
@context.poll_data
|
60
|
+
redirect back
|
79
61
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
62
|
+
|
63
|
+
VIEWS.each do |view|
|
64
|
+
get "/#{view}" do
|
65
|
+
@view = view
|
66
|
+
erb :index
|
85
67
|
end
|
86
68
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
user_template.render(data)
|
69
|
+
get "/render/#{view}" do
|
70
|
+
@view = view
|
71
|
+
@live_render = @context.live_render
|
72
|
+
erb :render_view do
|
73
|
+
@context.render_template(view)
|
74
|
+
end
|
95
75
|
end
|
96
76
|
end
|
97
77
|
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,129 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'filewatcher'
|
4
|
+
require 'json'
|
5
|
+
require 'liquid'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'toml-rb'
|
8
|
+
|
9
|
+
require_relative 'liquid_filters'
|
10
|
+
|
11
|
+
module TRMNLPreview
|
12
|
+
class Context
|
13
|
+
attr_reader :strategy, :temp_dir, :live_render
|
14
|
+
|
15
|
+
def initialize(root)
|
16
|
+
config_path = File.join(root, 'config.toml')
|
17
|
+
@user_views_dir = File.join(root, 'views')
|
18
|
+
@temp_dir = File.join(root, 'tmp')
|
19
|
+
@data_json_path = File.join(@temp_dir, 'data.json')
|
20
|
+
|
21
|
+
@liquid_environment = Liquid::Environment.build do |env|
|
22
|
+
env.register_filter(LiquidFilters)
|
23
|
+
end
|
24
|
+
|
25
|
+
unless File.exist?(config_path)
|
26
|
+
raise "No config.toml found in #{root}"
|
27
|
+
end
|
28
|
+
|
29
|
+
unless Dir.exist?(@user_views_dir)
|
30
|
+
raise "No views found at #{@user_views_dir}"
|
31
|
+
end
|
32
|
+
|
33
|
+
config = TomlRB.load_file(config_path)
|
34
|
+
@strategy = config['strategy']
|
35
|
+
@url = config['url']
|
36
|
+
@polling_headers = config['polling_headers'] || {}
|
37
|
+
@live_render = config['live_render'] != false
|
38
|
+
|
39
|
+
unless ['polling', 'webhook'].include?(@strategy)
|
40
|
+
raise "Invalid strategy: #{strategy} (must be 'polling' or 'webhook')"
|
41
|
+
end
|
42
|
+
|
43
|
+
FileUtils.mkdir_p(@temp_dir)
|
44
|
+
|
45
|
+
start_filewatcher_thread if @live_render
|
46
|
+
end
|
47
|
+
|
48
|
+
def start_filewatcher_thread
|
49
|
+
Thread.new do
|
50
|
+
loop do
|
51
|
+
begin
|
52
|
+
Filewatcher.new(@user_views_dir).watch do |changes|
|
53
|
+
views = changes.map { |path, _change| File.basename(path, '.liquid') }
|
54
|
+
views.each do |view|
|
55
|
+
@view_change_callback.call(view) if @view_change_callback
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rescue => e
|
59
|
+
puts "Error during live render: #{e}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def on_view_change(&block)
|
66
|
+
@view_change_callback = block
|
67
|
+
end
|
68
|
+
|
69
|
+
def user_data
|
70
|
+
data = JSON.parse(File.read(@data_json_path))
|
71
|
+
data = { data: data } if data.is_a?(Array) # per TRMNL docs, bare array is wrapped in 'data' key
|
72
|
+
data
|
73
|
+
end
|
74
|
+
|
75
|
+
def poll_data
|
76
|
+
if @url.nil?
|
77
|
+
raise "URL is required for polling strategy"
|
78
|
+
end
|
79
|
+
|
80
|
+
print "Fetching #{@url}... "
|
81
|
+
|
82
|
+
if @url.match?(/^https?:\/\//)
|
83
|
+
payload = URI.open(@url, @polling_headers).read
|
84
|
+
else
|
85
|
+
payload = File.read(@url)
|
86
|
+
end
|
87
|
+
|
88
|
+
File.write(@data_json_path, payload)
|
89
|
+
puts "got #{payload.size} bytes"
|
90
|
+
|
91
|
+
user_data
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_data(payload)
|
95
|
+
File.write(@data_json_path, payload)
|
96
|
+
end
|
97
|
+
|
98
|
+
def view_path(view)
|
99
|
+
File.join(@user_views_dir, "#{view}.liquid")
|
100
|
+
end
|
101
|
+
|
102
|
+
def render_template(view)
|
103
|
+
path = view_path(view)
|
104
|
+
unless File.exist?(path)
|
105
|
+
return "Missing plugin template: views/#{view}.liquid"
|
106
|
+
end
|
107
|
+
|
108
|
+
user_template = Liquid::Template.parse(File.read(path), environment: @liquid_environment)
|
109
|
+
user_template.render(user_data)
|
110
|
+
rescue StandardError => e
|
111
|
+
e.message
|
112
|
+
end
|
113
|
+
|
114
|
+
def render_full_page(view)
|
115
|
+
page_erb_template = File.read(File.join(__dir__, '..', '..', 'web', 'views', 'render_view.erb'))
|
116
|
+
|
117
|
+
ERB.new(page_erb_template).result(ERBBinding.new(view).get_binding do
|
118
|
+
render_template(view)
|
119
|
+
end)
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
class ERBBinding
|
125
|
+
def initialize(view) = @view = view
|
126
|
+
def get_binding = binding
|
127
|
+
end
|
128
|
+
end
|
129
|
+
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
|
data/trmnl_preview.gemspec
CHANGED
@@ -37,6 +37,8 @@ Gem::Specification.new do |spec|
|
|
37
37
|
spec.add_dependency "puma", "~> 6.5"
|
38
38
|
spec.add_dependency "liquid", "~> 5.6"
|
39
39
|
spec.add_dependency "toml-rb", "~> 3.0"
|
40
|
+
spec.add_dependency "filewatcher", "~> 2.1"
|
41
|
+
spec.add_dependency "faye-websocket", "~> 0.11.3"
|
40
42
|
|
41
43
|
# For more information and examples about making a new gem, check out our
|
42
44
|
# guide at: https://bundler.io/guides/creating_gem.html
|
Binary file
|
Binary file
|
@@ -0,0 +1,23 @@
|
|
1
|
+
live_render = {};
|
2
|
+
|
3
|
+
live_render.connect = function () {
|
4
|
+
const view = document.querySelector("meta[name='trmnl-view']").content;
|
5
|
+
const ws = new WebSocket("/live_render/" + view);
|
6
|
+
|
7
|
+
ws.onopen = function () {
|
8
|
+
console.log("Connected to live push server");
|
9
|
+
};
|
10
|
+
|
11
|
+
ws.onmessage = function (msg) {
|
12
|
+
document.querySelector(".view").innerHTML = msg.data;
|
13
|
+
};
|
14
|
+
|
15
|
+
ws.onclose = function () {
|
16
|
+
console.log("Reconnecting to live push server...");
|
17
|
+
setTimeout(live_render.connect, 1000);
|
18
|
+
};
|
19
|
+
};
|
20
|
+
|
21
|
+
document.addEventListener("DOMContentLoaded", function () {
|
22
|
+
live_render.connect();
|
23
|
+
});
|
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>
|
@@ -9,6 +9,11 @@
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap" rel="stylesheet">
|
11
11
|
<!-- End Inter font -->
|
12
|
+
|
13
|
+
<% if @live_render %>
|
14
|
+
<meta name="trmnl-view" content="<%= @view %>">
|
15
|
+
<script src="/live-render.js"></script>
|
16
|
+
<% end %>
|
12
17
|
</head>
|
13
18
|
<body class="environment trmnl">
|
14
19
|
<div class="screen">
|
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.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rockwell Schrock
|
@@ -79,6 +79,34 @@ dependencies:
|
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: '3.0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: filewatcher
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '2.1'
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '2.1'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: faye-websocket
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.11.3
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.11.3
|
82
110
|
description: Automatically rebuild and preview TRNML plugins in multiple views
|
83
111
|
email:
|
84
112
|
- rockwell@schrock.me
|
@@ -96,13 +124,19 @@ files:
|
|
96
124
|
- exe/trmnlp
|
97
125
|
- lib/trmnl_preview.rb
|
98
126
|
- lib/trmnl_preview/app.rb
|
127
|
+
- lib/trmnl_preview/cmd/build.rb
|
99
128
|
- lib/trmnl_preview/cmd/serve.rb
|
100
129
|
- lib/trmnl_preview/cmd/usage.rb
|
130
|
+
- lib/trmnl_preview/context.rb
|
101
131
|
- lib/trmnl_preview/liquid_filters.rb
|
102
132
|
- lib/trmnl_preview/version.rb
|
103
133
|
- trmnl_preview.gemspec
|
104
|
-
-
|
105
|
-
-
|
134
|
+
- web/public/black-case.jpg
|
135
|
+
- web/public/clear-case.jpg
|
136
|
+
- web/public/live-render.js
|
137
|
+
- web/public/white-case.jpg
|
138
|
+
- web/views/index.erb
|
139
|
+
- web/views/render_view.erb
|
106
140
|
homepage: https://github.com/schrockwell/trmnl_preview
|
107
141
|
licenses:
|
108
142
|
- 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>
|