trmnl_preview 0.5.1 → 0.5.3
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 +5 -0
- data/README.md +4 -0
- data/bin/trmnlp +4 -1
- data/lib/markup/custom_liquid_filters.rb +36 -0
- data/lib/markup/inline_templates_file_system.rb +19 -0
- data/lib/markup/template.rb +17 -0
- data/lib/markup/template_tag.rb +31 -0
- data/lib/trmnlp/commands/login.rb +1 -0
- data/lib/trmnlp/config/project.rb +2 -0
- data/lib/trmnlp/context.rb +20 -6
- data/lib/trmnlp/paths.rb +2 -0
- data/lib/trmnlp/version.rb +1 -1
- data/trmnl_preview.gemspec +2 -0
- data/web/public/index.css +0 -45
- data/web/public/index.js +16 -11
- data/web/public/trmnl-component.js +782 -0
- data/web/views/index.erb +7 -8
- data/web/views/render_html.erb +1 -1
- metadata +35 -3
- data/lib/trmnlp/custom_filters.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c67d7a534ea6adba08ab5b46d40f60ec8e505123136916ba98e43991c6877f58
|
4
|
+
data.tar.gz: 5a22288cb32a76b2b8cb8d8163a38c964736e6fd7c067da6f76f9fe8c7401ec1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9df016907e67d1cdc0b771299162b769c83b2bb2c7d9ab4b0e5bd77cf8db3f6a8f15db93ad4a00810e0fd224cb2e3dbee1876c9fe416854f26f239704a28537a
|
7
|
+
data.tar.gz: bc4e3d5c46fc64d25a3791c77c0e43af5a84875c2ad2853742e3ba31cf7a5bbdef5a548d92a4bab4b27b048e089eae4e712cd99eec43ad1ee0902f27e39a364c
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -22,6 +22,7 @@ This is the structure of a plugin project:
|
|
22
22
|
├── half_horizontal.liquid
|
23
23
|
├── half_vertical.liquid
|
24
24
|
├── quadrant.liquid
|
25
|
+
├── shared.liquid
|
25
26
|
└── settings.yml
|
26
27
|
```
|
27
28
|
|
@@ -94,6 +95,9 @@ watch:
|
|
94
95
|
custom_fields:
|
95
96
|
station: "{{ env.ICAO }}" # interpolate $IACO environment variable
|
96
97
|
|
98
|
+
# Time zone IANA identifier to inject into trmnl.user; see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
99
|
+
time_zone: America/New_York
|
100
|
+
|
97
101
|
# override variables
|
98
102
|
variables:
|
99
103
|
trmnl:
|
data/bin/trmnlp
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module Markup
|
5
|
+
module CustomLiquidFilters
|
6
|
+
class ActionViewHelpers
|
7
|
+
include Singleton
|
8
|
+
include ActionView::Helpers
|
9
|
+
end
|
10
|
+
|
11
|
+
def number_with_delimiter(number, delimiter = ',', separator = ',')
|
12
|
+
ActionViewHelpers.instance.number_with_delimiter(number, delimiter:, separator:)
|
13
|
+
end
|
14
|
+
|
15
|
+
def number_to_currency(number, unit = '$', delimiter = ',', separator = '.')
|
16
|
+
ActionViewHelpers.instance.number_to_currency(number, unit: unit, delimiter:, separator:)
|
17
|
+
end
|
18
|
+
|
19
|
+
def l_word(word, locale)
|
20
|
+
I18n.t("custom_plugins.#{word}", locale: locale)
|
21
|
+
end
|
22
|
+
|
23
|
+
def l_date(date, format, locale = 'en')
|
24
|
+
format = format.to_sym unless format.include?('%')
|
25
|
+
I18n.l(date.to_datetime, :format => format, locale: locale)
|
26
|
+
end
|
27
|
+
|
28
|
+
def pluralize(singular, count)
|
29
|
+
ActionViewHelpers.instance.pluralize(count, singular)
|
30
|
+
end
|
31
|
+
|
32
|
+
def json(obj)
|
33
|
+
JSON.generate(obj)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Markup
|
2
|
+
# This in-memory "file system" is the backing storage for custom templates defined {% template [name] %} tags.
|
3
|
+
class InlineTemplatesFileSystem < Liquid::BlankFileSystem
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
@templates = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# called by Markup::LiquidTemplateTag to save users' custom shared templates via our custom {% template %} tag
|
10
|
+
def register(name, body)
|
11
|
+
@templates[name] = body
|
12
|
+
end
|
13
|
+
|
14
|
+
# called by Liquid::Template for {% render 'foo' %} when rendering screen markup
|
15
|
+
def read_template_file(name)
|
16
|
+
@templates[name] || raise(Liquid::FileSystemError, "Template not found: #{name}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# get all files in the current directory as this file
|
2
|
+
Pathname.new(__dir__).glob('*.rb').each { |file| require file }
|
3
|
+
|
4
|
+
module Markup
|
5
|
+
# A very thin wrapper around Liquid::Template with TRMNL-specific functionality.
|
6
|
+
class Template < Liquid::Template
|
7
|
+
def self.parse(*)
|
8
|
+
template = super
|
9
|
+
|
10
|
+
# set up a temporary in-memory file system for custom user templates, via the magic :file_system register
|
11
|
+
# which will override the default file system
|
12
|
+
template.registers[:file_system] = InlineTemplatesFileSystem.new
|
13
|
+
|
14
|
+
template
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Markup
|
2
|
+
# The {% template [name] %} tag block is used in conjunction with InlineTemplatesFileSystem to allow users to define
|
3
|
+
# custom templates within the context of the current Liquid template. Generally speaking, they will define their own
|
4
|
+
# templates in the "shared" markup content, which is prepended to the individual screen templates before rendering.
|
5
|
+
class TemplateTag < Liquid::Block
|
6
|
+
NAME_REGEX = %r{\A[a-zA-Z0-9_/]+\z}
|
7
|
+
|
8
|
+
def initialize(tag_name, markup, options)
|
9
|
+
super
|
10
|
+
@name = markup.strip
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse(tokens)
|
14
|
+
@body = ""
|
15
|
+
while (token = tokens.shift)
|
16
|
+
break if token.strip == "{% endtemplate %}"
|
17
|
+
|
18
|
+
@body << token
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def render(context)
|
23
|
+
unless @name =~ NAME_REGEX
|
24
|
+
return "Liquid error: invalid template name #{@name.inspect} - template names must contain only letters, numbers, underscores, and slashes"
|
25
|
+
end
|
26
|
+
|
27
|
+
context.registers[:file_system].register(@name, @body.strip)
|
28
|
+
''
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -13,6 +13,7 @@ module TRMNLP
|
|
13
13
|
|
14
14
|
api_key = prompt("API Key: ")
|
15
15
|
raise Error, "API key cannot be empty" if api_key.empty?
|
16
|
+
raise Error, "Invalid API key; did you copy it from the right place?" unless api_key.start_with?("user_")
|
16
17
|
|
17
18
|
config.app.api_key = api_key
|
18
19
|
config.app.save
|
data/lib/trmnlp/context.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'active_support/time'
|
1
2
|
require 'erb'
|
2
3
|
require 'faraday'
|
3
4
|
require 'filewatcher'
|
@@ -5,8 +6,8 @@ require 'json'
|
|
5
6
|
require 'liquid'
|
6
7
|
|
7
8
|
require_relative 'config'
|
8
|
-
require_relative 'custom_filters'
|
9
9
|
require_relative 'paths'
|
10
|
+
require_relative '../markup/template'
|
10
11
|
|
11
12
|
module TRMNLP
|
12
13
|
class Context
|
@@ -121,7 +122,14 @@ module TRMNLP
|
|
121
122
|
template_path = paths.template(view)
|
122
123
|
return "Missing template: #{template_path}" unless template_path.exist?
|
123
124
|
|
124
|
-
|
125
|
+
shared_template_path = paths.shared_template
|
126
|
+
if shared_template_path.exist?
|
127
|
+
full_markup = shared_template_path.read + template_path.read
|
128
|
+
else
|
129
|
+
full_markup = template_path.read
|
130
|
+
end
|
131
|
+
|
132
|
+
user_template = Markup::Template.parse(full_markup, environment: liquid_environment)
|
125
133
|
user_template.render(user_data)
|
126
134
|
rescue StandardError => e
|
127
135
|
e.message
|
@@ -159,6 +167,11 @@ module TRMNLP
|
|
159
167
|
end
|
160
168
|
|
161
169
|
def base_trmnl_data
|
170
|
+
tz = ActiveSupport::TimeZone.find_tzinfo(config.project.time_zone)
|
171
|
+
time_zone_iana = tz.name
|
172
|
+
time_zone_name = ActiveSupport::TimeZone::MAPPING.invert[time_zone_iana] || time_zone_iana
|
173
|
+
utc_offset = tz.utc_offset
|
174
|
+
|
162
175
|
{
|
163
176
|
'trmnl' => {
|
164
177
|
'user' => {
|
@@ -166,9 +179,9 @@ module TRMNLP
|
|
166
179
|
'first_name' => 'first_name',
|
167
180
|
'last_name' => 'last_name',
|
168
181
|
'locale' => 'en',
|
169
|
-
'time_zone' =>
|
170
|
-
'time_zone_iana' =>
|
171
|
-
'utc_offset' =>
|
182
|
+
'time_zone' => time_zone_name,
|
183
|
+
'time_zone_iana' => time_zone_iana,
|
184
|
+
'utc_offset' => utc_offset,
|
172
185
|
},
|
173
186
|
'device' => {
|
174
187
|
'friendly_id' => 'ABC123',
|
@@ -194,7 +207,8 @@ module TRMNLP
|
|
194
207
|
|
195
208
|
def liquid_environment
|
196
209
|
@liquid_environment ||= Liquid::Environment.build do |env|
|
197
|
-
env.register_filter(
|
210
|
+
env.register_filter(Markup::CustomLiquidFilters)
|
211
|
+
env.register_tag('template', Markup::TemplateTag)
|
198
212
|
|
199
213
|
config.project.user_filters.each do |module_name, relative_path|
|
200
214
|
require paths.root_dir.join(relative_path)
|
data/lib/trmnlp/paths.rb
CHANGED
data/lib/trmnlp/version.rb
CHANGED
data/trmnl_preview.gemspec
CHANGED
@@ -45,6 +45,7 @@ Gem::Specification.new do |spec|
|
|
45
45
|
# HTML rendering
|
46
46
|
spec.add_dependency "liquid", "~> 5.6"
|
47
47
|
spec.add_dependency "activesupport", "~> 8.0"
|
48
|
+
spec.add_dependency "actionview", "~> 8.0"
|
48
49
|
|
49
50
|
# BMP rendering
|
50
51
|
spec.add_dependency "ferrum", "~> 0.16"
|
@@ -59,6 +60,7 @@ Gem::Specification.new do |spec|
|
|
59
60
|
spec.add_dependency "rubyzip", "~> 2.3.0"
|
60
61
|
spec.add_dependency "thor", "~> 1.3"
|
61
62
|
spec.add_dependency "oj", "~> 3.16.9"
|
63
|
+
spec.add_dependency "tzinfo-data", "~> 1.2025"
|
62
64
|
|
63
65
|
# For more information and examples about making a new gem, check out our
|
64
66
|
# guide at: https://bundler.io/guides/creating_gem.html
|
data/web/public/index.css
CHANGED
@@ -34,51 +34,6 @@ menu a.active {
|
|
34
34
|
color: white;
|
35
35
|
}
|
36
36
|
|
37
|
-
.case {
|
38
|
-
width: 1000px;
|
39
|
-
height: 680px;
|
40
|
-
position: relative;
|
41
|
-
}
|
42
|
-
|
43
|
-
iframe {
|
44
|
-
position: absolute;
|
45
|
-
border: none;
|
46
|
-
left: 98px;
|
47
|
-
top: 65px;
|
48
|
-
filter: grayscale(100%);
|
49
|
-
}
|
50
|
-
|
51
|
-
.case .case-overlay {
|
52
|
-
position: absolute;
|
53
|
-
width: 100%;
|
54
|
-
height: 100%;
|
55
|
-
top: 0;
|
56
|
-
left: 0;
|
57
|
-
background-size: cover;
|
58
|
-
mix-blend-mode: multiply;
|
59
|
-
pointer-events: none;
|
60
|
-
}
|
61
|
-
|
62
|
-
.case--none .case-overlay {
|
63
|
-
background: none;
|
64
|
-
}
|
65
|
-
|
66
|
-
.case--white .case-overlay {
|
67
|
-
background-image: url("white-case.jpg");
|
68
|
-
}
|
69
|
-
|
70
|
-
.case--black .case-overlay {
|
71
|
-
background-image: url("black-case.jpg");
|
72
|
-
}
|
73
|
-
|
74
|
-
.case--clear .case-overlay {
|
75
|
-
background-image: url("clear-case.jpg");
|
76
|
-
}
|
77
|
-
|
78
|
-
.case--none iframe {
|
79
|
-
border: 1px solid black;
|
80
|
-
}
|
81
|
-
|
82
37
|
.spinner {
|
83
38
|
width: 22px;
|
84
39
|
height: 22px;
|
data/web/public/index.js
CHANGED
@@ -11,7 +11,7 @@ trmnlp.connectLiveRender = function () {
|
|
11
11
|
const payload = JSON.parse(msg.data);
|
12
12
|
|
13
13
|
if (payload.type === "reload") {
|
14
|
-
trmnlp.
|
14
|
+
trmnlp.setFrameSrc(trmnlp.frame._src);
|
15
15
|
trmnlp.userData.textContent = JSON.stringify(payload.user_data, null, 2);
|
16
16
|
}
|
17
17
|
};
|
@@ -22,9 +22,9 @@ trmnlp.connectLiveRender = function () {
|
|
22
22
|
};
|
23
23
|
};
|
24
24
|
|
25
|
-
trmnlp.
|
25
|
+
trmnlp.setFrameColor = function () {
|
26
26
|
const value = trmnlp.caseSelect.value;
|
27
|
-
document.querySelector("
|
27
|
+
document.querySelector("trmnl-frame").setAttribute("color", value);
|
28
28
|
localStorage.setItem("trmnlp-case", value);
|
29
29
|
};
|
30
30
|
|
@@ -32,17 +32,17 @@ trmnlp.setPreviewFormat = function () {
|
|
32
32
|
const value = trmnlp.formatSelect.value;
|
33
33
|
localStorage.setItem("trmnlp-format", value);
|
34
34
|
|
35
|
-
trmnlp.
|
35
|
+
trmnlp.setFrameSrc(`/render/${trmnlp.view}.${value}`);
|
36
36
|
};
|
37
37
|
|
38
|
-
trmnlp.
|
38
|
+
trmnlp.setFrameSrc = function (src) {
|
39
39
|
document.querySelector(".spinner").style.display = "inline-block";
|
40
|
-
trmnlp.
|
40
|
+
trmnlp.frame.setSrc(src);
|
41
41
|
};
|
42
42
|
|
43
43
|
document.addEventListener("DOMContentLoaded", function () {
|
44
44
|
trmnlp.view = document.querySelector("meta[name='trmnl-view']").content;
|
45
|
-
trmnlp.
|
45
|
+
trmnlp.frame = document.querySelector("trmnl-frame");
|
46
46
|
trmnlp.caseSelect = document.querySelector(".select-case");
|
47
47
|
trmnlp.formatSelect = document.querySelector(".select-format");
|
48
48
|
trmnlp.userData = document.getElementById("user-data");
|
@@ -58,7 +58,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|
58
58
|
|
59
59
|
trmnlp.caseSelect.value = caseValue;
|
60
60
|
trmnlp.caseSelect.addEventListener("change", () => {
|
61
|
-
trmnlp.
|
61
|
+
trmnlp.setFrameColor();
|
62
62
|
});
|
63
63
|
|
64
64
|
trmnlp.formatSelect.value = formatValue;
|
@@ -66,10 +66,15 @@ document.addEventListener("DOMContentLoaded", function () {
|
|
66
66
|
trmnlp.setPreviewFormat();
|
67
67
|
});
|
68
68
|
|
69
|
-
trmnlp.
|
69
|
+
trmnlp.frame._iframe.addEventListener("load", () => {
|
70
70
|
document.querySelector(".spinner").style.display = "none";
|
71
|
+
|
72
|
+
// On page load, trmnl-frame loads "about:blank", so wait for that to load
|
73
|
+
// before updating the src to the preview.
|
74
|
+
if (trmnlp.frame._src === null) {
|
75
|
+
trmnlp.setPreviewFormat();
|
76
|
+
}
|
71
77
|
});
|
72
78
|
|
73
|
-
trmnlp.
|
74
|
-
trmnlp.setPreviewFormat();
|
79
|
+
trmnlp.setFrameColor();
|
75
80
|
});
|