trmnl_preview 0.3.2 → 0.5.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 +44 -0
- data/README.md +75 -28
- data/bin/trmnlp +14 -0
- data/lib/trmnlp/api_client.rb +72 -0
- data/lib/{trmnl_preview → trmnlp}/app.rb +24 -12
- data/lib/trmnlp/cli.rb +66 -0
- data/lib/trmnlp/commands/base.rb +40 -0
- data/lib/trmnlp/commands/build.rb +21 -0
- data/lib/trmnlp/commands/clone.rb +30 -0
- data/lib/trmnlp/commands/init.rb +55 -0
- data/lib/trmnlp/commands/login.rb +24 -0
- data/lib/trmnlp/commands/pull.rb +43 -0
- data/lib/trmnlp/commands/push.rb +61 -0
- data/lib/trmnlp/commands/serve.rb +25 -0
- data/lib/trmnlp/commands.rb +1 -0
- data/lib/trmnlp/config/app.rb +43 -0
- data/lib/trmnlp/config/plugin.rb +74 -0
- data/lib/trmnlp/config/project.rb +47 -0
- data/lib/trmnlp/config.rb +15 -0
- data/lib/trmnlp/context.rb +211 -0
- data/lib/{trmnl_preview → trmnlp}/custom_filters.rb +2 -1
- data/lib/trmnlp/paths.rb +56 -0
- data/lib/{trmnl_preview → trmnlp}/screen_generator.rb +1 -1
- data/lib/trmnlp/version.rb +5 -0
- data/lib/trmnlp.rb +14 -0
- data/templates/init/.trmnlp.yml +14 -0
- data/templates/init/bin/dev +25 -0
- data/templates/init/src/full.liquid +1 -0
- data/templates/init/src/half_horizontal.liquid +1 -0
- data/templates/init/src/half_vertical.liquid +1 -0
- data/templates/init/src/quadrant.liquid +1 -0
- data/templates/init/src/settings.yml +15 -0
- data/trmnl_preview.gemspec +22 -13
- data/web/public/index.js +5 -1
- data/web/views/index.erb +2 -0
- data/web/views/render_html.erb +1 -1
- metadata +110 -24
- data/.ruby-version +0 -1
- data/config.example.toml +0 -14
- data/docs/preview.png +0 -0
- data/exe/trmnlp +0 -14
- data/lib/trmnl_preview/cmd/build.rb +0 -25
- data/lib/trmnl_preview/cmd/serve.rb +0 -31
- data/lib/trmnl_preview/cmd/usage.rb +0 -11
- data/lib/trmnl_preview/cmd/version.rb +0 -1
- data/lib/trmnl_preview/context.rb +0 -135
- data/lib/trmnl_preview/version.rb +0 -5
- data/lib/trmnl_preview.rb +0 -10
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'zip'
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
require_relative '../api_client'
|
5
|
+
|
6
|
+
module TRMNLP
|
7
|
+
module Commands
|
8
|
+
class Push < Base
|
9
|
+
def call
|
10
|
+
context.validate!
|
11
|
+
authenticate!
|
12
|
+
|
13
|
+
is_new = false
|
14
|
+
|
15
|
+
api = APIClient.new(config)
|
16
|
+
|
17
|
+
plugin_settings_id = options.id || config.plugin.id
|
18
|
+
if plugin_settings_id.nil?
|
19
|
+
output 'Creating a new plugin on the server...'
|
20
|
+
response = api.post_plugin_setting(name: 'New TRMNLP Plugin', plugin_id: 37) # hardcoded id for private_plugin
|
21
|
+
plugin_settings_id = response.dig('data', 'id')
|
22
|
+
is_new = true
|
23
|
+
end
|
24
|
+
|
25
|
+
unless is_new || options.force
|
26
|
+
answer = prompt("Plugin settings on the server will be overwritten. Are you sure? (y/n) ").downcase
|
27
|
+
raise Error, 'aborting' unless answer == 'y' || answer == 'yes'
|
28
|
+
end
|
29
|
+
|
30
|
+
size = 0
|
31
|
+
|
32
|
+
Tempfile.create(binmode: true) do |temp_file|
|
33
|
+
Zip::File.open(temp_file.path, Zip::File::CREATE) do |zip_file|
|
34
|
+
paths.src_files.each do |file|
|
35
|
+
zip_file.add(File.basename(file), file)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
response = api.post_plugin_setting_archive(plugin_settings_id, temp_file.path)
|
40
|
+
paths.plugin_config.write(response.dig('data', 'settings_yaml'))
|
41
|
+
|
42
|
+
size = File.size(temp_file.path)
|
43
|
+
end
|
44
|
+
|
45
|
+
output <<~HEREDOC
|
46
|
+
Uploaded plugin (#{size} bytes)
|
47
|
+
Dashboard: #{config.app.edit_plugin_settings_uri(plugin_settings_id)}
|
48
|
+
HEREDOC
|
49
|
+
|
50
|
+
if is_new
|
51
|
+
output <<~HEREDOC
|
52
|
+
|
53
|
+
IMPORTANT! Don't forget to add it to your device playlist!
|
54
|
+
|
55
|
+
#{config.app.playlists_uri}
|
56
|
+
HEREDOC
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'zip'
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
require_relative '../api_client'
|
5
|
+
|
6
|
+
module TRMNLP
|
7
|
+
module Commands
|
8
|
+
class Serve < Base
|
9
|
+
def call
|
10
|
+
context.validate!
|
11
|
+
|
12
|
+
# Must come AFTER parsing options
|
13
|
+
require_relative '../app'
|
14
|
+
|
15
|
+
# Now we can configure things
|
16
|
+
App.set(:context, context)
|
17
|
+
App.set(:bind, options.bind)
|
18
|
+
App.set(:port, options.port)
|
19
|
+
|
20
|
+
# Finally, start the app!
|
21
|
+
App.run!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join(__dir__, 'commands', '*.rb')].each { |file| require file }
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module TRMNLP
|
4
|
+
class Config
|
5
|
+
# Stores trmnlp-wide configuration (irrespective of the current plugin)
|
6
|
+
class App
|
7
|
+
def initialize(paths)
|
8
|
+
@paths = paths
|
9
|
+
@config = read_config
|
10
|
+
end
|
11
|
+
|
12
|
+
def save
|
13
|
+
paths.app_config_dir.mkpath
|
14
|
+
paths.app_config.write(YAML.dump(@config))
|
15
|
+
end
|
16
|
+
|
17
|
+
def logged_in? = api_key && !api_key.empty?
|
18
|
+
def logged_out? = !logged_in?
|
19
|
+
|
20
|
+
def api_key = @config['api_key']
|
21
|
+
|
22
|
+
def api_key=(key)
|
23
|
+
@config['api_key'] = key
|
24
|
+
end
|
25
|
+
|
26
|
+
def base_uri = URI.parse(@config['base_url'] || 'https://usetrmnl.com')
|
27
|
+
|
28
|
+
def api_uri = URI.join(base_uri, '/api')
|
29
|
+
|
30
|
+
def account_uri = URI.join(base_uri, '/account')
|
31
|
+
|
32
|
+
def edit_plugin_settings_uri(id) = URI.join(base_uri, "/plugin_settings/#{id.to_s}/edit")
|
33
|
+
|
34
|
+
def playlists_uri = URI.join(base_uri, '/playlists')
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :paths
|
39
|
+
|
40
|
+
def read_config = paths.app_config.exist? ? YAML.safe_load(paths.app_config.read) : {}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module TRMNLP
|
4
|
+
class Config
|
5
|
+
class Plugin
|
6
|
+
def initialize(paths, trmnlp_config)
|
7
|
+
@paths = paths
|
8
|
+
@trmnlp_config = trmnlp_config
|
9
|
+
reload!
|
10
|
+
end
|
11
|
+
|
12
|
+
def reload!
|
13
|
+
if paths.plugin_config.exist?
|
14
|
+
@config = YAML.load_file(paths.plugin_config)
|
15
|
+
else
|
16
|
+
@config = {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def strategy = @config['strategy']
|
21
|
+
def polling? = strategy == 'polling'
|
22
|
+
def webhook? = strategy == 'webhook'
|
23
|
+
def static? = strategy == 'static'
|
24
|
+
|
25
|
+
def polling_urls
|
26
|
+
return [] if @config['polling_url'].nil? || @config['polling_url'].empty?
|
27
|
+
|
28
|
+
urls = @config['polling_url'].split("\n").map(&:strip)
|
29
|
+
|
30
|
+
urls.map { |url| with_custom_fields(url) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def polling_url_text = polling_urls.join("\r\n") # for {{ trmnl }}
|
34
|
+
|
35
|
+
def polling_verb = @config['polling_verb'] || 'GET'
|
36
|
+
|
37
|
+
def polling_headers
|
38
|
+
string_to_hash(@config['polling_headers'] || '').transform_values { |v| with_custom_fields(v) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def polling_headers_encoded = polling_headers.map { |k, v| "#{k}=#{v}" }.join('&') # for {{ trmnl }}
|
42
|
+
|
43
|
+
def polling_body = with_custom_fields(@config['polling_body'] || '')
|
44
|
+
|
45
|
+
def dark_mode = @config['dark_mode'] || 'no'
|
46
|
+
|
47
|
+
def no_screen_padding = @config['no_screen_padding'] || 'no'
|
48
|
+
|
49
|
+
def id = @config['id']
|
50
|
+
|
51
|
+
def static_data
|
52
|
+
JSON.parse(@config['static_data'] || '{}')
|
53
|
+
rescue JSON::ParserError
|
54
|
+
raise Error, 'invalid JSON in static_data'
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
attr_reader :paths, :trmnlp_config
|
60
|
+
|
61
|
+
def with_custom_fields(value) = trmnlp_config.with_custom_fields(value)
|
62
|
+
|
63
|
+
# copied from TRMNL core
|
64
|
+
def string_to_hash(str, delimiter: '=')
|
65
|
+
str.split('&').map do |k_v|
|
66
|
+
key, value = k_v.split(delimiter)
|
67
|
+
next if value.nil?
|
68
|
+
|
69
|
+
{ key => CGI.unescape_uri_component(value) }
|
70
|
+
end.compact.reduce({}, :merge)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module TRMNLP
|
4
|
+
class Config
|
5
|
+
class Project
|
6
|
+
attr_reader :paths
|
7
|
+
|
8
|
+
def initialize(paths)
|
9
|
+
@paths = paths
|
10
|
+
reload!
|
11
|
+
end
|
12
|
+
|
13
|
+
def reload!
|
14
|
+
if paths.trmnlp_config.exist?
|
15
|
+
@config = YAML.load_file(paths.trmnlp_config)
|
16
|
+
else
|
17
|
+
@config = {}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def user_filters = @config['custom_filters'] || []
|
22
|
+
|
23
|
+
def live_render? = !watch_paths.empty?
|
24
|
+
|
25
|
+
def watch_paths
|
26
|
+
(@config['watch'] || []).map { |watch_path| paths.expand(watch_path) }.uniq
|
27
|
+
end
|
28
|
+
|
29
|
+
def custom_fields = @config['custom_fields'] || {}
|
30
|
+
|
31
|
+
def user_data_overrides = @config['variables'] || {}
|
32
|
+
|
33
|
+
# for interpolating custom_fields into polling_* options
|
34
|
+
def with_custom_fields(value)
|
35
|
+
custom_fields_with_env = custom_fields.transform_values { |v| with_env(v) }
|
36
|
+
Liquid::Template.parse(value).render(custom_fields_with_env)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# for interpolating ENV vars into custom_fields
|
42
|
+
def with_env(value)
|
43
|
+
Liquid::Template.parse(value).render({ 'env' => ENV.to_h })
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'config/app'
|
2
|
+
require_relative 'config/plugin'
|
3
|
+
require_relative 'config/project'
|
4
|
+
|
5
|
+
module TRMNLP
|
6
|
+
class Config
|
7
|
+
attr_reader :app, :project, :plugin
|
8
|
+
|
9
|
+
def initialize(path)
|
10
|
+
@app = App.new(path)
|
11
|
+
@project = Project.new(path)
|
12
|
+
@plugin = Plugin.new(path, @project)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'faraday'
|
3
|
+
require 'filewatcher'
|
4
|
+
require 'json'
|
5
|
+
require 'liquid'
|
6
|
+
|
7
|
+
require_relative 'config'
|
8
|
+
require_relative 'custom_filters'
|
9
|
+
require_relative 'paths'
|
10
|
+
|
11
|
+
module TRMNLP
|
12
|
+
class Context
|
13
|
+
attr_reader :config, :paths
|
14
|
+
|
15
|
+
def initialize(root_dir)
|
16
|
+
@paths = Paths.new(root_dir)
|
17
|
+
@config = Config.new(paths)
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate!
|
21
|
+
raise Error, "not a plugin directory (did not find #{paths.trmnlp_config})" unless paths.valid?
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_filewatcher
|
25
|
+
@filewatcher_thread ||= Thread.new do
|
26
|
+
loop do
|
27
|
+
begin
|
28
|
+
Filewatcher.new(config.project.watch_paths).watch do |changes|
|
29
|
+
config.project.reload!
|
30
|
+
config.plugin.reload!
|
31
|
+
new_user_data = user_data
|
32
|
+
|
33
|
+
views = changes.map { |path, _change| File.basename(path, '.liquid') }
|
34
|
+
views.each do |view|
|
35
|
+
@view_change_callback.call(view, new_user_data) if @view_change_callback
|
36
|
+
end
|
37
|
+
end
|
38
|
+
rescue => e
|
39
|
+
puts "Error during live render: #{e}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_view_change(&block)
|
46
|
+
@view_change_callback = block
|
47
|
+
end
|
48
|
+
|
49
|
+
def user_data
|
50
|
+
merged_data = base_trmnl_data
|
51
|
+
|
52
|
+
if config.plugin.static?
|
53
|
+
merged_data.merge!(config.plugin.static_data)
|
54
|
+
elsif paths.user_data.exist?
|
55
|
+
merged_data.merge!(JSON.parse(paths.user_data.read))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Praise be to ActiveSupport
|
59
|
+
merged_data.deep_merge!(config.project.user_data_overrides)
|
60
|
+
end
|
61
|
+
|
62
|
+
def poll_data
|
63
|
+
return unless config.plugin.polling?
|
64
|
+
|
65
|
+
data = {}
|
66
|
+
|
67
|
+
if config.plugin.polling_urls.empty?
|
68
|
+
raise Error, "config must specify polling_url or polling_urls"
|
69
|
+
end
|
70
|
+
|
71
|
+
config.plugin.polling_urls.each.with_index do |url, i|
|
72
|
+
verb = config.plugin.polling_verb.upcase
|
73
|
+
|
74
|
+
print "#{verb} #{url}... "
|
75
|
+
|
76
|
+
conn = Faraday.new(url:, headers: config.plugin.polling_headers)
|
77
|
+
|
78
|
+
case verb
|
79
|
+
when 'GET'
|
80
|
+
response = conn.get
|
81
|
+
when 'POST'
|
82
|
+
response = conn.post do |req|
|
83
|
+
req.body = config.plugin.polling_body
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
puts "received #{response.body.length} bytes (#{response.status} status)"
|
88
|
+
if response.status == 200
|
89
|
+
json = wrap_array(JSON.parse(response.body))
|
90
|
+
else
|
91
|
+
json = {}
|
92
|
+
puts response.body
|
93
|
+
end
|
94
|
+
|
95
|
+
if config.plugin.polling_urls.count == 1
|
96
|
+
# For a single polling URL, we just return the JSON directly
|
97
|
+
data = json
|
98
|
+
break
|
99
|
+
else
|
100
|
+
# Multiple URLs are namespaced by index
|
101
|
+
data["IDX_#{i}"] = json
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
write_user_data(data)
|
106
|
+
|
107
|
+
data
|
108
|
+
rescue StandardError => e
|
109
|
+
puts "error: #{e.message}"
|
110
|
+
{}
|
111
|
+
end
|
112
|
+
|
113
|
+
def put_webhook(payload)
|
114
|
+
data = wrap_array(JSON.parse(payload))
|
115
|
+
write_user_data(data)
|
116
|
+
rescue
|
117
|
+
puts "webhook error: #{e.message}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def render_template(view)
|
121
|
+
template_path = paths.template(view)
|
122
|
+
return "Missing template: #{template_path}" unless template_path.exist?
|
123
|
+
|
124
|
+
user_template = Liquid::Template.parse(template_path.read, environment: liquid_environment)
|
125
|
+
user_template.render(user_data)
|
126
|
+
rescue StandardError => e
|
127
|
+
e.message
|
128
|
+
end
|
129
|
+
|
130
|
+
def render_full_page(view)
|
131
|
+
template = paths.render_template.read
|
132
|
+
|
133
|
+
ERB.new(template).result(TemplateBinding.new(self, view).get_binding do
|
134
|
+
render_template(view)
|
135
|
+
end)
|
136
|
+
end
|
137
|
+
|
138
|
+
def screen_classes
|
139
|
+
classes = 'screen'
|
140
|
+
classes << ' screen--no-bleed' if config.plugin.no_screen_padding == 'yes'
|
141
|
+
classes << ' dark-mode' if config.plugin.dark_mode == 'yes'
|
142
|
+
classes
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
# bindings must match the `GET /render/{view}.html` route in app.rb
|
148
|
+
class TemplateBinding
|
149
|
+
def initialize(context, view)
|
150
|
+
@screen_classes = context.screen_classes
|
151
|
+
@view = view
|
152
|
+
end
|
153
|
+
|
154
|
+
def get_binding = binding
|
155
|
+
end
|
156
|
+
|
157
|
+
def wrap_array(json)
|
158
|
+
json.is_a?(Array) ? { data: json } : json
|
159
|
+
end
|
160
|
+
|
161
|
+
def base_trmnl_data
|
162
|
+
{
|
163
|
+
'trmnl' => {
|
164
|
+
'user' => {
|
165
|
+
'name' => 'name',
|
166
|
+
'first_name' => 'first_name',
|
167
|
+
'last_name' => 'last_name',
|
168
|
+
'locale' => 'en',
|
169
|
+
'time_zone' => 'Eastern Time (US & Canada)',
|
170
|
+
'time_zone_iana' => 'America/New_York',
|
171
|
+
'utc_offset' => -14400
|
172
|
+
},
|
173
|
+
'device' => {
|
174
|
+
'friendly_id' => 'ABC123',
|
175
|
+
'percent_charged' => 85.0,
|
176
|
+
'wifi_strength' => 90,
|
177
|
+
'height' => 480,
|
178
|
+
'width' => 800
|
179
|
+
},
|
180
|
+
'system' => {
|
181
|
+
'timestamp_utc' => Time.now.utc.to_i,
|
182
|
+
},
|
183
|
+
'plugin_settings' => {
|
184
|
+
'instance_name' => 'instance_name',
|
185
|
+
'strategy' => config.plugin.strategy,
|
186
|
+
'dark_mode' => config.plugin.dark_mode,
|
187
|
+
'polling_headers' => config.plugin.polling_headers_encoded,
|
188
|
+
'polling_url' => config.plugin.polling_url_text,
|
189
|
+
'custom_fields_values' => config.project.custom_fields
|
190
|
+
}
|
191
|
+
}
|
192
|
+
}
|
193
|
+
end
|
194
|
+
|
195
|
+
def liquid_environment
|
196
|
+
@liquid_environment ||= Liquid::Environment.build do |env|
|
197
|
+
env.register_filter(CustomFilters)
|
198
|
+
|
199
|
+
config.project.user_filters.each do |module_name, relative_path|
|
200
|
+
require paths.root_dir.join(relative_path)
|
201
|
+
env.register_filter(Object.const_get(module_name))
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def write_user_data(data)
|
207
|
+
paths.create_cache_dir
|
208
|
+
paths.user_data.write(JSON.generate(data))
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
data/lib/trmnlp/paths.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'xdg'
|
2
|
+
|
3
|
+
module TRMNLP
|
4
|
+
class Paths
|
5
|
+
attr_reader :root_dir
|
6
|
+
|
7
|
+
def initialize(root_dir)
|
8
|
+
@root_dir = Pathname.new(root_dir)
|
9
|
+
@xdg = XDG.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# --- trmnlp library ---
|
13
|
+
|
14
|
+
def gem_dir = Pathname.new(__dir__).join('..', '..').expand_path
|
15
|
+
|
16
|
+
def templates_dir = gem_dir.join('templates')
|
17
|
+
|
18
|
+
# --- directories ---
|
19
|
+
|
20
|
+
def src_dir = root_dir.join('src')
|
21
|
+
|
22
|
+
def build_dir = root_dir.join('_build')
|
23
|
+
def create_build_dir = build_dir.mkpath
|
24
|
+
|
25
|
+
def app_config_dir = xdg.config_home.join('trmnlp')
|
26
|
+
|
27
|
+
def cache_dir = xdg.cache_home.join('trmnl')
|
28
|
+
def create_cache_dir = cache_dir.mkpath
|
29
|
+
|
30
|
+
def valid? = trmnlp_config.exist?
|
31
|
+
|
32
|
+
# --- files ---
|
33
|
+
|
34
|
+
def trmnlp_config = root_dir.join('.trmnlp.yml')
|
35
|
+
|
36
|
+
def plugin_config = src_dir.join('settings.yml')
|
37
|
+
|
38
|
+
def template(view) = src_dir.join("#{view}.liquid")
|
39
|
+
|
40
|
+
def app_config = app_config_dir.join('config.yml')
|
41
|
+
|
42
|
+
def user_data = cache_dir.join('data.json')
|
43
|
+
|
44
|
+
def render_template = Pathname.new(__dir__).join('..', '..', 'web', 'views', 'render_html.erb')
|
45
|
+
|
46
|
+
def src_files = src_dir.glob('*').select(&:file?)
|
47
|
+
|
48
|
+
# --- utilities ---
|
49
|
+
|
50
|
+
def expand(path) = Pathname.new(path).expand_path(root_dir)
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
attr_reader :xdg
|
55
|
+
end
|
56
|
+
end
|
data/lib/trmnlp.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TRMNLP; end
|
4
|
+
require 'oj'
|
5
|
+
Oj.mimic_JSON()
|
6
|
+
require_relative "trmnlp/config"
|
7
|
+
require_relative "trmnlp/context"
|
8
|
+
require_relative "trmnlp/version"
|
9
|
+
|
10
|
+
module TRMNLP
|
11
|
+
VIEWS = %w{full half_horizontal half_vertical quadrant}
|
12
|
+
|
13
|
+
class Error < StandardError; end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# TRMNLP configuration
|
2
|
+
# {{ env.VARIABLE }} interpolation is available here
|
3
|
+
---
|
4
|
+
# auto-reload when files change (`watch: false` to disable)
|
5
|
+
watch:
|
6
|
+
- .trmnlp.yml
|
7
|
+
- src
|
8
|
+
|
9
|
+
# values of custom fields (defined in src/settings.yml)
|
10
|
+
custom_fields: {}
|
11
|
+
|
12
|
+
# override variables
|
13
|
+
variables:
|
14
|
+
trmnl: {}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#! /bin/bash
|
2
|
+
|
3
|
+
if command -v trmnlp &> /dev/null
|
4
|
+
then
|
5
|
+
echo "Starting trmnlp..."
|
6
|
+
trmnlp serve
|
7
|
+
exit
|
8
|
+
fi
|
9
|
+
|
10
|
+
if command -v docker &> /dev/null
|
11
|
+
then
|
12
|
+
echo "Running trmnl/trmnlp container..."
|
13
|
+
docker run -p 4567:4567 -v .:/plugin trmnl/trmnlp
|
14
|
+
exit
|
15
|
+
fi
|
16
|
+
|
17
|
+
echo "Install the trmnl_preview gem:
|
18
|
+
|
19
|
+
gem install trmnl_preview
|
20
|
+
|
21
|
+
Or install Docker:
|
22
|
+
|
23
|
+
https://docs.docker.com/get-docker/"
|
24
|
+
|
25
|
+
exit 1
|
@@ -0,0 +1 @@
|
|
1
|
+
full!
|
@@ -0,0 +1 @@
|
|
1
|
+
half horizontal!
|
@@ -0,0 +1 @@
|
|
1
|
+
half vertical!
|
@@ -0,0 +1 @@
|
|
1
|
+
quadrant!
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#
|
2
|
+
# Changes to this file will be overwritten by `trmnlp pull`.
|
3
|
+
#
|
4
|
+
# Docs: https://help.usetrmnl.com/en/articles/10542599-importing-and-exporting-private-plugins#h_581fb988f0
|
5
|
+
#
|
6
|
+
---
|
7
|
+
strategy: polling
|
8
|
+
no_screen_padding: 'no'
|
9
|
+
dark_mode: 'no'
|
10
|
+
static_data: ''
|
11
|
+
polling_verb: get
|
12
|
+
polling_url: ''
|
13
|
+
polling_headers: ''
|
14
|
+
name: My Plugin
|
15
|
+
refresh_interval: 1440
|