Skip to content

feat: make image preview support inline image protocol#1069

Draft
yorukot wants to merge 9 commits intomainfrom
feat/inline-image-protocol
Draft

feat: make image preview support inline image protocol#1069
yorukot wants to merge 9 commits intomainfrom
feat/inline-image-protocol

Conversation

@yorukot
Copy link
Owner

@yorukot yorukot commented Sep 13, 2025

No description provided.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 13, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

codescene-delta-analysis[bot]

This comment was marked as outdated.

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Failed
Enforce critical code health rules (1 file with Bumpy Road Ahead)

Gates Passed
2 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce critical code health rules Violations Code Health Impact
image_preview.go 1 critical rule 9.10 → 8.85 Suppress

Quality Gate Profile: The Bare Minimum
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

// ImagePreview generates a preview of an image file
func (p *ImagePreviewer) ImagePreview(path string, maxWidth int, maxHeight int,
defaultBGColor string, sideAreaWidth int) (string, error) {
defaultBGColor string, sideAreaWidth int) (string, error, ImageRenderer) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Bumpy Road Ahead
ImagePreview has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function

Suppress

Comment on lines +378 to +402
func (p *ImagePreviewer) ClearAllImages() string {
var result strings.Builder

// Clear Kitty protocol images if supported
if p.IsKittyCapable() {
if clearCmd := p.ClearKittyImages(); clearCmd != "" {
result.WriteString(clearCmd)
}
}

// Clear inline protocol images if supported
if p.IsInlineCapable() {
if clearCmd := p.ClearInlineImage(); clearCmd != "" {
result.WriteString(clearCmd)
}
}

return result.String()
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Bumpy Road Ahead
ClearAllImages has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function

Suppress

return "" // No need to clear if terminal doesn't support inline protocol
}

return strings.Repeat(" ", 9999)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's precompute this. Store it in a predefined variable InlineImageCleanupString or something. Will save a bit of processing, and will make code more readable.

@yorukot
Copy link
Owner Author

yorukot commented Sep 14, 2025

The current issue is that the inline image protocol doesn’t seem to support removing images, so we need to handle this through bubbletea instead. However, during this process, I found that bubbletea recognizes an image as a single line of text, which prevents the subsequent content from being re-rendered. So I used this workaround to handle the issue, but it’s admittedly a bit of a hack. I’m still considering whether there’s a cleaner solution, or if it might be better to simply force bubbletea to re-render.

@lazysegtree
Copy link
Collaborator

I see. I will try to take a stab at the issue if possible.

@lazysegtree lazysegtree force-pushed the feat/inline-image-protocol branch from 8da7a7f to 1790cd4 Compare December 21, 2025 04:58
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Dec 21, 2025

Deploying superfile with  Cloudflare Pages  Cloudflare Pages

Latest commit: aa49bd6
Status: ✅  Deploy successful!
Preview URL: https://5238a227.superfile.pages.dev
Branch Preview URL: https://feat-inline-image-protocol.superfile.pages.dev

View logs

@lazysegtree
Copy link
Collaborator

Rebased

@lazysegtree
Copy link
Collaborator

lazysegtree commented Dec 21, 2025

image
echo "\x1b]1337;File=inline=1;width=10%:$(base64 -i jin.jpg)\x07"
image
------~~~^^^
➜  Pictures clear; echo "------${jin}###\x1b[1;17H~~~~~~\n**********\n@@@@@@@@@@@\x1b[3;20H^^^"
------~~~~~~
**********
@@@@@@@@@@@###^^^
➜  Pictures clear; echo "------${jin}###\x1b[1;17H~~~~~~\n**********\n@@@@@@@@@@@\x1b[3;20H^^^"
➜  Pictures
image

@lazysegtree
Copy link
Collaborator

image

For kitty we don't inline. Its on a diff z index.

@lazysegtree lazysegtree force-pushed the feat/inline-image-protocol branch from 4c41607 to fea4eee Compare December 21, 2025 06:52
@lazysegtree
Copy link
Collaborator

in kitty we added "\x1b[1;" + strconv.Itoa(sideAreaWidth) + "H". As it was not inline and kitty would randomly move the cursor.

