trmnl_preview 0.7.1 → 0.8.0

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -1
  3. data/README.md +180 -5
  4. data/bin/rake +6 -6
  5. data/bin/trmnlp +1 -0
  6. data/db/data/form_fields.yml +24 -0
  7. data/db/data/framework_versions.yml +72 -0
  8. data/lib/trmnlp/api_client.rb +41 -28
  9. data/lib/trmnlp/app.rb +73 -44
  10. data/lib/trmnlp/browser_pool.rb +82 -0
  11. data/lib/trmnlp/cli.rb +24 -11
  12. data/lib/trmnlp/commands/base.rb +33 -10
  13. data/lib/trmnlp/commands/build.rb +13 -8
  14. data/lib/trmnlp/commands/clone.rb +12 -7
  15. data/lib/trmnlp/commands/init.rb +17 -13
  16. data/lib/trmnlp/commands/lint.rb +42 -0
  17. data/lib/trmnlp/commands/list.rb +40 -0
  18. data/lib/trmnlp/commands/login.rb +28 -13
  19. data/lib/trmnlp/commands/pull.rb +14 -6
  20. data/lib/trmnlp/commands/push.rb +29 -19
  21. data/lib/trmnlp/commands/serve.rb +32 -3
  22. data/lib/trmnlp/commands.rb +3 -1
  23. data/lib/trmnlp/config/app.rb +6 -3
  24. data/lib/trmnlp/config/plugin.rb +56 -14
  25. data/lib/trmnlp/config/project.rb +59 -7
  26. data/lib/trmnlp/config.rb +3 -1
  27. data/lib/trmnlp/context.rb +21 -224
  28. data/lib/trmnlp/errors.rb +15 -0
  29. data/lib/trmnlp/form_field.rb +42 -0
  30. data/lib/trmnlp/framework_version.rb +69 -0
  31. data/lib/trmnlp/image_quantizer.rb +58 -0
  32. data/lib/trmnlp/lint/check.rb +31 -0
  33. data/lib/trmnlp/lint/checks/custom_fields_used.rb +32 -0
  34. data/lib/trmnlp/lint/checks/form_fields_valid.rb +20 -0
  35. data/lib/trmnlp/lint/checks/highcharts_animations_disabled.rb +23 -0
  36. data/lib/trmnlp/lint/checks/highcharts_elements_unique.rb +24 -0
  37. data/lib/trmnlp/lint/checks/image_links_reachable.rb +53 -0
  38. data/lib/trmnlp/lint/checks/layouts_have_content.rb +24 -0
  39. data/lib/trmnlp/lint/checks/limited_inline_styles.rb +26 -0
  40. data/lib/trmnlp/lint/checks/no_async_functions.rb +18 -0
  41. data/lib/trmnlp/lint/checks/no_opacity.rb +19 -0
  42. data/lib/trmnlp/lint/checks/no_size_classes.rb +19 -0
  43. data/lib/trmnlp/lint/checks/title_casing.rb +20 -0
  44. data/lib/trmnlp/lint/checks/title_length.rb +18 -0
  45. data/lib/trmnlp/lint/checks/waits_for_dom_load.rb +23 -0
  46. data/lib/trmnlp/lint/source.rb +42 -0
  47. data/lib/trmnlp/lint.rb +39 -0
  48. data/lib/trmnlp/paths.rb +28 -8
  49. data/lib/trmnlp/poller.rb +105 -0
  50. data/lib/trmnlp/renderer.rb +87 -0
  51. data/lib/trmnlp/reporter.rb +28 -0
  52. data/lib/trmnlp/screen.rb +16 -0
  53. data/lib/trmnlp/screen_generator.rb +11 -217
  54. data/lib/trmnlp/screenshot.rb +96 -0
  55. data/lib/trmnlp/transform_backend/http.rb +107 -0
  56. data/lib/trmnlp/transform_backend/subprocess.rb +130 -0
  57. data/lib/trmnlp/transform_backend/wrapper.rb +113 -0
  58. data/lib/trmnlp/transform_client.rb +47 -0
  59. data/lib/trmnlp/transform_pipeline.rb +65 -0
  60. data/lib/trmnlp/user_data_assembler.rb +96 -0
  61. data/lib/trmnlp/version.rb +1 -1
  62. data/lib/trmnlp/watcher.rb +60 -0
  63. data/lib/trmnlp.rb +6 -10
  64. data/templates/init/bin/trmnlp +1 -1
  65. data/templates/init/src/settings.yml +1 -0
  66. data/templates/init/src/transform.py.example +14 -0
  67. data/trmnl_preview.gemspec +34 -34
  68. data/web/public/index.css +6 -0
  69. data/web/public/index.js +31 -18
  70. data/web/views/index.erb +6 -1
  71. data/web/views/render_html.erb +4 -2
  72. metadata +81 -56
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f442804907f15903eb4e5f543ad94eb18fdef92f82d191e67c4554621ca3c74
4
- data.tar.gz: 0e8df9b455430254cc007c28e9799155b6e75cccc675fd9790e9a260e998c84a
3
+ metadata.gz: bf11a528033d7882be345a627e2a480f1cdf95a5f9cef22d01f51f5b46012635
4
+ data.tar.gz: 973c3dc1b96f1d7a81ffa8e9fc82f403285d9449463d210684732a43028509d8
5
5
  SHA512:
