mirror of
https://github.com/usetrmnl/trmnlp.git
synced 2026-08-13 14:27:33 -07:00
@@ -1,6 +1,11 @@
|
||||
|
||||
# Changelog
|
||||
|
||||
## 0.8.2
|
||||
|
||||
- Fixed `framework_version: latest` rendering against the auto-upgrading `/latest/` asset path instead of the current concrete release, matching the hosted service (#99)
|
||||
- Cleanup and minor improvements
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Added
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
trmnl_preview (0.8.1)
|
||||
trmnl_preview (0.8.2)
|
||||
activesupport (~> 8.0)
|
||||
cgi (~> 0.5)
|
||||
faraday (~> 2.1)
|
||||
|
||||
@@ -111,7 +111,7 @@ trmnlp build --png --color-depth 2
|
||||
| `--png` | Render a PNG per view alongside the HTML |
|
||||
| `--width` | PNG width in pixels (default 800) |
|
||||
| `--height` | PNG height in pixels (default 480) |
|
||||
| `--color-depth` | PNG bit depth — 1, 2, or 4 — overriding the markup |
|
||||
| `--color-depth` | PNG bit depth — 1-8 — overriding the markup |
|
||||
|
||||
`--width`, `--height`, and `--color-depth` apply only with `--png`. PNG rendering needs Firefox and ImageMagick installed; plain `trmnlp build` needs neither.
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ module TRMNLP
|
||||
method_option :png, type: :boolean, default: false, desc: 'Also render a PNG per view'
|
||||
method_option :width, type: :numeric, desc: 'PNG width in pixels (with --png)'
|
||||
method_option :height, type: :numeric, desc: 'PNG height in pixels (with --png)'
|
||||
method_option :color_depth, type: :numeric, desc: 'PNG bit depth: 1, 2, or 4 (with --png)'
|
||||
method_option :color_depth, type: :numeric, desc: 'PNG bit depth: 1-8 (with --png)'
|
||||
def build
|
||||
Commands::Build.run(options)
|
||||
end
|
||||
|
||||
@@ -44,9 +44,13 @@ module TRMNLP
|
||||
|
||||
def pinned? = @pinned
|
||||
|
||||
def css_url = "#{@asset_host}/css/#{path_segment}/plugins.css"
|
||||
# Both a pinned and an unpinned ("latest") version resolve to a
|
||||
# concrete release here — #number is never the literal "latest" — so a
|
||||
# local preview renders the same bundle as the hosted service instead
|
||||
# of drifting onto a new release the moment one ships.
|
||||
def css_url = "#{@asset_host}/css/#{number}/plugins.css"
|
||||
|
||||
def js_url = "#{@asset_host}/js/#{path_segment}/plugins.js"
|
||||
def js_url = "#{@asset_host}/js/#{number}/plugins.js"
|
||||
|
||||
def ==(other) = other.is_a?(self.class) && number == other.number
|
||||
|
||||
@@ -59,11 +63,5 @@ module TRMNLP
|
||||
def as_json(*) = number
|
||||
|
||||
def to_s = number
|
||||
|
||||
private
|
||||
|
||||
# When pinned, requests assets at /css/<version>/plugins.css to lock
|
||||
# behavior; otherwise hit /css/latest/ for live updates.
|
||||
def path_segment = pinned? ? @number : 'latest'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,8 +24,8 @@ module TRMNLP
|
||||
def pass? = static_image_urls.all? { |url| reachable?(url) }
|
||||
|
||||
def static_image_urls
|
||||
source.view_markup.values
|
||||
.flat_map { |html| html.scan(/<img[^>]+src\s*=\s*["']([^"']+)["']/i).flatten }
|
||||
source.all_markup
|
||||
.scan(/<img[^>]+src\s*=\s*["']([^"']+)["']/i).flatten
|
||||
.map(&:strip)
|
||||
.reject { |src| src.empty? || src.include?('{{') || src.start_with?('data:') }
|
||||
end
|
||||
|
||||
@@ -6,7 +6,8 @@ module TRMNLP
|
||||
module Lint
|
||||
module Checks
|
||||
class WaitsForDomLoad < Check
|
||||
MESSAGE = 'JavaScript should listen for the DOMContentLoaded event, not window.onLoad()'
|
||||
MESSAGE = 'JavaScript should listen for the DOMContentLoaded event, ' \
|
||||
'not window.onload or window.addEventListener("load")'
|
||||
LEARN_MORE = 'https://help.trmnl.com/en/articles/9510536-private-plugins#h_db7030f8b8'
|
||||
FORBIDDEN = ['window.onload', 'window.addeventlistener("load")',
|
||||
"window.addeventlistener('load')"].freeze
|
||||
|
||||
@@ -72,7 +72,7 @@ module TRMNLP
|
||||
end
|
||||
end
|
||||
|
||||
# bindings must match the `GET /render/{view}.html` route in app.rb
|
||||
# ivars must match the @-references in web/views/render_html.erb
|
||||
class TemplateBinding
|
||||
def initialize(renderer, view, params)
|
||||
@view = view
|
||||
|
||||
@@ -59,9 +59,7 @@ module TRMNLP
|
||||
TransformClient::Result.new(
|
||||
stdout: parsed['stdout'] || '',
|
||||
stderr: parsed['stderr'] || '',
|
||||
# Fall back to stdout for daemons that haven't been upgraded to
|
||||
# the separate `output` channel yet.
|
||||
output: (parsed['output'].to_s.empty? ? parsed['stdout'] : parsed['output']).to_s,
|
||||
output: parsed['output'].to_s,
|
||||
exit_code: parsed['exit_code'] || 0,
|
||||
duration_ms: parsed['duration_ms'] || 0,
|
||||
error: parsed['error']
|
||||
|
||||
@@ -9,8 +9,10 @@ module TRMNLP
|
||||
# snippet supplied by the caller. Subprocess writes to a tempfile
|
||||
# path, Http writes to FD 3 for the production daemon to capture.
|
||||
#
|
||||
# Mirrors the hosted serverless runtime's code-wrapping behavior
|
||||
# verbatim except for the configurable sink.
|
||||
# Mirrors the hosted serverless runtime's code-wrapping behavior,
|
||||
# parameterized on the per-backend output sink. PHP additionally
|
||||
# carries a leading `<?php` tag the file-based Subprocess path
|
||||
# needs; the daemon tolerates it, so parity holds.
|
||||
#
|
||||
# NOTE: `output_sink` is spliced verbatim into the generated script as
|
||||
# executable code. It MUST be trmnlp-generated (see Subprocess#sink_for
|
||||
|
||||
@@ -17,9 +17,9 @@ module TRMNLP
|
||||
|
||||
# Assembles the merged data hash. The trmnl namespace is built first,
|
||||
# layered with static_data / cached polled data / user_data_overrides,
|
||||
# then piped through the transform. The trmnl namespace is re-applied
|
||||
# after the transform so device, user, and plugin_settings survive
|
||||
# even when the transform doesn't pass them through.
|
||||
# then piped through the transform. The whole trmnl namespace is
|
||||
# re-applied after the transform so it survives even when the
|
||||
# transform doesn't pass it through.
|
||||
def call(device: {})
|
||||
namespace = base_trmnl_data(device:)
|
||||
merged = assemble(namespace)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module TRMNLP
|
||||
VERSION = '0.8.1'
|
||||
VERSION = '0.8.2'
|
||||
end
|
||||
|
||||
@@ -20,9 +20,14 @@ RSpec.describe TRMNLP::FrameworkVersion do
|
||||
context 'when version is "latest"' do
|
||||
subject(:framework) { described_class.new('latest') }
|
||||
|
||||
it 'serves from the /latest/ path so updates are live' do
|
||||
expect(framework.css_url).to eq('https://trmnl.com/css/latest/plugins.css')
|
||||
expect(framework.js_url).to eq('https://trmnl.com/js/latest/plugins.js')
|
||||
# NOTE: "latest" resolves to a concrete version rather than the
|
||||
# auto-upgrading /latest/ CDN path, matching the hosted service —
|
||||
# otherwise a local preview silently drifts when a new release ships.
|
||||
it 'serves from the concrete current version' do
|
||||
number = described_class.latest.number
|
||||
|
||||
expect(framework.css_url).to eq("https://trmnl.com/css/#{number}/plugins.css")
|
||||
expect(framework.js_url).to eq("https://trmnl.com/js/#{number}/plugins.js")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ require 'trmnlp/lint/checks/image_links_reachable'
|
||||
RSpec.describe TRMNLP::Lint::Checks::ImageLinksReachable do
|
||||
subject(:check) { described_class.new(source) }
|
||||
|
||||
let(:source) { instance_double(TRMNLP::Lint::Source, view_markup: { 'full' => markup }) }
|
||||
let(:source) { instance_double(TRMNLP::Lint::Source, all_markup: markup) }
|
||||
let(:markup) { '<img src="https://example.com/logo.png">' }
|
||||
|
||||
describe '#issues' do
|
||||
|
||||
@@ -80,16 +80,6 @@ RSpec.describe TRMNLP::TransformBackend::Http do
|
||||
expect(result).to be_success
|
||||
end
|
||||
|
||||
it 'falls back to stdout when output field is absent (older daemons)' do
|
||||
stubs.post('/execute') do
|
||||
[200, {}, '{"stdout":"{\"legacy\":true}","stderr":"","exit_code":0,"duration_ms":7,"error":null}']
|
||||
end
|
||||
|
||||
result = backend.execute(**payload)
|
||||
|
||||
expect(result.output).to eq('{"legacy":true}')
|
||||
end
|
||||
|
||||
it 'returns a failure Result when the daemon responds non-200' do
|
||||
stubs.post('/execute') { [500, {}, 'kaboom'] }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user