Cursor now moves back to start. and bubblea fills everything with empty bg character.

But, since kitty is not inline, its image is not affected.

For inline, its not simple. Moving cursor back to start will overwrite everything.

@lazysegtree
Copy link
Collaborator

image
st="\x1b[48;2;39;40;34m"
end="\x1b[0m"
 
line="                            "
clr_line="${st}${line}${end}"
block="${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}"
pos="\x1b[2;6H"
clear; echo "${block}${pos}${jin}"

@lazysegtree
Copy link
Collaborator

image image image

@lazysegtree
Copy link
Collaborator

image

The image must rescaled to best fit.
Rest of the whitespace will be removed manually via overwriting.

@lazysegtree
Copy link
Collaborator

image

Set preserveAspectRatio as 0

image
jin="\x1b]1337;File=inline=1;preserveAspectRatio=0;width=12;height=3:$(base64 -i ~/Pictures/jin.jpg)\x07"
st="\x1b[48;100;139;140;34m"
end="\x1b[0m"
 
line="                            "
clr_line="${st}${line}${end}"
block="${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}"
pos="\x1b[2;6H"
clear; echo "${block}${pos}${st}${jin}${end}\n\n"

@lazysegtree
Copy link
Collaborator

We will use \x1b[s\x1b[1;4Hsomething\x1b[u" to save and reset cursor

@lazysegtree
Copy link
Collaborator

@lazysegtree
Copy link
Collaborator

The code of image preview is not very modular. Need to create a common interface. May use same pattern as we have in thumbnail generators.

We need to pass the style info to ANSI/Kitty/Inilne renderers and have them pass the final block, without us doing any thing else.

@lazysegtree
Copy link
Collaborator

image
jin="\x1b]1337;File=inline=1;preserveAspectRatio=0;width=12;height=3:$(base64 -i ~/Pictures/jin.jpg)\x07"
st="\x1b[48;100;139;140;34m"
end="\x1b[0m"
 
line="                            "
clr_line="${st}${line}${end}"
block="${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}\n${clr_line}"
pos="\x1b[2;6H"
save="\x1b[s"
reset="\x1b[u"
pos_beg="\x1b[1;1H"
clear; echo "${block}${save}${pos}${jin}${reset}${save}${pos}${st} ${end}${reset}"

Space characters should clear the images. But something isn't working.

image

@lazysegtree
Copy link
Collaborator

lazysegtree commented Dec 21, 2025

image

Attempting to clear with - doesn't work too

Test preview only

diff --git a/src/internal/model.go b/src/internal/model.go
index b028f88..bad3ed6 100644
--- a/src/internal/model.go
+++ b/src/internal/model.go
@@ -610,7 +610,7 @@ func (m *model) View() string {
 	}
 
 	finalRender = m.updateRenderForOverlay(finalRender)
-
+	finalRender = m.filePreviewPanelRender()
 	return finalRender
 }
 
diff --git a/src/pkg/file_preview/inline.go b/src/pkg/file_preview/inline.go
index 616e4c5..f694ad9 100644
--- a/src/pkg/file_preview/inline.go
+++ b/src/pkg/file_preview/inline.go
@@ -101,6 +101,7 @@ func (p *ImagePreviewer) renderWithInlineUsingTermCap(
 	//block := strings.Repeat("------------------\n", 10)
 	buf.WriteString(filePreviewStyle.Render(block))
 	buf.WriteString("\x1b[s")
+	sideAreaWidth = 1
 	buf.WriteString("\x1b[1;" + strconv.Itoa(sideAreaWidth) + "H")
 	opts := rasterm.ItermImgOpts{
 		Width:             strconv.FormatInt(int64(displayWidthCells), 10),

@lazysegtree lazysegtree added the pr needs work PR needs work before it can be further reviewed/merged label Jan 1, 2026
@yorukot
Copy link
Owner Author

yorukot commented Jan 13, 2026

Currently, it's working very well, with no aspect ratio issues and images rendering correctly. The only problem is that old images sometimes freeze and aren't cleared.

@lazysegtree
Copy link
Collaborator

@yorukot Yes, Images don't get cleared

2026-01-13.23-30-52.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr needs work PR needs work before it can be further reviewed/merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants