Use kitty capability to display image in the terminal? #417
Replies: 31 comments 15 replies
-
Proof of concept: show_plot_in_kitty <- function(expr) {
png_file <- tempfile(fileext = ".png")
png(png_file, width = 800, height = 600)
force(expr)
dev.off()
system(sprintf("kitty +kitten icat %s", shQuote(png_file)))
}
show_plot_in_kitty(plot(pressure, col = "red"))
library(ggplot2)
p <- cars |>
ggplot(aes(x = speed, y = dist)) +
geom_point(size = 5L)
show_plot_in_kitty(print(p)) |
Beta Was this translation helpful? Give feedback.
-
Original idea from: https://rstats.me/@adamhsparks/114993016239999921 |
Beta Was this translation helpful? Give feedback.
-
It would be great to have a graphics device that saves PNG images and runs an application to display them. We could split Kitty or WezTerm window to reserve a space for the graphics. The system would be perfect if the graphics had a black background and the palette of colors were adjusted accordingly. |
Beta Was this translation helpful? Give feedback.
-
Instead of splitting the terminal, displaying the graphics mixed with text output as in your example, might be equally or even better. |
Beta Was this translation helpful? Give feedback.
-
I think we could add an option to use terminal capability to display the plot. What would be the best way?
|
Beta Was this translation helpful? Give feedback.
-
Even easier, just a hook? if (interactive() && Sys.getenv("TERM") == "xterm-kitty") {
show_plot_in_kitty <- function(expr, width = 800, height = 600, dpi = 100) {
png_file <- tempfile(fileext = ".png")
png(png_file, width = width, height = height, res = dpi)
on.exit(dev.off(), add = TRUE)
force(expr)
dev.off()
system2("kitty", c("+kitten", "icat", png_file))
}
base_plot <- base::plot
plot <- function(...) {
show_plot_in_kitty(base_plot(...))
}
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2_print <- getS3method("print", "ggplot")
assign("print.ggplot", function(x, ...) {
show_plot_in_kitty(ggplot2_print(x, ...))
}, envir = globalenv())
}
} I will make an attempt |
Beta Was this translation helpful? Give feedback.
-
I'm trying your code, and it works great! In my opinion, it's already good enough, and we don't need a graphics device. |
Beta Was this translation helpful? Give feedback.
-
For reference: here is some code I use to create plots in kitty from R: https://codeberg.org/djvanderlaan/kitty.r One of the things this adds, is that it automatically tries to scale the plot and resolution (size of of the fonts) such that it matches those in the terminal. It can also match the colours to those in the terminal. |
Beta Was this translation helpful? Give feedback.
-
@jalvesaq can you try the I have tested it with a file library(ggplot2)
plot(1)
ggplot(mtcars, aes(x = mpg, y = cyl)) +
geom_point() |
Beta Was this translation helpful? Give feedback.
-
I'll try it. I didn't have time look at @djvanderlaan's kitty.r package, but it seems to do everything that we need. |
Beta Was this translation helpful? Give feedback.
-
![]() Just tried it with a more "complex" plot. |
Beta Was this translation helpful? Give feedback.
-
@PMassicotte, it works! Good job! However, it's not the same as a graphics device. That is, it will work only for predefined functions, not, for example, for |
Beta Was this translation helpful? Give feedback.
-
library(kitty.r)
kitty_plot(hist(mtcars$qsec))
# Example from kitty.r README
kitty_plot({
data(iris)
plot(iris$Sepal.Width, iris$Sepal.Length,
col = kitty_palette()[iris$Species],
pch = 20)
grid(lty = 2)
legend("topright", legend = levels(iris$Species),
col = kitty_palette()[1:3], pch = 20)
})
# ggplot example
p <- ggplot(mtcars, aes(mpg, hp)) +
geom_point()
kitty_plot(print(p)) However, this doesn't work: kitty_plot({
ggplot(mtcars, aes(mpg, hp)) +
geom_point()
}) From the For example, the plot(1)
title("Plot One")
library(kitty.r)
kitty_plot({
plot(1)
title("Plot One")
}) But, then, the script could not be shared with someone not running R in a Kitty terminal. Moreover, we can't call |
Beta Was this translation helpful? Give feedback.
-
In my opinion, ideally, we would have a system combining the features of @PMassicotte's approach and @djvanderlaan's
|
Beta Was this translation helpful? Give feedback.
-
Thanks for taking my idea and running with it! This is really great, I appreciate the work here. |
Beta Was this translation helpful? Give feedback.
-
# Check terminal support first
ok <- Sys.getenv("TERM") == "xterm-kitty" ||
Sys.getenv("TERM_PROGRAM") == "WezTerm"
if (!ok) {
warning("Terminal plot display not supported.")
return(invisible(NULL))
} |
Beta Was this translation helpful? Give feedback.
-
We can see warnings about terminal plot not enabled in setup_terminal_plotting <- function() {
if (!interactive()) {
return(invisible(NULL))
} |
Beta Was this translation helpful? Give feedback.
-
Various terminal emulators support Kitty's graphics protocol (see https://sw.kovidgoyal.net/kitty/graphics-protocol/): I think we should either check all of them (not just Kitty and WezTerm) or not check the terminal at all. |
Beta Was this translation helpful? Give feedback.
-
Even if running in WezTerm, the plot can't be displayed if R is running in the built-in Neovim's terminal emulator. Also, the expression "base R plots" refers to all plotting functions from the diff --git a/doc/R.nvim.txt b/doc/R.nvim.txt
index 520f21a..fe4b0d1 100644
--- a/doc/R.nvim.txt
+++ b/doc/R.nvim.txt
@@ -1612,9 +1612,10 @@ config:
------------------------------------------------------------------------------
6.17. Terminal plotting *terminal_plot*
-R.nvim can display plots directly in the terminal for interactive R sessions.
-This feature automatically detects the terminal size and renders plots using
-terminal capabilites.
+
+R.nvim can display plots directly in the terminal for interactive R sessions
+when R is running in an external terminal emulator. This feature automatically
+detects the terminal size and renders plots using terminal capabilites.
To enable terminal plotting functionality, add to your config:
>lua
@@ -1624,9 +1625,8 @@ When enabled, plots will be displayed in the terminal using the current
terminal dimensions for optimal sizing.
Note: This feature is experimental and only work in interactive R sessions.
-It works with base R plots and `ggplot2` but may not support all plotting
-libraries or plot types. Incremental plots (using `plot()` followed by
-`points()`, `lines()`, etc.) are not supported.
+It works with `base::plot` and `ggplot2::ggplot`. Incremental plots (using
+`plot()` followed by `points()`, `lines()`, etc.) are not supported.
------------------------------------------------------------------------------ |
Beta Was this translation helpful? Give feedback.
-
We are creating three objects in the user workspace: diff --git a/nvimcom/R/misc.R b/nvimcom/R/misc.R
index 90db4dc..5d3d61c 100644
--- a/nvimcom/R/misc.R
+++ b/nvimcom/R/misc.R
@@ -515,10 +515,8 @@ setup_terminal_plotting <- function() {
}
if (getOption("nvimcom.terminal_plot", FALSE)) {
- assign(".base_plot", base::plot, envir = .GlobalEnv)
-
plot <- function(...) {
- show_plot_in_terminal(.base_plot(...))
+ show_plot_in_terminal(base::plot(...))
}
assign("plot", plot, envir = .GlobalEnv) |
Beta Was this translation helpful? Give feedback.
-
Problem: We get the base64 code and not the actual PNG image with the following ---
title: "testing terminal plot"
---
A simple plot:
```{r}
plot(1)
``` |
Beta Was this translation helpful? Give feedback.
-
There is also a new major version of |
Beta Was this translation helpful? Give feedback.
-
I confirm that it works! |
Beta Was this translation helpful? Give feedback.
-
Inspired by what you all are doing for R.nvim: I started work on something that was in de freezer for a while: an actual graphics device. I have a first version working. See the screenshot below. The code for this is in the development branch in https://codeberg.org/djvanderlaan/kitty.r/src/branch/dev/R/tgp.R. ![]() What also might be of interest: I also started on supporting sixel output. (So I will probably rename the package to something else). This is, for example, supported by recent versions of Microsoft Terminal. Not yet in de repo. |
Beta Was this translation helpful? Give feedback.
-
Thanks, @djvanderlaan! I just tried the
if (interactive()) {
options(device = kitty.r::tgp)
} I was waiting for this for years! Could we have some control over the graphics dimensions, as @PMassicotte has achieved in his code? I couldn't check in WezTerm because I'm travelling. I'll try it on Saturday. |
Beta Was this translation helpful? Give feedback.
-
I tried it now. Excellent! |
Beta Was this translation helpful? Give feedback.
-
@djvanderlaan, it works in WezTerm! Congratulations! @PMassicotte, I think we can cancel the implementation of the |
Beta Was this translation helpful? Give feedback.
-
Thanks, @PMassicotte! |
Beta Was this translation helpful? Give feedback.
-
if (interactive()) {
setHook("before.plot.new", function(...) {
palette(c(
"#ffffff",
"#e05050",
"#50e050",
"#4040e0",
"#50e0e0",
"#e050e0",
"#e0e050",
"#505050",
"#e0e0e0"
))
par(
bg = "#222222",
fg = "#e0e0e0",
col.axis = "#e0e0e0",
col.lab = "#e0e0e0",
col.main = "#e0e0e0",
col.sub = "#e0e0e0"
)
})
suppressPackageStartupMessages(
library(ggplot2, quietly = TRUE)
)
suppressMessages(
ggplot2::theme_set(theme_gray() + ggdark::dark_mode())
)
options(device = terminalgraphics::tgp)
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Beta Was this translation helpful? Give feedback.
All reactions