6
- metadata.gz: ae89a85a00a4b5719128dc4e198cda157085c905b914a3f30ebf14b17f7c2ff3d3c4f87552e0c455d0691671998c003b019cf34dd5f948859f64749f05b615c0
7
- data.tar.gz: 59642ee5f4741bf88fbcedcdcc8af927ed80127b044afdc464144e660a283c055dd395d8e579d97b58b001e7f441ecafe63a838bb90f9ea6cc2ed4ea3f8aff69
6
+ metadata.gz: 39963f376ff877a4170ce57564876e5cb4bd6eacc23de4b1792a6cba6cd01f4252579fe19c8d678f21144f37e9ba050ed03866b38a3f7aaa9a67521aa324fd04
7
+ data.tar.gz: 835b494bb56d29e9e4b607a482ae05466b08aa527ff38c00fae2a98e87524bbcbb6db78c9abd5dc8288c3b36692008107a1c9bbbdd298c36ddf1d435cd0d2e65
data/CHANGELOG.md CHANGED
@@ -1,6 +1,59 @@
1
1
 
2
2
  # Changelog
3
3
 
4
+ ## 0.8.0
5
+
6
+ ### Housekeeping
7
+
8
+ - Upgraded the development, CI, and Docker baseline to Ruby 4.0.4
9
+ - Replaced the faye-websocket live reload with server-sent events, removing the eventmachine dependency
10
+ - Upgraded `filewatcher` to 3.x for Ruby 4.0 support
11
+ - Upgraded `mini_magick` to 5.x (ImageMagick 7 only)
12
+ - Upgraded `rubyzip` to 3.x
13
+ - Upgraded `puma` to 8.x
14
+ - Upgraded `oj` and `selenium-webdriver` to their latest releases
15
+ - Upgraded `trmnl-liquid` to 0.7 and `xdg` to 10
16
+ - Added the `cgi` gem, removed from Ruby's standard library in 4.0
17
+ - Dropped the redundant `pathname` gem dependency; Ruby provides `Pathname` built in
18
+ - Added `.rspec` configuration and a `Rakefile`
19
+
20
+ ### Refactor
21
+
22
+ - Refactored screen generation into focused objects: `Screen`, `Screenshot`, `Renderer`, `ImageQuantizer`, `BrowserPool`, `Reporter`, `Watcher`, `Poller`, `UserDataAssembler`
23
+ - Added support for `text/html` and `text/plain` polling responses with JSON body sniffing (#81)
24
+ - Fixed Liquid conditionals (`{% if %}...{% endif %}`) spanning `polling_headers` values (#79)
25
+ - Fixed multi-select custom_fields being coerced into JSON strings — arrays now preserved (#80)
26
+ - Fixed `trmnl.device.{width,height}` in user-data so they reflect the picker's selected model (#94)
27
+ - Fixed `Permission denied` from `trmnlp clone` on Linux when overwriting template files (#83)
28
+ - Fixed deprecated `convert` warning by switching to mini_magick's `Magick` tool (#89)
29
+
30
+ ### Serverless Transforms
31
+
32
+ - Added `transform_runtime:` config in `.trmnlp.yml` — serverless transforms are enabled by default and run whenever a `src/transform.*` file is present; set to `disabled` to turn them off
33
+ - Added `serverless_daemon_url:` override for pointing at a remote transform daemon (production-fidelity testing, shared team daemons)
34
+ - Added `serverless_language:` override (`python`, `ruby`, `php`, `node`)
35
+ - Added detection of `src/transform.{py,rb,php,js}` with language inferred from extension
36
+ - Added `TRMNLP::TransformClient` strategy host that selects `TransformBackend::Subprocess` (default) or `TransformBackend::Http` (when `serverless_daemon_url` is set) via `.from_config`
37
+ - Added `TRMNLP::TransformBackend::Subprocess` — local subprocess execution mirroring the hosted serverless wrapper contract verbatim, output flows back via a per-execution tempfile
38
+ - Added `python3`, `nodejs`, and `php-cli` to the main `Dockerfile`'s runtime stage alongside the existing `ruby` so all four supported transform languages work out of the box — no sidecar required
39
+ - Added transform-error surfacing in the preview UI when execution fails
40
+ - Added filewatcher re-poll when transform source changes (hot reload)
41
+ - Added `examples/hn-stories/` — a complete worked example fetching Hacker News top stories and rendering across all four sizes
42
+
43
+ ### Framework Picker
44
+
45
+ - Added `framework_version:` plugin setting in `src/settings.yml` (defaults to `latest`, supports pinning to any released version) — round-trips through `trmnlp push`/`pull` alongside the hosted plugin archive format
46
+ - Added `framework_asset_host:` override in `.trmnlp.yml` for offline / mirrored development
47
+ - Added `TRMNLP::FrameworkVersion` mirroring the hosted framework versioning
48
+ - Added `rake framework:sync` to refresh `db/data/framework_versions.yml` from a local design-system checkout
49
+ - Updated `render_html.erb` to derive CSS/JS URLs from the resolved framework version
50
+
51
+ ### FormField & Init Template
52
+
53
+ - Added FormField schema vendored from the hosted service (`db/data/form_fields.yml`) covering the full field-type allowlist
54
+ - Refreshed the `trmnlp init` template to scaffold `framework_version` and transform configuration, including a `transform.py.example`
55
+ - Fixed non-portable `/bin/bash` shebang in the generated `bin/trmnlp` (#78)
56
+
4
57
  ## 0.7.0
5
58
 
6
59
  - Switch from Puppeteer + CDP to Selenium + WebDriver BiDi (@SorceressLyra)
@@ -29,7 +82,7 @@
29
82
 
30
83
  ## 0.5.7
31
84
 
32
- - Use the `trmnl-liquid` gem so tags and filters stay up-to-date with the core offering
85
+ - Use the `trmnl-liquid` gem so tags and filters stay up-to-date with the hosted offering
33
86
 
34
87
  ## 0.5.6
35
88
 
data/README.md CHANGED
@@ -1,15 +1,27 @@
1
1
  # trmnlp
2
2
 
3
+ [![CI](https://github.com/usetrmnl/trmnlp/actions/workflows/ci.yaml/badge.svg)](https://github.com/usetrmnl/trmnlp/actions/workflows/ci.yaml)
4
+ [![Gem Version](https://img.shields.io/gem/v/trmnl_preview)](https://rubygems.org/gems/trmnl_preview)
5
+
3
6
  A basic self-hosted web server to ease the development and sharing of [TRMNL](https://trmnl.com/) plugins.
4
7
 
5
8
  [Liquid](https://shopify.github.io/liquid/) templates are rendered leveraging the [TRMNL Design System](https://trmnl.com/framework). They may be generated as HTML (faster, and a good approximation of the final result) or as PNG images (slower, but more accurate).
6
9
 
7
- Custom Liquid filters and tags are provided by the [trmnl-liquid](https://github.com/usetrmnl/trmnl-liquid) gem.
8
-
9
10
  The server watches the filesystem for changes to the Liquid templates, seamlessly updating the preview without the need to refresh.
10
11
 
11
12
  ![Screenshot](docs/preview.png)
12
13
 
14
+ ## Quick Start
15
+
16
+ ```sh
17
+ gem install trmnl_preview # install
18
+ trmnlp init my_plugin # scaffold a project
19
+ cd my_plugin
20
+ trmnlp serve # preview at http://localhost:4567
21
+ ```
22
+
23
+ No Ruby on hand? Run it through Docker instead — see [Installing via Docker](#installing-via-docker).
24
+
13
25
  ## Project Structure
14
26
 
15
27
  This is the structure of a plugin project:
@@ -18,7 +30,7 @@ This is the structure of a plugin project:
18
30
  .
19
31
  ├── .trmnlp.yml
20
32
  ├── bin
21
- │ └── dev
33
+ │ └── trmnlp
22
34
  └── src
23
35
  ├── full.liquid
24
36
  ├── half_horizontal.liquid
@@ -28,6 +40,16 @@ This is the structure of a plugin project:
28
40
  └── settings.yml
29
41
  ```
30
42
 
43
+ | File | Purpose |
44
+ |---|---|
45
+ | `.trmnlp.yml` | Local dev-server config — not uploaded to TRMNL |
46
+ | `src/full.liquid` | Markup for the full screen |
47
+ | `src/half_horizontal.liquid` | Top or bottom half of a stacked mashup |
48
+ | `src/half_vertical.liquid` | Left or right half of a side-by-side mashup |
49
+ | `src/quadrant.liquid` | One quarter of a 2x2 mashup |
50
+ | `src/shared.liquid` | Reusable markup included by the other templates |
51
+ | `src/settings.yml` | Plugin configuration — uploaded to TRMNL |
52
+
31
53
  ## Creating a New Plugin
32
54
 
33
55
  You can start building a plugin locally, then `push` it to the TRMNL server for display on your device.
@@ -52,6 +74,23 @@ trmnlp serve # develop locally
52
74
  trmnlp push # upload
53
75
  ```
54
76
 
77
+ ## Commands
78
+
79
+ | Command | Description |
80
+ |---|---|
81
+ | `trmnlp init NAME` | Start a new plugin project |
82
+ | `trmnlp serve` | Start a local dev server |
83
+ | `trmnlp build` | Generate static HTML files |
84
+ | `trmnlp lint` | Check plugin code against TRMNL best practices |
85
+ | `trmnlp login` | Authenticate with TRMNL server |
86
+ | `trmnlp list` | List private plugins from TRMNL server |
87
+ | `trmnlp clone NAME ID` | Copy a plugin project from TRMNL server |
88
+ | `trmnlp pull` | Download latest plugin settings from TRMNL server |
89
+ | `trmnlp push` | Upload latest plugin settings to TRMNL server |
90
+ | `trmnlp version` | Show version |
91
+
92
+ `trmnlp lint` exits non-zero when it finds issues, so you can gate CI on it. Run `trmnlp help` for all flags.
93
+
55
94
  ## Authentication
56
95
 
57
96
  The `trmnlp login` command saves your API key to `~/.config/trmnlp/config.yml`.
@@ -64,11 +103,13 @@ The `bin/trmnlp` script is provided as a convenience. It will use the local Ruby
64
103
 
65
104
  You can modify the `bin/trmnlp` script to set up environment variables (plugin secrets, etc.) before running the server.
66
105
 
106
+ **Gem or Docker?** Install the gem if you already have Ruby >= 3.4 — it has the fastest startup. Use Docker for zero local setup.
107
+
67
108
  ### Installing via RubyGems
68
109
 
69
110
  Prerequisites:
70
111
 
71
- - Ruby 3.x
112
+ - Ruby >= 3.4
72
113
  - For PNG rendering (optional):
73
114
  - Firefox
74
115
  - ImageMagick
@@ -87,6 +128,61 @@ docker run \
87
128
  trmnl/trmnlp serve
88
129
  ```
89
130
 
131
+ Inside a container, `serve` binds to `0.0.0.0` automatically (it detects `/.dockerenv`) so the preview is reachable from your host browser. Outside Docker it binds to `127.0.0.1`.
132
+
133
+ Swap `serve` for any other command (`lint`, `login`, `clone`, etc.) to run it in a one-off container.
134
+
135
+ #### Interactive Mode
136
+
137
+ For running multiple commands (login, clone, serve), you can start an interactive shell:
138
+
139
+ ```sh
140
+ docker run -it \
141
+ --publish 4567:4567 \
142
+ --volume "$HOME/.config/trmnlp:/root/.config/trmnlp" \
143
+ --volume "$(pwd):/plugin" \
144
+ --entrypoint /bin/bash \
145
+ trmnl/trmnlp
146
+ ```
147
+
148
+ Then run commands inside the container:
149
+
150
+ ```sh
151
+ trmnlp login
152
+ trmnlp clone my_plugin 12345
153
+ cd my_plugin
154
+ trmnlp serve
155
+ ```
156
+
157
+ The config volume (`$HOME/.config/trmnlp`) persists your API key between sessions.
158
+
159
+ #### Docker Compose
160
+
161
+ For a checked-in config — like [`examples/hn-stories/`](examples/hn-stories/) uses — a minimal `docker-compose.yml`:
162
+
163
+ ```yaml
164
+ services:
165
+ trmnlp:
166
+ image: trmnl/trmnlp
167
+ command: ["serve"]
168
+ ports:
169
+ - "4567:4567"
170
+ volumes:
171
+ - .:/plugin
172
+ ```
173
+
174
+ Then `docker compose up`.
175
+
176
+ #### Building Locally
177
+
178
+ To build the Docker image from source:
179
+
180
+ ```sh
181
+ git clone https://github.com/usetrmnl/trmnlp.git
182
+ cd trmnlp
183
+ docker build -t trmnlp .
184
+ ```
185
+
90
186
  ## `.trmnlp.yml` Reference - Project Config
91
187
 
92
188
  The `.trmnlp.yml` file lives in the root of the plugin project, and is for configuring the local dev server.
@@ -110,6 +206,18 @@ custom_fields:
110
206
  # Time zone IANA identifier to inject into trmnl.user; see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
111
207
  time_zone: America/New_York
112
208
 
209
+ # Serverless transforms run automatically when a src/transform.*
210
+ # file is present. Set to 'disabled' to turn off.
211
+ transform_runtime: enabled
212
+
213
+ # Optional remote transform daemon URL — when set, transforms POST
214
+ # here instead of running locally. Useful for production-fidelity
215
+ # testing against a real microVM daemon.
216
+ # serverless_daemon_url: https://transforms.your-team.example
217
+
218
+ # Optional explicit language for src/transform.* (otherwise inferred from extension)
219
+ # serverless_language: python
220
+
113
221
  # override variables
114
222
  variables:
115
223
  trmnl:
@@ -120,13 +228,80 @@ variables:
120
228
 
121
229
  ```
122
230
 
231
+ ## Serverless Transforms
232
+
233
+ `trmnlp` can run a transform script (`python`, `ruby`, `php`, or `node`) against the polled API response before handing data to your Liquid templates — matching the hosted plugin service's behavior.
234
+
235
+ Drop a file at `src/transform.{py,rb,php,js}` and define a `run(input)` function — transforms are enabled by default, so it runs automatically. To turn them off, set `transform_runtime: disabled` in `.trmnlp.yml`.
236
+
237
+ > **Heads up:** because transforms run by default, a plugin you `clone` or `pull` from somewhere else will execute its `src/transform.*` code on your machine the first time you preview it — there is no opt-in prompt. Review a third-party plugin's transform script before serving it, or set `transform_runtime: disabled`.
238
+
239
+ The transform receives the polled response on stdin as JSON; whatever `run(input)` returns becomes the new merge data.
240
+
241
+ Example `src/transform.py`:
242
+
243
+ ```python
244
+ def run(input):
245
+ return {"items": [x["title"] for x in input["data"]]}
246
+ ```
247
+
248
+ The trmnlp image bundles `python3`, `node`, `php`, and `ruby` — no sidecar daemon required.
249
+
250
+ ### Language detection
251
+
252
+ The transform language comes from the **file extension**:
253
+
254
+ | File | Language |
255
+ |-------------------|----------|
256
+ | `src/transform.py` | `python` |
257
+ | `src/transform.rb` | `ruby` |
258
+ | `src/transform.js` | `node` |
259
+ | `src/transform.php` | `php` |
260
+
261
+ `trmnlp push` uploads the file under its own name, and the hosted service records `serverless_language` from the extension automatically — you don't need to set it by hand. `trmnlp pull` / `trmnlp clone` bring the transform file back under the same name.
262
+
263
+ ### Pointing at a remote daemon
264
+
265
+ For production-fidelity testing against a real microVM daemon, set `serverless_daemon_url:` in `.trmnlp.yml`:
266
+
267
+ ```yaml
268
+ transform_runtime: enabled
269
+ serverless_daemon_url: https://transforms.your-team.example
270
+ ```
271
+
272
+ Provide the daemon's bearer token via `$TRMNL_SERVERLESS_DAEMON_API_KEY` (env-first, mirroring how `$TRMNL_API_KEY` works for trmnl.com auth):
273
+
274
+ ```sh
275
+ export TRMNL_SERVERLESS_DAEMON_API_KEY=...
276
+ trmnlp serve
277
+ ```
278
+
279
+ Or commit a per-project value to `.trmnlp.yml` as `serverless_daemon_api_key:` — though the env var is preferred to keep the secret out of version control.
280
+
281
+ A complete worked example lives at [`examples/hn-stories/`](examples/hn-stories/) — a polling plugin that fetches the Hacker News top-stories list, enriches each story via additional HTTPS calls from inside the transform, and renders the result with TRMNL design-system markup across all four sizes. `cd examples/hn-stories && docker compose up` and you're running it.
282
+
123
283
  ## `src/settings.yml` Reference (Plugin Config)
124
284
 
125
- The `settings.yml` file is part of the plugin definition.
285
+ The `settings.yml` file is part of the plugin definition, and is uploaded and downloaded by `trmnlp push` / `pull`.
286
+
287
+ `framework_version:` pins the [TRMNL Design System](https://trmnl.com/framework) version this plugin renders against — `latest` (the default) tracks the newest release, or set a specific version for reproducibility. It lives here rather than in `.trmnlp.yml` so the value round-trips with the hosted plugin service.
126
288
 
127
289
  See [TRMNL documentation](https://help.trmnl.com/en/articles/10542599-importing-and-exporting-private-plugins#h_581fb988f0) for details on this file's contents.
128
290
 
129
291
 
292
+ ## Development
293
+
294
+ To run trmnlp from a checkout of this repo — handy for trying unreleased changes or contributing:
295
+
296
+ ```sh
297
+ git clone https://github.com/usetrmnl/trmnlp.git
298
+ cd trmnlp
299
+ bundle install
300
+ bundle exec bin/trmnlp serve
301
+ ```
302
+
303
+ The repo pins its Ruby version in `.ruby-version` — a version manager will pick it up when you `cd` in. This `bin/trmnlp` runs the CLI straight from `lib/`; it's a different script from the gem-or-Docker `bin/trmnlp` that `trmnlp init` scaffolds into a plugin project.
304
+
130
305
  ## Tests
131
306
 
132
307
  To test, run:
data/bin/rake CHANGED
@@ -8,12 +8,12 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
11
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
12
12
 
13
- bundle_binstub = File.expand_path("bundle", __dir__)
13
+ bundle_binstub = File.expand_path('bundle', __dir__)
14
14
 
15
15
  if File.file?(bundle_binstub)
16
- if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
16
+ if File.read(bundle_binstub, 300).include?('This file was generated by Bundler')
17
17
  load(bundle_binstub)
18
18
  else
19
19
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -21,7 +21,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
21
21
  end
22
22
  end
23
23
 
24
- require "rubygems"
25
- require "bundler/setup"
24
+ require 'rubygems'
25
+ require 'bundler/setup'
26
26
 
27
- load Gem.bin_path("rake", "rake")
27
+ load Gem.bin_path('rake', 'rake')
data/bin/trmnlp CHANGED
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'thor'
4
5
 
@@ -0,0 +1,24 @@
1
+ # Form-field schema vendored from the TRMNL hosted service.
2
+ #
3
+ # Unlike framework_versions.yml there is no upstream YAML to sync from —
4
+ # the canonical schema lives in the hosted service's Ruby code. Refresh
5
+ # this file by hand when that schema changes.
6
+ #
7
+ # author_bio is included although the hosted service validates it on a
8
+ # separate branch (so it is absent from the hosted FIELDS allowlist) —
9
+ # it is still a real field type a plugin may declare.
10
+ required_keys:
11
+ - keyname
12
+ - field_type
13
+ - name
14
+ field_types:
15
+ - text
16
+ - string
17
+ - number
18
+ - password
19
+ - boolean
20
+ - xhrString
21
+ - xhrSelect
22
+ - xhrFunction
23
+ - select
24
+ - author_bio
@@ -0,0 +1,72 @@
1
+ # Mirrored from the TRMNL design-system source.
2
+ # Refresh with `rake framework:sync` — do not edit manually.
3
+ latest: 3.1.1
4
+ versions:
5
+ - number: 0.0.1
6
+ released_at: '2024-06-22'
7
+ - number: 0.0.2
8
+ released_at: '2024-06-23'
9
+ - number: 0.0.3
10
+ released_at: '2024-07-09'
11
+ - number: 0.0.4
12
+ released_at: '2024-07-11'
13
+ - number: 0.0.5
14
+ released_at: '2024-07-14'
15
+ - number: 0.0.6
16
+ released_at: '2024-08-18'
17
+ - number: 0.0.7
18
+ released_at: '2024-09-19'
19
+ - number: 1.0.0
20
+ released_at: '2024-11-03'
21
+ - number: 1.0.1
22
+ released_at: '2025-01-21'
23
+ - number: 1.0.2
24
+ released_at: '2025-02-04'
25
+ - number: 1.0.3
26
+ released_at: '2025-02-06'
27
+ - number: 1.0.4
28
+ released_at: '2025-02-24'
29
+ - number: 1.0.5
30
+ released_at: '2025-04-23'
31
+ - number: 1.1.0
32
+ released_at: '2025-05-19'
33
+ - number: 1.1.1
34
+ released_at: '2025-05-20'
35
+ - number: 1.2.0
36
+ released_at: '2025-07-28'
37
+ - number: 2.0.0
38
+ released_at: '2025-09-14'
39
+ - number: 2.0.1
40
+ released_at: '2025-11-10'
41
+ - number: 2.1.0
42
+ released_at: '2025-12-30'
43
+ - number: 2.2.0
44
+ released_at: '2026-01-13'
45
+ - number: 2.2.1
46
+ released_at: '2026-01-16'
47
+ - number: 2.3.0
48
+ released_at: '2026-02-07'
49
+ - number: 2.3.1
50
+ released_at: '2026-02-09'
51
+ - number: 2.3.2
52
+ released_at: '2026-02-26'
53
+ - number: 2.3.3
54
+ released_at: '2026-03-02'
55
+ - number: 2.3.7
56
+ released_at: '2026-03-20'
57
+ - number: 3.0.0
58
+ released_at: '2026-04-01'
59
+ - number: 3.0.1
60
+ released_at: '2026-04-03'
61
+ - number: 3.0.2
62
+ released_at: '2026-04-03'
63
+ - number: 3.0.3
64
+ released_at: '2026-04-07'
65
+ - number: 3.0.4
66
+ released_at: '2026-04-29'
67
+ - number: 3.0.5
68
+ released_at: '2026-05-01'
69
+ - number: 3.1.0
70
+ released_at: '2026-04-29'
71
+ - number: 3.1.1
72
+ released_at: '2026-05-01'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'faraday'
2
4
  require 'faraday/multipart'
3
5
 
@@ -9,20 +11,36 @@ module TRMNLP
9
11
  @config = config
10
12
  end
11
13
 
14
+ def get_me
15
+ response = conn.get('me')
16
+
17
+ raise Error, "failed to fetch user info: #{response.status} #{response.body}" unless response.status == 200
18
+
19
+ JSON.parse(response.body)['data']
20
+ end
21
+
22
+ def get_plugin_settings
23
+ response = conn.get('plugin_settings')
24
+
25
+ raise Error, "failed to list plugin settings: #{response.status} #{response.body}" unless response.status == 200
26
+
27
+ JSON.parse(response.body)['data']
28
+ end
29
+
12
30
  def get_plugin_setting_archive(id)
13
31
  response = conn.get("plugin_settings/#{id}/archive")
14
32
 
15
- if response.status == 200
16
- temp_file = Tempfile.new(["plugin_settings_#{id}", '.zip'])
17
- temp_file.binmode
18
- temp_file.write(response.body)
19
- temp_file.rewind
20
-
21
- # return the temp file IO
22
- temp_file
23
- else
33
+ unless response.status == 200
24
34
  raise Error, "failed to download plugin settings archive: #{response.status} #{response.body}"
25
35
  end
36
+
37
+ temp_file = Tempfile.new(["plugin_settings_#{id}", '.zip'])
38
+ temp_file.binmode
39
+ temp_file.write(response.body)
40
+ temp_file.rewind
41
+
42
+ # return the temp file IO
43
+ temp_file
26
44
  end
27
45
 
28
46
  def post_plugin_setting_archive(id, path)
@@ -36,37 +54,33 @@ module TRMNLP
36
54
 
37
55
  filepart.close
38
56
 
39
- if response.status == 200
40
- JSON.parse(response.body)
41
- else
57
+ unless response.status == 200
42
58
  raise Error, "failed to upload plugin settings archive: #{response.status} #{response.body}"
43
59
  end
60
+
61
+ JSON.parse(response.body)
44
62
  end
45
63
 
46
64
  def post_plugin_setting(params)
47
- response = conn.post("plugin_settings", params.to_json, content_type: 'application/json')
65
+ response = conn.post('plugin_settings', params.to_json, content_type: 'application/json')
48
66
 
49
- if response.status == 200
50
- JSON.parse(response.body)
51
- else
52
- raise Error, "failed to create plugin setting: #{response.status} #{response.body}"
53
- end
67
+ raise Error, "failed to create plugin setting: #{response.status} #{response.body}" unless response.status == 200
68
+
69
+ JSON.parse(response.body)
54
70
  end
55
71
 
56
72
  def delete_plugin_setting(id)
57
73
  response = conn.delete("plugin_settings/#{id}")
58
74
 
59
- if response.status == 204
60
- true
61
- else
62
- raise Error, "failed to delete plugin setting: #{response.status} #{response.body}"
63
- end
75
+ raise Error, "failed to delete plugin setting: #{response.status} #{response.body}" unless response.status == 204
76
+
77
+ true
64
78
  end
65
79
 
66
80
  private
67
-
81
+
68
82
  attr_reader :config
69
-
83
+
70
84
  def api_uri = config.app.api_uri
71
85
 
72
86
  def conn
@@ -75,12 +89,11 @@ module TRMNLP
75
89
  end
76
90
  end
77
91
 
78
-
79
92
  def headers
80
93
  {
81
94
  'Authorization' => "Bearer #{config.app.api_key}",
82
- 'User-Agent' => "trmnlp/#{VERSION}",
95
+ 'User-Agent' => "trmnlp/#{VERSION}"
83
96
  }
84
97
  end
85
98
  end
86
- end
99
+ end