inlay 0.2.0 → 0.2.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 +12 -5
- data/LICENSE.txt +1 -1
- data/README.md +19 -11
- data/docs/index.html +28 -0
- data/docs/manual-checks.md +13 -0
- data/docs/spikes.md +5 -5
- data/lib/inlay/irb.rb +4 -1
- data/lib/inlay/iruby.rb +93 -31
- data/lib/inlay/sizer.rb +16 -4
- data/lib/inlay/terminal_output.rb +8 -6
- data/lib/inlay/version.rb +1 -1
- data/sig/inlay.rbs +9 -1
- metadata +35 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6273a1bf011d3c9cd941d30b87f50d5724eb203c00a06697c4a9247c992274ff
|
|
4
|
+
data.tar.gz: bdc9b71941f4742ba3b7ef41f2a9c597d4c60d1be5ebe84dbc918ea6561894d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1eceb81681746d2fcb025f69c78280be9eefbf8213aed812b398e37466dc9150afb603ef53c74d52e5885519603fb21d39103cd2e3722b5e67e7fa7d3e16cccf
|
|
7
|
+
data.tar.gz: 3765bcfe8b45a28d015f7339a5106ff52bc950667332d3bfd9e0c86b5d46eb076c4fd745d49d045008c33c37ba81aae39b54bcb156d61eb1cd589711e730beb8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.2.
|
|
3
|
+
## [0.2.1] - Unreleased
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
- Fit terminal images to protocol-specific cell dimensions.
|
|
6
|
+
- Preserve SVG chart output when optional PNG dependencies are unavailable.
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-09-25
|
|
9
|
+
|
|
10
|
+
- Add terminal image output, `Inlay.show`, normalization, and custom adapters.
|
|
11
|
+
- Integrate image display with IRB, Pry, and IRuby, including SVG, PNG, and animated GIF notebook output.
|
|
12
|
+
|
|
13
|
+
## [0.1.0] - 2026-09-25
|
|
14
|
+
|
|
15
|
+
Initial release
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Inlay
|
|
2
2
|
|
|
3
|
-
Render Ruby graphics where you inspect them. Inlay shows Tessel images, RBGL surfaces, and chart output in
|
|
3
|
+
Render Ruby graphics where you inspect them. Inlay shows Tessel images, RBGL surfaces, and chart output in terminals, IRB, and Pry.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Render Ruby graphics where you inspect them. Inlay shows Tessel images, RBGL sur
|
|
|
8
8
|
gem install inlay
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Add `inlay` to your Gemfile. Inlay uses [Tessel](https://github.com/rbgfx/tessel) for images and [Termvas](https://github.com/rbgfx/termvas) for terminal output.
|
|
11
|
+
Add `inlay` to your Gemfile. Inlay uses [Tessel](https://github.com/rbgfx/tessel) for images and [Termvas](https://github.com/rbgfx/termvas) for terminal output.
|
|
12
12
|
|
|
13
13
|
## Use in IRB
|
|
14
14
|
|
|
@@ -31,7 +31,21 @@ Pry uses the same integration style:
|
|
|
31
31
|
require "inlay/pry"
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
## Use in IRuby / Jupyter
|
|
35
|
+
|
|
36
|
+
Install [IRuby](https://github.com/SciRuby/iruby) 0.8 or newer, then require Inlay after the optional graphics gems you use:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
require "inkplot" # optional: charts
|
|
40
|
+
require "inlay/iruby"
|
|
41
|
+
|
|
42
|
+
Tessel.read("brick.png")
|
|
43
|
+
Inkplot.line(prices, x: :date, y: :close) # when Inkplot is installed
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
IRuby provides PNG for images and both SVG and PNG for charts, with SVG listed first. Animations use an installed Flipbook to produce a GIF data URI; if Flipbook is unavailable, Inlay displays the first frame as PNG. These integrations are opt-in and do not load IRuby, Inkplot, or Flipbook when you only `require "inlay"`.
|
|
47
|
+
|
|
48
|
+
Inlay uses the terminal protocol selected by Termvas: Kitty, iTerm2, Sixel, or true-color half blocks. Image limits use terminal columns and rows; pixel protocols use Termvas cell-size estimates (8 × 16 by default, configurable with `TERMVAS_CELL_WIDTH` and `TERMVAS_CELL_HEIGHT`). Piped output, `TERM=dumb`, `INLAY=off`, and `NO_COLOR` with half blocks show only the summary.
|
|
35
49
|
|
|
36
50
|
## Use in a script
|
|
37
51
|
|
|
@@ -51,14 +65,6 @@ Objects can implement `to_inlay` and return a `Tessel::Image`, `{ png: bytes }`,
|
|
|
51
65
|
Inlay.register(MySurface) { |surface| surface.to_tessel_image }
|
|
52
66
|
```
|
|
53
67
|
|
|
54
|
-
## Use in IRuby
|
|
55
|
-
|
|
56
|
-
```ruby
|
|
57
|
-
require "inlay/iruby"
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Images are returned as PNG, SVG output as SVG, and animations as an embedded GIF when Flipbook is installed. Without Flipbook, an animation displays its first frame.
|
|
61
|
-
|
|
62
68
|
## Configuration
|
|
63
69
|
|
|
64
70
|
`Inlay.config.enabled` toggles terminal rendering. `max_rows` caps terminal height and `max_pixels` limits decoded and displayed input size. Small images (up to 32×32) are enlarged with nearest-neighbor scaling; larger images are reduced with alpha-aware area averaging. Set `INLAY=off` to disable terminal output for a process.
|
|
@@ -70,6 +76,8 @@ bundle install
|
|
|
70
76
|
bundle exec rake verify
|
|
71
77
|
```
|
|
72
78
|
|
|
79
|
+
Run `bundle exec rbs -I sig validate` to check the published signatures. The manual terminal and notebook checklist is in [docs/manual-checks.md](docs/manual-checks.md).
|
|
80
|
+
|
|
73
81
|
## License
|
|
74
82
|
|
|
75
83
|
MIT
|
data/docs/index.html
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="theme-color" content="#101114">
|
|
7
|
+
<meta name="description" content="Render Ruby graphics where you inspect them with Inlay.">
|
|
8
|
+
<title>Inlay — graphics where you work</title>
|
|
9
|
+
<style>
|
|
10
|
+
:root{color-scheme:dark;--bg:#101114;--panel:#191b20;--line:#2b2e35;--text:#f1f0ec;--muted:#a3a5ad;--accent:#f0a66e;--mono:ui-monospace,SFMono-Regular,Menlo,monospace;--sans:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
|
|
11
|
+
*{box-sizing:border-box}body{margin:0;background:radial-gradient(ellipse at 86% 0%,#3a2b21,transparent 38%),var(--bg);color:var(--text);font:16px/1.65 var(--sans)}a{color:inherit;text-decoration:none}a:hover{color:var(--accent)}.wrap{width:min(1020px,calc(100% - 40px));margin:auto}.nav{height:76px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line)}.brand{font-size:20px;font-weight:750;letter-spacing:-.04em}.brand i,.label{font-style:normal;color:var(--accent)}.navlinks{display:flex;gap:24px;color:var(--muted);font-size:14px}.hero{padding:94px 0 72px;max-width:780px}.label{font:12px var(--mono);text-transform:uppercase;letter-spacing:.14em}h1{font-size:clamp(48px,8vw,82px);line-height:1;letter-spacing:-.075em;margin:20px 0}.hero p{font-size:19px;color:var(--muted);max-width:620px}.actions{display:flex;gap:12px;flex-wrap:wrap;margin:28px 0}.button{padding:10px 16px;border:1px solid var(--line);border-radius:5px;font-weight:650}.primary{color:#21160f;background:var(--accent);border-color:var(--accent)}.install{display:inline-flex;gap:16px;background:#090a0c;border:1px solid var(--line);border-radius:6px;padding:12px 15px;font:14px var(--mono)}.install span{color:#777}.section{padding:42px 0 64px;border-top:1px solid var(--line)}.section h2{font-size:28px;letter-spacing:-.045em;margin:8px 0 10px}.section>p{color:var(--muted);max-width:640px}.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:25px}.card{padding:20px;background:var(--panel);border:1px solid var(--line);border-radius:7px}.num{font:12px var(--mono);color:var(--accent)}.card h3{font-size:17px;margin:16px 0 6px}.card p{font-size:14px;color:var(--muted);margin:0}.code{background:#090a0c;border:1px solid var(--line);border-radius:7px;padding:18px;overflow:auto;font:14px/1.8 var(--mono);color:#e7d5c7}.foot{padding:24px 0 36px;border-top:1px solid var(--line);color:var(--muted);font-size:13px;display:flex;justify-content:space-between;gap:16px}
|
|
12
|
+
@media(max-width:700px){.hero{padding:68px 0 54px}.navlinks{gap:14px}.cards{grid-template-columns:1fr}.foot{flex-direction:column}}
|
|
13
|
+
</style>
|
|
14
|
+
</head>
|
|
15
|
+
<body><div class="wrap">
|
|
16
|
+
<nav class="nav" aria-label="Main navigation"><a class="brand" href="#top">inlay<i>.</i></a><div class="navlinks"><a href="#integrations">Integrations</a><a href="https://github.com/rbgfx/inlay">GitHub</a><a href="https://rubygems.org/gems/inlay">RubyGems</a></div></nav>
|
|
17
|
+
<main id="top">
|
|
18
|
+
<section class="hero"><div class="label">Ruby graphics, inline</div><h1>See what your<br>code makes.</h1><p>Inlay renders images, surfaces, charts, and animations where you inspect them: in IRB, Pry, Jupyter, or a supported terminal.</p><div class="actions"><a class="button primary" href="https://github.com/rbgfx/inlay#use-in-irb">Get started ↗</a><a class="button" href="https://github.com/rbgfx/inlay/blob/main/docs/manual-checks.md">Compatibility notes</a></div><div class="install"><span>$</span> gem install inlay</div></section>
|
|
19
|
+
<section class="section" id="integrations"><div class="label">One small adapter</div><h2>Fits the way you already work</h2><p>Install the optional adapter for your environment. Inlay keeps notebook and terminal integrations opt-in, so a basic install stays small.</p><div class="cards"><article class="card"><div class="num">01 / REPL</div><h3>IRB & Pry</h3><p>Return an image at the prompt and see it inline, with a short summary ready for the next command.</p></article><article class="card"><div class="num">02 / NOTEBOOK</div><h3>IRuby & Jupyter</h3><p>Display PNG images, SVG or PNG charts, and animated GIF output with a first-frame fallback.</p></article><article class="card"><div class="num">03 / TERMINAL</div><h3>Kitty, iTerm2 & Sixel</h3><p>Choose the terminal protocol through Termvas, with true-color blocks as a portable fallback.</p></article></div></section>
|
|
20
|
+
<section class="section"><div class="label">In IRB</div><h2>Require it once</h2><pre class="code"><code># ~/.irbrc
|
|
21
|
+
require "inlay/irb"
|
|
22
|
+
|
|
23
|
+
# Then return an image from any prompt
|
|
24
|
+
image = Tessel.read("brick.png").scale_nearest(64, 64)
|
|
25
|
+
image</code></pre></section>
|
|
26
|
+
</main><footer class="foot"><span>Inlay · Render Ruby graphics where you inspect them</span><span><a href="https://github.com/rbgfx/inlay">Source</a> · MIT License</span></footer>
|
|
27
|
+
</div></body>
|
|
28
|
+
</html>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Manual integration checks
|
|
2
|
+
|
|
3
|
+
Run these checks with a real terminal or JupyterLab session before marking them complete. Automated specs cover the IRB PTY path and IRuby's display registry, but cannot confirm each frontend's rendering.
|
|
4
|
+
|
|
5
|
+
| Frontend | Check | Status |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| Kitty | Image appears inline and the next prompt is intact | Not run |
|
|
8
|
+
| WezTerm | Image appears inline and the next prompt is intact | Not run |
|
|
9
|
+
| iTerm2 | Image appears inline and the next prompt is intact | Not run |
|
|
10
|
+
| Ghostty | Image appears inline and the next prompt is intact | Not run |
|
|
11
|
+
| VS Code integrated terminal | Image appears inline with image support enabled | Not run |
|
|
12
|
+
| tmux | Image appears inline and the next prompt is intact | Not run |
|
|
13
|
+
| JupyterLab | Image, chart SVG, and animation/fallback render in cells | Not run |
|
data/docs/spikes.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Integration spikes
|
|
2
2
|
|
|
3
|
-
- IRB 1.18
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
- Pry's configured printer receives `(output, value, pry_instance)`; the adapter retains that printer and passes it the summary.
|
|
7
|
-
- IRuby's
|
|
3
|
+
- Verified with IRB 1.18 in a PTY: Inlay's inspector writes the Kitty image sequence directly and leaves the one-line summary in IRB's result buffer. Both an assignment and a later expression display correctly, and the following prompt remains usable. The supported minimum is IRB 1.13, which provides the inspector API used here.
|
|
4
|
+
- Terminal protocol selection uses Termvas environment detection during `require "inlay/irb"`; Inlay does not send device queries. The PTY test forces IRB's pager through `tee` for a long value, then checks the following Kitty image sequence and prompt remain intact. Real pager navigation and terminal rendering still need frontend checks.
|
|
5
|
+
- Verified with a temporary Rails 8.1.4 console and project `.irbrc` loading `inlay/irb`: ordinary values and Tessel image assignments keep the prompt usable. This caught and fixed the wrapped Inspector's missing initializer call (`IRB::ColorPrinter` on pretty-print output).
|
|
6
|
+
- Pry's configured printer receives `(output, value, pry_instance)`; the adapter retains that printer and passes it the summary. The configured printer path is covered by a spec.
|
|
7
|
+
- IRuby 0.8.3's display registry supports predicate matchers and MIME-specific renderers. Registry specs verify PNG, SVG, and HTML GIF output; a local integration check against Flipbook also produced a GIF data URI. JupyterLab frontend rendering has not been manually checked.
|
|
8
8
|
- `@ruby/3.4-wasm-wasi` 2.10.1 / Ruby 3.4.1 with Larb and JS host bindings completed 3,600 simulated 60 fps callbacks in 61.95 seconds. With one stable JS trampoline and Ruby/JS GC every 600 frames, WASM memory changed from 71,499,776 to 71,565,312 bytes; JS heap grew 4,261,280 bytes. This validates the callback strategy under Node, not a browser or GPU workload.
|
data/lib/inlay/irb.rb
CHANGED
|
@@ -14,7 +14,10 @@ module Inlay
|
|
|
14
14
|
selected = mode.is_a?(IRB::Inspector) ? mode : IRB::Inspector::INSPECTORS[mode] || IRB::Inspector::INSPECTORS[mode.to_s] || IRB::Inspector::INSPECTORS[:p]
|
|
15
15
|
return false unless selected
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
unless mode.equal?(@wrapper)
|
|
18
|
+
@original = selected
|
|
19
|
+
@original.init
|
|
20
|
+
end
|
|
18
21
|
@protocol = TerminalOutput.protocol
|
|
19
22
|
@wrapper = IRB::Inspector.new(proc do |value, output, colorize: true|
|
|
20
23
|
begin
|
data/lib/inlay/iruby.rb
CHANGED
|
@@ -1,53 +1,115 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "base64"
|
|
3
4
|
require "inlay"
|
|
4
|
-
require "mime/types"
|
|
5
5
|
require "iruby/display"
|
|
6
|
-
require "
|
|
7
|
-
require "tempfile"
|
|
8
|
-
begin
|
|
9
|
-
require "flipbook"
|
|
10
|
-
rescue LoadError
|
|
11
|
-
# Animation falls back to its first frame when Flipbook is unavailable.
|
|
12
|
-
end
|
|
6
|
+
require "mime/types"
|
|
13
7
|
|
|
14
8
|
module Inlay
|
|
15
9
|
module IRubyIntegration
|
|
16
10
|
module_function
|
|
17
11
|
|
|
12
|
+
module MimeBundle
|
|
13
|
+
def to_iruby_mimebundle(include: [])
|
|
14
|
+
return super if defined?(super)
|
|
15
|
+
|
|
16
|
+
Inlay::IRubyIntegration.mimebundle(self, include: include)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
18
20
|
def install
|
|
19
|
-
|
|
21
|
+
unless @registered
|
|
22
|
+
registry = IRuby::Display::Registry
|
|
23
|
+
registry.match { |object| Inlay.displayable?(object) }
|
|
24
|
+
registry.format("text/html") { |object| html(object) }
|
|
25
|
+
registry.format("image/svg+xml") { |object| svg(object) }
|
|
26
|
+
registry.format("image/png") { |object| png(object) }
|
|
27
|
+
@registered = true
|
|
28
|
+
end
|
|
29
|
+
install_mimebundle
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def html(object)
|
|
34
|
+
animation_html(Inlay.normalize(object))
|
|
35
|
+
rescue StandardError
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
20
38
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@installed = true
|
|
39
|
+
def svg(object)
|
|
40
|
+
Inlay.normalize(object).svg
|
|
41
|
+
rescue StandardError
|
|
42
|
+
nil
|
|
26
43
|
end
|
|
27
44
|
|
|
28
|
-
def
|
|
45
|
+
def png(object)
|
|
46
|
+
png_content(Inlay.normalize(object))
|
|
47
|
+
rescue LoadError, StandardError
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def mimebundle(object, include: [])
|
|
29
52
|
normalized = Inlay.normalize(object)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
53
|
+
formats = {}
|
|
54
|
+
formats["text/html"] = animation_html(normalized) if normalized.frames
|
|
55
|
+
formats["image/svg+xml"] = normalized.svg if normalized.svg
|
|
56
|
+
png_bytes = png_content(normalized)
|
|
57
|
+
formats["image/png"] = Base64.strict_encode64(png_bytes) if png_bytes
|
|
58
|
+
formats.select! { |mime, _| include.empty? || include.include?(mime) }
|
|
59
|
+
[formats, {}]
|
|
60
|
+
rescue StandardError
|
|
61
|
+
[{}, {}]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def install_mimebundle
|
|
65
|
+
classes = [Tessel::Image]
|
|
66
|
+
classes << RBGL::Engine::Framebuffer if defined?(RBGL::Engine::Framebuffer)
|
|
67
|
+
classes << RBGL::Engine::Texture if defined?(RBGL::Engine::Texture)
|
|
68
|
+
classes << Inkplot::Chart if defined?(Inkplot::Chart)
|
|
69
|
+
classes.uniq.each do |klass|
|
|
70
|
+
klass.prepend(MimeBundle) unless klass.ancestors.include?(MimeBundle)
|
|
38
71
|
end
|
|
39
72
|
end
|
|
73
|
+
private_class_method :install_mimebundle
|
|
74
|
+
|
|
75
|
+
def animation_html(normalized)
|
|
76
|
+
return unless normalized.frames
|
|
77
|
+
|
|
78
|
+
writer = flipbook_writer
|
|
79
|
+
return unless writer
|
|
40
80
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
["text/html", "<img alt=\"animation\" src=\"data:image/gif;base64,#{Base64.strict_encode64(File.binread(file.path))}\">"]
|
|
46
|
-
end
|
|
47
|
-
else
|
|
48
|
-
["image/png", Tessel::PNG.encode(frames.first)]
|
|
81
|
+
require "tempfile"
|
|
82
|
+
gif = Tempfile.create(["inlay-", ".gif"]) do |file|
|
|
83
|
+
writer.write(file.path, normalized.frames, fps: normalized.fps)
|
|
84
|
+
File.binread(file.path)
|
|
49
85
|
end
|
|
86
|
+
"<img alt=\"Animation with #{normalized.frames.length} frames\" src=\"data:image/gif;base64,#{Base64.strict_encode64(gif)}\">"
|
|
87
|
+
rescue LoadError, StandardError
|
|
88
|
+
nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def png_content(normalized)
|
|
92
|
+
image = normalized.image || normalized.frames&.first
|
|
93
|
+
return Tessel::PNG.encode(image) if image
|
|
94
|
+
|
|
95
|
+
value = normalized.png
|
|
96
|
+
value = value.call if value.respond_to?(:call)
|
|
97
|
+
return value if value.is_a?(String)
|
|
98
|
+
return Tessel::PNG.encode(value) if value.is_a?(Tessel::Image)
|
|
99
|
+
rescue LoadError, StandardError
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
private_class_method :animation_html, :png_content
|
|
103
|
+
|
|
104
|
+
def flipbook_writer
|
|
105
|
+
return Flipbook if defined?(Flipbook) && Flipbook.respond_to?(:write)
|
|
106
|
+
|
|
107
|
+
require "flipbook"
|
|
108
|
+
Flipbook if defined?(Flipbook) && Flipbook.respond_to?(:write)
|
|
109
|
+
rescue LoadError => error
|
|
110
|
+
raise unless error.path == "flipbook"
|
|
50
111
|
end
|
|
112
|
+
private_class_method :flipbook_writer
|
|
51
113
|
end
|
|
52
114
|
end
|
|
53
115
|
|
data/lib/inlay/sizer.rb
CHANGED
|
@@ -4,14 +4,26 @@ module Inlay
|
|
|
4
4
|
module Sizer
|
|
5
5
|
module_function
|
|
6
6
|
|
|
7
|
-
def fit(image, width: nil, height: nil, scale: :auto, columns: nil, rows: nil)
|
|
7
|
+
def fit(image, width: nil, height: nil, scale: :auto, columns: nil, rows: nil, protocol: :blocks, env: ENV)
|
|
8
8
|
raise TypeError, "expected a Tessel::Image" unless image.is_a?(Tessel::Image)
|
|
9
9
|
raise ArgumentError, "unknown scale mode: #{scale}" unless %i[auto fit pixel none].include?(scale)
|
|
10
10
|
return image if image.width.zero? || image.height.zero?
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
terminal = Termvas::Terminal.new(alt_screen: false, env: env)
|
|
13
|
+
terminal_columns, terminal_rows = terminal.size
|
|
14
|
+
columns ||= terminal_columns
|
|
15
|
+
rows ||= terminal_rows
|
|
16
|
+
block_pixels = protocol.to_sym == :blocks
|
|
17
|
+
cell_width, cell_height = terminal.cell_size
|
|
18
|
+
available_width = columns * (block_pixels ? 1 : cell_width)
|
|
19
|
+
available_height = [rows, Inlay.config.max_rows].min * (block_pixels ? 2 : cell_height)
|
|
20
|
+
if available_width * available_height > Inlay.config.max_pixels
|
|
21
|
+
factor = Math.sqrt(Inlay.config.max_pixels.to_f / (available_width * available_height))
|
|
22
|
+
available_width = [(available_width * factor).floor, 1].max
|
|
23
|
+
available_height = [(available_height * factor).floor, 1].max
|
|
24
|
+
end
|
|
25
|
+
width = [Integer(width || available_width), available_width].min
|
|
26
|
+
height = [Integer(height || available_height), available_height].min
|
|
15
27
|
raise ArgumentError, "display dimensions must be positive" unless width.positive? && height.positive?
|
|
16
28
|
pixel_scale = scale == :pixel || (scale == :auto && image.width <= 32 && image.height <= 32)
|
|
17
29
|
max_factor = [width / [image.width, 1].max, height / [image.height, 1].max].min
|
|
@@ -5,10 +5,7 @@ module Inlay
|
|
|
5
5
|
module_function
|
|
6
6
|
|
|
7
7
|
def protocol(output: $stdout, env: ENV)
|
|
8
|
-
return :none unless
|
|
9
|
-
return :none unless output.tty?
|
|
10
|
-
return :none unless env["INLAY"] != "off"
|
|
11
|
-
return :none if env["TERM"] == "dumb"
|
|
8
|
+
return :none unless enabled?(output: output, env: env)
|
|
12
9
|
|
|
13
10
|
selected = Termvas::Detector.protocol(env)
|
|
14
11
|
return :none if selected == :blocks && env.key?("NO_COLOR")
|
|
@@ -18,11 +15,11 @@ module Inlay
|
|
|
18
15
|
|
|
19
16
|
def write(image, width: nil, height: nil, scale: :auto, protocol: nil, output: $stdout, env: ENV)
|
|
20
17
|
selected = (protocol || self.protocol(output: output, env: env)).to_sym
|
|
21
|
-
return false unless
|
|
18
|
+
return false unless enabled?(output: output, env: env) && selected != :none
|
|
22
19
|
return false if selected == :blocks && env.key?("NO_COLOR")
|
|
23
20
|
return false if image.width * image.height > Inlay.config.max_pixels
|
|
24
21
|
|
|
25
|
-
image = Sizer.fit(image, width: width, height: height, scale: scale)
|
|
22
|
+
image = Sizer.fit(image, width: width, height: height, scale: scale, protocol: selected, env: env)
|
|
26
23
|
bytes = image.bytes
|
|
27
24
|
encoded = case selected.to_sym
|
|
28
25
|
when :blocks then Termvas::Encoders::Blocks.encode(bytes, image.width, image.height)
|
|
@@ -38,6 +35,11 @@ module Inlay
|
|
|
38
35
|
true
|
|
39
36
|
end
|
|
40
37
|
|
|
38
|
+
def enabled?(output:, env:)
|
|
39
|
+
Inlay.config.enabled && output.tty? && env["INLAY"] != "off" && env["TERM"] != "dumb"
|
|
40
|
+
end
|
|
41
|
+
private_class_method :enabled?
|
|
42
|
+
|
|
41
43
|
def next_kitty_id
|
|
42
44
|
@kitty_id_lock ||= Mutex.new
|
|
43
45
|
@kitty_id_lock.synchronize { @kitty_id = (@kitty_id.to_i % 2_147_483_647) + 1 }
|
data/lib/inlay/version.rb
CHANGED
data/sig/inlay.rbs
CHANGED
|
@@ -17,7 +17,7 @@ module Inlay
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def self.config: () -> Config
|
|
20
|
-
def self.register: (Module klass) { (untyped object) -> untyped } ->
|
|
20
|
+
def self.register: (Module klass) { (untyped object) -> untyped } -> untyped
|
|
21
21
|
def self.normalize: (untyped object, ?allow_path: bool) -> Normalized
|
|
22
22
|
def self.displayable?: (untyped object) -> bool
|
|
23
23
|
def self.protocol: () -> Symbol
|
|
@@ -26,6 +26,14 @@ module Inlay
|
|
|
26
26
|
def self.irb_uninstall!: () -> (bool | nil)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
module Inlay::IRubyIntegration
|
|
30
|
+
def self.install: () -> bool
|
|
31
|
+
def self.html: (untyped object) -> String?
|
|
32
|
+
def self.svg: (untyped object) -> String?
|
|
33
|
+
def self.png: (untyped object) -> String?
|
|
34
|
+
def self.mimebundle: (untyped object, ?include: Array[String]) -> [Hash[String, String], Hash[untyped, untyped]]
|
|
35
|
+
end
|
|
36
|
+
|
|
29
37
|
module Kernel
|
|
30
38
|
private
|
|
31
39
|
def inlay: (untyped object, **untyped options) -> String
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inlay
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yudai Takada
|
|
@@ -36,7 +36,7 @@ dependencies:
|
|
|
36
36
|
requirements:
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.
|
|
39
|
+
version: 0.3.0
|
|
40
40
|
- - "<"
|
|
41
41
|
- !ruby/object:Gem::Version
|
|
42
42
|
version: '1'
|
|
@@ -46,7 +46,7 @@ dependencies:
|
|
|
46
46
|
requirements:
|
|
47
47
|
- - ">="
|
|
48
48
|
- !ruby/object:Gem::Version
|
|
49
|
-
version: 0.
|
|
49
|
+
version: 0.3.0
|
|
50
50
|
- - "<"
|
|
51
51
|
- !ruby/object:Gem::Version
|
|
52
52
|
version: '1'
|
|
@@ -56,14 +56,20 @@ dependencies:
|
|
|
56
56
|
requirements:
|
|
57
57
|
- - ">="
|
|
58
58
|
- !ruby/object:Gem::Version
|
|
59
|
-
version: '0'
|
|
59
|
+
version: '0.2'
|
|
60
|
+
- - "<"
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '1'
|
|
60
63
|
type: :runtime
|
|
61
64
|
prerelease: false
|
|
62
65
|
version_requirements: !ruby/object:Gem::Requirement
|
|
63
66
|
requirements:
|
|
64
67
|
- - ">="
|
|
65
68
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: '0'
|
|
69
|
+
version: '0.2'
|
|
70
|
+
- - "<"
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '1'
|
|
67
73
|
- !ruby/object:Gem::Dependency
|
|
68
74
|
name: irb
|
|
69
75
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -71,6 +77,9 @@ dependencies:
|
|
|
71
77
|
- - ">="
|
|
72
78
|
- !ruby/object:Gem::Version
|
|
73
79
|
version: '1.13'
|
|
80
|
+
- - "<"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '2'
|
|
74
83
|
type: :development
|
|
75
84
|
prerelease: false
|
|
76
85
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -78,34 +87,29 @@ dependencies:
|
|
|
78
87
|
- - ">="
|
|
79
88
|
- !ruby/object:Gem::Version
|
|
80
89
|
version: '1.13'
|
|
90
|
+
- - "<"
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: '2'
|
|
81
93
|
- !ruby/object:Gem::Dependency
|
|
82
94
|
name: iruby
|
|
83
95
|
requirement: !ruby/object:Gem::Requirement
|
|
84
96
|
requirements:
|
|
85
97
|
- - ">="
|
|
86
98
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: '0'
|
|
88
|
-
|
|
89
|
-
prerelease: false
|
|
90
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
91
|
-
requirements:
|
|
92
|
-
- - ">="
|
|
93
|
-
- !ruby/object:Gem::Version
|
|
94
|
-
version: '0'
|
|
95
|
-
- !ruby/object:Gem::Dependency
|
|
96
|
-
name: flipbook
|
|
97
|
-
requirement: !ruby/object:Gem::Requirement
|
|
98
|
-
requirements:
|
|
99
|
-
- - ">="
|
|
99
|
+
version: '0.8'
|
|
100
|
+
- - "<"
|
|
100
101
|
- !ruby/object:Gem::Version
|
|
101
|
-
version: '
|
|
102
|
+
version: '1'
|
|
102
103
|
type: :development
|
|
103
104
|
prerelease: false
|
|
104
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
105
106
|
requirements:
|
|
106
107
|
- - ">="
|
|
107
108
|
- !ruby/object:Gem::Version
|
|
108
|
-
version: '0'
|
|
109
|
+
version: '0.8'
|
|
110
|
+
- - "<"
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: '1'
|
|
109
113
|
- !ruby/object:Gem::Dependency
|
|
110
114
|
name: pry
|
|
111
115
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -113,6 +117,9 @@ dependencies:
|
|
|
113
117
|
- - ">="
|
|
114
118
|
- !ruby/object:Gem::Version
|
|
115
119
|
version: '0'
|
|
120
|
+
- - "<"
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '1'
|
|
116
123
|
type: :development
|
|
117
124
|
prerelease: false
|
|
118
125
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -120,6 +127,9 @@ dependencies:
|
|
|
120
127
|
- - ">="
|
|
121
128
|
- !ruby/object:Gem::Version
|
|
122
129
|
version: '0'
|
|
130
|
+
- - "<"
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '1'
|
|
123
133
|
- !ruby/object:Gem::Dependency
|
|
124
134
|
name: rspec
|
|
125
135
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -134,8 +144,8 @@ dependencies:
|
|
|
134
144
|
- - "~>"
|
|
135
145
|
- !ruby/object:Gem::Version
|
|
136
146
|
version: '3.0'
|
|
137
|
-
description: Render Tessel images, graphics buffers, and
|
|
138
|
-
and IRuby.
|
|
147
|
+
description: Render Tessel images, graphics buffers, charts, and animations in terminals,
|
|
148
|
+
IRB, Pry, and IRuby.
|
|
139
149
|
email:
|
|
140
150
|
- t.yudai92@gmail.com
|
|
141
151
|
executables: []
|
|
@@ -146,6 +156,8 @@ files:
|
|
|
146
156
|
- LICENSE.txt
|
|
147
157
|
- README.md
|
|
148
158
|
- Rakefile
|
|
159
|
+
- docs/index.html
|
|
160
|
+
- docs/manual-checks.md
|
|
149
161
|
- docs/spikes.md
|
|
150
162
|
- lib/inlay.rb
|
|
151
163
|
- lib/inlay/adapters.rb
|
|
@@ -184,5 +196,5 @@ requirements: []
|
|
|
184
196
|
rubygems_version: 3.4.19
|
|
185
197
|
signing_key:
|
|
186
198
|
specification_version: 4
|
|
187
|
-
summary: Display Ruby graphics in terminals and
|
|
199
|
+
summary: Display Ruby graphics in terminals and REPLs
|
|
188
200
|
test_files: []
|