trmnl_preview 0.3.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/README.md +3 -0
- data/config.example.toml +3 -0
- data/lib/trmnl_preview/app.rb +24 -0
- data/lib/trmnl_preview/context.rb +35 -8
- data/lib/trmnl_preview/version.rb +1 -1
- data/trmnl_preview.gemspec +2 -0
- data/web/public/live-render.js +23 -0
- data/web/views/render_view.erb +5 -0
- metadata +30 -1
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/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/lib/trmnl_preview/app.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
|
2
|
+
require 'faye/websocket'
|
2
3
|
require 'sinatra'
|
3
4
|
require 'sinatra/base'
|
4
5
|
|
@@ -21,6 +22,13 @@ module TRMNLPreview
|
|
21
22
|
end
|
22
23
|
|
23
24
|
@context.poll_data if @context.strategy == 'polling'
|
25
|
+
|
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
|
24
32
|
end
|
25
33
|
|
26
34
|
post '/webhook' do
|
@@ -32,6 +40,21 @@ module TRMNLPreview
|
|
32
40
|
redirect '/full'
|
33
41
|
end
|
34
42
|
|
43
|
+
get '/live_render/:view' do
|
44
|
+
ws = Faye::WebSocket.new(request.env)
|
45
|
+
view = params['view']
|
46
|
+
|
47
|
+
ws.on(:open) do |event|
|
48
|
+
@live_render_clients[view] << ws
|
49
|
+
end
|
50
|
+
|
51
|
+
ws.on(:close) do |event|
|
52
|
+
@live_render_clients[view].delete(ws)
|
53
|
+
end
|
54
|
+
|
55
|
+
ws.rack_response
|
56
|
+
end
|
57
|
+
|
35
58
|
get '/poll' do
|
36
59
|
@context.poll_data
|
37
60
|
redirect back
|
@@ -45,6 +68,7 @@ module TRMNLPreview
|
|
45
68
|
|
46
69
|
get "/render/#{view}" do
|
47
70
|
@view = view
|
71
|
+
@live_render = @context.live_render
|
48
72
|
erb :render_view do
|
49
73
|
@context.render_template(view)
|
50
74
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'fileutils'
|
3
|
+
require 'filewatcher'
|
3
4
|
require 'json'
|
4
5
|
require 'liquid'
|
5
6
|
require 'open-uri'
|
@@ -9,7 +10,7 @@ require_relative 'liquid_filters'
|
|
9
10
|
|
10
11
|
module TRMNLPreview
|
11
12
|
class Context
|
12
|
-
attr_reader :strategy, :temp_dir
|
13
|
+
attr_reader :strategy, :temp_dir, :live_render
|
13
14
|
|
14
15
|
def initialize(root)
|
15
16
|
config_path = File.join(root, 'config.toml')
|
@@ -33,12 +34,36 @@ module TRMNLPreview
|
|
33
34
|
@strategy = config['strategy']
|
34
35
|
@url = config['url']
|
35
36
|
@polling_headers = config['polling_headers'] || {}
|
37
|
+
@live_render = config['live_render'] != false
|
36
38
|
|
37
39
|
unless ['polling', 'webhook'].include?(@strategy)
|
38
40
|
raise "Invalid strategy: #{strategy} (must be 'polling' or 'webhook')"
|
39
41
|
end
|
40
42
|
|
41
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
|
42
67
|
end
|
43
68
|
|
44
69
|
def user_data
|
@@ -75,13 +100,15 @@ module TRMNLPreview
|
|
75
100
|
end
|
76
101
|
|
77
102
|
def render_template(view)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
85
112
|
end
|
86
113
|
|
87
114
|
def render_full_page(view)
|
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
|
@@ -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
|
+
});
|
data/web/views/render_view.erb
CHANGED
@@ -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.3.
|
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
|
@@ -105,6 +133,7 @@ files:
|
|
105
133
|
- trmnl_preview.gemspec
|
106
134
|
- web/public/black-case.jpg
|
107
135
|
- web/public/clear-case.jpg
|
136
|
+
- web/public/live-render.js
|
108
137
|
- web/public/white-case.jpg
|
109
138
|
- web/views/index.erb
|
110
139
|
- web/views/render_view.erb
|