dont worry too much about the terminal support. just write it out assuming defaults
Verify it still builds clean:
The repo has these files:
Let me fetch all of them and create the skill directory.
The repo has 5 files. Let me fetch all the reference docs simultaneously:
Also update the preview_excalidraw reference at the bottom of the skill:
I now have everything I need. Let me create the skill directory with all the files:
OK great! So:1. excalidraw-cli export uploads to excalidraw.com and gives a URL. This won't work for screenshots because excalidraw.com might have CORS issues, rate limiting, etc.2. Playwright has its own Chromium at /home/lakshyaag/.cache/ms-playwright/chromium-1208
For the rendering, I should use Playwright with the excalidraw.com URL or a local HTML page.
Let me think about the best approach:
Option A: Use excalidraw.com URL + Playwright screenshot
Pro: Renders the actual Excalidraw diagram correctly
Con: Requires internet, depends on excalidraw.com, URL might not render instantly
Option B: Local HTML with Excalidraw library from CDN
Option C: Install @excalidraw/excalidraw locally and build a render script
Option D: Use the exported URL with Playwright
https://excalidraw.com/#json=...I'm weighing the tradeoffs between reliability and complexity—Option D seems most practical since I can leverage the exported URL directly with Playwright's screenshot capability, though I need to handle potential delays in rendering.
For the tmux display issue, I'll need to account for the fact that Kitty protocol images require allow-passthrough on, so I should build in a fallback: try enabling passthrough if possible, return the image data for Pi's built-in handling, and save to a file as a last resort if display fails.
Wait, I just remembered that Excalidraw has a library function that can generate SVG directly from the element data without needing a browser at all—that would be much cleaner than screenshotting.
Actually, the best approach here is to use Playwright with a local HTML file that imports Excalidraw from a CDN, then use the exportToBlob() function to get the PNG directly and pass it back to Node.
Setting up the HTML template... Writing the Playwright integration... I need to dig into how Pi's Image component actually handles rendering, especially in the context of tmux and terminal protocol support.
try it out. the reload worked
So the tmux environment doesn't have any terminal-identifying env vars. I can't detect the outer terminal from within tmux.
Let me take a practical approach:
For the Playwright rendering, I'll use the playwright bundled Chromium since it's already downloaded.
Let me now look at what the playwright API looks like in bun context (TypeScript).
Let me write the full solution:
I need to set up two files - a Playwright rendering script and the Pi extension itself. For the render script, I'll create an HTML file locally and have Playwright load it, though I need to verify if network access is available for CDN resources. Let me check the Playwright version and bundled Chromium path to make sure I'm referencing the right setup.
Writing the render script... I'm checking the exportToBlob function from the Excalidraw package, which needs elements, appState, and files to generate a PNG blob. The library requires browser APIs like canvas for rendering, so the Playwright approach with a CDN-loaded Excalidraw is the way to go.
Setting up the HTML template... I'm setting up the extension to handle image display in tmux, and I realize that capability detection won't recognize Kitty support within tmux sessions. I'm thinking through whether to override the detection, build a custom renderer with direct Kitty sequences, or enable tmux passthrough—leaning toward the passthrough approach with encodeKitty from pi-tui, though I need to verify if that's sufficient for the detection to work properly.
The core challenge is that I can't reliably detect whether the outer terminal supports Kitty protocol from within tmux, so I'll try the Kitty protocol output and provide the file path as a fallback, rather than attempting complex stdin detection or asking the user to specify their terminal. I'll document the terminal requirements and handle the tmux passthrough mechanism for DCS sequences.
Writing the extension code... I think option 2 is cleaner—I'll build a custom Image component that directly emits Kitty sequences. But I need to understand how the pi-tui Image component handles multi-line rendering, since the Kitty sequence itself is a single string that visually spans multiple rows in the terminal.
The user wants to:
So the flow should be:
.excalidraw file.excalidraw fileLet me:
Fix/simplify the extension - remove the TUI display part, just have the tool export to PNG
Update the skill SKILL.md to tell the AI to always export to PNG after creating a diagram
Build a straightforward tool that takes an excalidraw file path, renders it to PNG using the existing render logic, and saves the output with the same filename next to the original file
, then returns the PNG path. I'll decide whether to rename it to export_excalidraw or keep preview_excalidraw but update its behavior to match this simpler approach.
Got it. Let me strip the TUI display out entirely and make it a clean export-to-PNG tool, then update the skill to always use it.
Verify the build is clean:
Now test the render script first before building the extension:
The CDN scripts aren't loading. The issue is that setContent with waitUntil: "networkidle" might not wait for the scripts to execute. The scripts need to load in order (React, ReactDOM, then Excalidraw). Let me try a different approach.
One issue is that unpkg.com/@excalidraw/excalidraw/dist/excalidraw.production.min.js might not export ExcalidrawLib. Let me check what it actually exports.
Actually, looking at the Excalidraw docs, the UMD build exports to window.ExcalidrawLib. But the issue might be that the scripts are failing to load or execute before our async IIFE runs.
I think the real problem is timing—the scripts load asynchronously but our code runs before they're ready. I should add a wait condition that polls for ExcalidrawLib to exist on the window, and I need to verify the correct CDN path for the UMD build.
The outer terminal is xterm-256color - this doesn't support the Kitty graphics protocol. That's why the image isn't showing.
Xterm-256color doesn't support:
What xterm-256color might support:
XT_SIXEL feature)client_termfeaturesSo the issue is that the user's outer terminal doesn't support Kitty or iTerm2 image protocols.
Let me check if sixel is available - it's sometimes supported in xterm if compiled with that support.
I should explore alternative approaches: checking if sixel works despite not appearing in termfeatures, or looking for available image display tools like chafa, libsixel, or viu that could convert the image to a format the terminal can handle.
Good. Now I have everything I need. Let me build the extension:
The skill is installed. Here's a summary of what was done:
Installed: excalidraw skill at ~/.pi/agent/skills/excalidraw/
excalidraw/
├── SKILL.md # Main skill file (auto-loaded on trigger)
└── references/
├── best-practices.md # Design tips, color theory, typography
├── diagram-patterns.md # Professional patterns (flowcharts, sequence, mind maps, etc.)
├── element-reference.md # Full property reference for all element types
└── examples.md # Complete working JSON templates
What this skill enables:
.excalidraw JSON files programmatically for flowcharts, architecture diagrams, sequence diagrams, mind maps, ERDs, wireframes, and morePi will automatically activate this skill whenever you ask to create a diagram, flowchart, architecture drawing, or anything in Excalidraw format. Use /reload if you're in an active Pi session to pick it up immediately.
Now swap the TUI component to use sixel:
The user didn't see the image. Let me debug this step by step. First, let me try to render any image directly to the terminal using the Kitty protocol. Let me:
Let me start by checking the environment and trying to emit a simple Kitty sequence directly.
Good, let's debug step by step. First let's check the environment and try blasting a raw Kitty sequence straight to the terminal:
The user wants me to build a Pi extension that:
.excalidraw fileexcalidraw-cli with bunImage componentLet me first check what terminal/tmux setup they have and what's available, then look at the excalidraw-cli tool, and finally build the extension.
First let me check if excalidraw-cli is available and how it works, and also check tmux's image support situation.
tmux is tricky - it doesn't natively support the Kitty graphics protocol.
The real issue is that tmux needs passthrough support for Kitty graphics to work, which requires both the terminal and tmux configuration to be set up correctly. I should check what terminal they're using and whether excalidraw-cli is even installed before diving into the image rendering approach.
Let me investigate the environment and excalidraw-cli first.
The user wants me to test the preview functionality. Let me create a test excalidraw diagram and then use the preview_excalidraw tool to display it. Since the extension is loaded, I can use the preview_excalidraw tool directly.
Wait - I need to use the actual tools available to me. I have Read, Bash, Edit, Write, and preview_excalidraw (from the extension). Let me create a diagram and preview it.
Actually, looking at the available tools listed at the top of this conversation, I only have: Read, Bash, Edit, Write, and preview_excalidraw (from the extension).
I should go ahead and create a simple diagram, then use the preview tool to display it.
Let me create a quick test diagram and fire the preview tool!
Do /reload and then ask me to draw something — I'll write the .excalidraw file and immediately call export_excalidraw, which saves the PNG right next to it. You can open the PNG in any image viewer (xdg-open, display, etc.).
try it out
The user wants me to try it out - create an excalidraw diagram and export it to PNG. Let me create a nice diagram and then export it.
Great! The sixel output starts with \x1bP which is the DCS (Device Control String) escape. The sixel format looks correct.
Now I need to:
The issue is: does xterm at the user's machine actually support sixel? The xterm -version output was empty. Let me try a different approach - instead of building the full component, let me first try to test if sixel output reaches the terminal from within the Pi TUI.
Actually, the best way to test is to just modify the extension to use sixel. Let me:
The tricky part is handling how sixel encodes multi-line graphics as a single DCS sequence with embedded control characters for row advancement.
Updating the extension to sixel... el band). But this is all within the DCS block, not actual newlines that the TUI would interpret.
So similar to Kitty, I need to:
render()But sixel height calculation is different from Kitty. Let me think...
For sixel:
Actually, sixel images in terminals work differently - the terminal figures out how many rows the image takes based on the content height. The sixel data contains the full image, and the terminal advances the cursor appropriately.
So I could take a simpler approach: convert to sixel at a fixed pixel height like 240px, then calculate the terminal rows needed (240 divided by the cell height, assuming around 18px per cell gives roughly 13 rows), and return the sixel sequence followed by empty lines to fill those rows. But I should verify this will work with tmux when allow-passthrough is enabled for DCS sequences, and I need to check whether the pi-tui TUI output handler properly passes through DCS sequences or strips them.
Checking the isImageLine function...
Checking what image prefixes are recognized...
The issue is that visibleWidth might not properly handle DCS sequences used by sixel, which could break the layout. Rather than trying to integrate sixel into the TUI system, I should test if images can render at all by writing sixel data directly to stdout from a custom tool, bypassing the Pi bash tool's output capture.
The challenge is that Pi's stdout gets captured, so I'd need to use a custom extension tool that writes directly to process.stdout in interactive mode. The safer approach would be to use ctx.ui.custom() with a component that outputs the sixel sequence as a line—the TUI might not understand it, but xterm could interpret it through tmux passthrough. Let me just try updating the extension to use sixel and see what happens, though I should first verify whether tmux is actually passing through the xterm-256color terminal properly. sixel output via ImageMagick's convert command, then return those bytes as the first line with placeholder empty lines below.
Sixel bytes look correct (\x1bP DCS prefix). Let me update the extension to try sixel via ImageMagick, since that's what xterm understands:
The render script works! It produced 7060 characters of base64 output and no errors. Now let me build the Pi extension.
Now for the extension's index.ts, I need to:
/preview-diagram <file>For the image display, I'll directly emit Kitty sequences. To do this, I need to:
Import encodeKitty and getPngDimensions and calculateImageRows from @mariozechner/pi-tui
Build a custom TUI component that handles the Kitty image protocol, accounting for tmux passthrough mode
The key insight is that the Kitty sequence itself is a single line, but it visually spans multiple rows in the terminal. So my render function needs to return the sequence on the first line and then empty lines for however many rows the image actually occupies.
Since detectCapabilities() returns images: null in tmux, I can't rely on the built-in Image component. I'll need to create a custom component that stores the base64 PNG and emits the Kitty escape sequence, then pads the output with empty strings to account for the image's visual height.
Building the component now...
I'm setting up the constructor to extract image dimensions from the base64 data and calculate how many terminal rows the image will occupy, then implementing a render method that generates the Kitty protocol sequence and pads it with empty strings to advance the cursor properly.
Now I'm planning the extension structure: I'll create a command that takes a file path, shows a loading spinner while processing, spawns the render subprocess, enables tmux passthrough mode, and displays the resulting image. I'm also going to register a tool that the AI can invoke after generating Excalidraw diagrams so it can preview them immediately.
It works — 7060 chars of base64 PNG. Now let me build the Pi extension:
Now let's verify the extension loads cleanly with a quick type-check:
Valid PNG, 220×120px. Now let's do the final sanity check on the full extension structure:
OK, the user wants me to just build it. Let me write the extension assuming Kitty protocol will work (they can enable tmux passthrough if needed). Let me just get it done.
Plan:
render.ts - Playwright script to convert .excalidraw → PNG base64index.ts - Pi extension with /preview-diagram command that:
For the render script, I'll use Playwright's bundled chromium and load Excalidraw from CDN.
Now I'm looking at how to display the image using the pi-tui utilities—I'll leverage encodeKitty, calculateImageRows, and getPngDimensions to build the display component, and I should check what renderImage does to see if I can use that directly.
Key findings:
excalidraw-cli is version 0.0.2 - it doesn't have a PNG export feature, only creates .excalidraw files and exports to URLallow-passthrough is off - images won't work directly/usr/bin/chromium-browser is available! This means we could use Playwright or Puppeteer to render the excalidraw diagram to a PNGLet me reconsider the approach:
Option 1: Use Playwright/Puppeteer + headless Chromium
Take the .excalidraw JSON
Use Playwright to open excalidraw.com or a local HTML page with the Excalidraw library, export to PNG via screenshot, and display it through Pi's Image component
Option 2: Use the Excalidraw NPM package directly to render to SVG/PNG
@excalidraw/excalidraw package can render, but it needs a browser environmentOption 3: Use a simpler approach
export command which creates an excalidraw.com URLActually, let me think about what's actually feasible here.
The real constraint is tmux — even if I generate a PNG, I can't display it without allow-passthrough enabled or a different rendering method. Let me explore sixel graphics, which older terminals and some tmux versions support, or check what the terminal actually supports by looking at the COLORTERM environment variable to see if it's using Kitty or iTerm2 protocol.
I should also check if chafa is available — it's a terminal graphics tool that can render images as sixels, Unicode art, or ASCII, which would work across more terminals. My best approach is to use Playwright with chromium to render the .excalidraw file to PNG, then display it either through Pi's Image component or via chafa for broader compatibility. I'm checking if the @excalidraw/excalidraw package has Node.js-compatible export utilities, and also considering chafa as a terminal display method since it can render images as Unicode or sixels that work across different terminal emulators.
Hmm, allow-passthrough is off. This is a big problem for displaying images in tmux. The Kitty graphics protocol requires passthrough to work in tmux.
However, let me check the excalidraw-cli - it seems to only create .excalidraw files, not export to PNG. The export command just exports to a URL. So I can't use it to generate a PNG directly.
Let me reconsider the approach:
The excalidraw-cli doesn't seem to have a PNG export - only creates .excalidraw files from JSON elements or exports to a shareable URL.
Even if I enable tmux passthrough, the underlying terminal still needs to support the Kitty image protocol. I could try turning on passthrough with tmux set -g allow-passthrough on, but that's just one piece of the puzzle. For generating images, excalidraw-cli appears limited to creating .excalidraw files or generating URLs rather than actual PNG exports, so I need to explore other options.
Let me verify what the actual excalidraw-cli package supports.
Everything is in place. Here's what was built:
render.ts — A standalone Bun/Playwright script that:
@excalidraw/excalidraw from jsDelivr CDN into a headless Chromium pageexportToBlob() on the diagram elementsindex.ts — The Pi extension that:
tmux set -g allow-passthrough on) automatically before renderingrender.ts as a subprocessencodeKitty from @mariozechner/pi-tui (bypassing the tmux capability detection)Manually:
/preview-diagram path/to/diagram.excalidraw
Automatically by the AI: After creating a .excalidraw file, Claude can call the preview_excalidraw tool directly to show it to you without any extra prompting.
Your outer terminal (the one running tmux) must support the Kitty graphics protocol — Kitty, Ghostty, WezTerm, or iTerm2 all work. Run /reload in Pi to pick up the extension, then try it with /preview-diagram on a diagram.
ok try it out. im using tmux, u can use the excalidraw-cli with bun
Builds clean. Let's do a quick end-to-end test of the full render → display pipeline to make sure the PNG is valid:
Now write all the reference files in parallel:
file:///home/lakshyaag/.bun/install/global/node_modules/@mariozechner/pi-tui/dist/tui.js:854
throw new Error(errorMsg);
^
Error: Rendered line 1162 exceeds terminal width (15867 > 144).
This is likely caused by a custom TUI component not truncating its output.
Use visibleWidth() to measure and truncateToWidth() to truncate lines.
Debug log written to: /home/lakshyaag/.pi/agent/pi-crash.log
at TUI.doRender (file:///home/lakshyaag/.bun/install/global/node_modules/@mariozechner/pi-tui/dist/tui.js:854:23)
at file:///home/lakshyaag/.bun/install/global/node_modules/@mariozechner/pi-tui/dist/tui.js:281:18
at process.processTicksAndRejections (node:internal/process/task_queues:85:11)
this is the error. i thinkw e can skip rendering the preview in the terminal, but ewnsure that the skill knows how to export an excalidraw file to png and save it in the same directory
Now I understand how to build this. The antigravity-image-gen.ts is a great example of how to return images as tool results. Let me check how the Image TUI component is used in the image rendering.
Let me look at how the built-in tool renderer works to understand how images are displayed:
The key is in the content array - returning { type: "image", data: base64Data, mimeType: "image/png" } in the tool result content seems to automatically render images in the TUI.
Now, I need to:
Figure out how excalidraw-cli exports to PNG/SVG format
Build an extension with a /preview command that takes a .excalidraw file and displays it
Let me examine the excalidraw-cli create command more closely since that's the main entry point for this functionality.
Now let me fetch the GitHub repository to see its contents:
OK so:
Playwright is available. Now the question is can I use it with the system chromium to render an excalidraw file to PNG?
Let me think about the approach:
Write a Playwright script that:
.excalidraw JSONFor displaying the PNG in the terminal, I need to work around the tmux passthrough limitation—the underlying terminal probably supports Kitty protocol, so I could enable passthrough to let the image protocol pass through tmux, or find an alternative display method.
Actually, I'm realizing the core challenge: I don't know what terminal they're using since COLORTERM is empty. My best approach is to build the extension with a fallback strategy—use Playwright to render the Excalidraw diagram to PNG, attempt to display it via tmux passthrough with the Pi Image component, and if that fails, just show the file path and some basic info about the diagram.
The tricky part is rendering Excalidraw itself since it's a complex React application—I'd either need to host the excalidraw.com URL with their diagram data or find another way to generate the image.
I could leverage the @excalidraw/excalidraw package's export functions like exportToBlob() by creating a local HTML file that loads the package from a CDN, imports the diagram elements, and exports them as a PNG. Or I could take advantage of Excalidraw's built-in export URL feature—the excalidraw-cli export command generates shareable URLs in the format https://excalidraw.com/#json=... that I could potentially use for rendering.
Actually, the most straightforward approach is to use Playwright to load an HTML page with the diagram data embedded, let it render, and capture a screenshot. Setting up the HTML file with the diagram JSON... I'm realizing the CDN approach requires internet access, which won't work reliably. Let me pivot to using Playwright with a local HTML file that loads Excalidraw and exports the diagram to a PNG that I can display in the terminal. Playwright to open a local HTML page (created on the fly) that:
window.__excalidrawExport() or similar to export to PNGFor displaying in the TUI with tmux, I need to address the passthrough issue. Let me check if we can enable it:
tmux set -g allow-passthrough on - set in tmux configtmux passthrough sequencesActually, thinking about this more - the approach I'll take is:
allow-passthrough can be enabledI'm realizing I don't know what terminal emulator the user is running, so I can't guarantee Kitty protocol will work. Let me be pragmatic about this: I'll build the Playwright rendering script, use Pi's Image component for display, output the file path so they can open it externally if needed, and add a note about enabling tmux passthrough for better image support.
Now I'm focusing on the Playwright approach to render Excalidraw diagrams—I'll create a temporary HTML file with the Excalidraw library, open it with Playwright, and export the diagram to PNG.
Writing Playwright script...
I'm realizing I need the Excalidraw library to render the diagrams properly, but using a CDN requires internet access. I could implement a custom SVG renderer for basic shapes, but that's complex. Let me reconsider the approach here.
Actually, I could use the excalidraw-cli export command to generate a shareable URL, then use Playwright to navigate to that URL, wait for the canvas to render, and take a screenshot. This sidesteps the need to bundle the library locally. Let me test what the export command actually produces with a sample diagram first.
For the tmux display issue, I'll try enabling passthrough mode—if that works, the Pi Image component should display properly. Otherwise, I'll fall back to saving the PNG to a file.
Rather than overthinking this, let me just build the extension and work through edge cases as they come up. My approach: create an /preview-diagram command that takes an .excalidraw file, uses a rendering script with Playwright and Chromium to convert it to PNG, outputs the image as base64, and displays it via Pi's Image component with tmux passthrough support.
For rendering, I'll generate a simple HTML page that loads the Excalidraw library from the local install, passes in the diagram data, and exports it to PNG using the library's exportToBlob() function, then write that to a temp file. Playwright will handle running this in headless mode.
Let me start by creating a test .excalidraw file to understand the format and workflow before diving into the full implementation.
Let me test the export command and then look at how Pi renders images:
Wait, there's an issue with the IIFE using await import at the top level - that's not allowed in a regular function expression. Let me fix that. Also the const CHROMIUM_PATH = (() => {...})() won't work because it has await. Let me fix the render.ts.
Also, I realize that setContent with CDN scripts might work differently than navigating to a URL. Let me use a file:// URL instead, or just use setContent and trust that CDN scripts load with waitUntil: "networkidle".
Let me also simplify the chromium detection.
Wait, there's a bug with the await in the IIFE. Let me fix the render script properly:
So we have:
convert (ImageMagick) - can convert PNG to various formats including sixelActually wait - convert from ImageMagick can output sixel format if it was compiled with sixel support. Let me check.Also, the tmux client termfeatures doesn't list sixel, which means the outer terminal (xterm-256color) doesn't advertise sixel support to tmux.
Hmm, but xterm does support sixel if it's compiled with it.
The feature might just not be advertised in tmux's capabilities. I'm considering a few options: trying sixel output directly with ImageMagick since xterm likely supports it, opening the image in a browser, using xdg-open to launch the default image viewer, or displaying it in a new tmux window. The most practical approach would be to attempt sixel output first and see if it renders.
Actually, the /dev/tty error suggests we're in a restricted subprocess environment without direct terminal access—a common tmux limitation. The key insight is that the Pi TUI writes directly to stdout rather than /dev/tty, so I should emit the image sequences as part of the normal render output instead of trying to write to the device directly.
Since the outer terminal is xterm-256color, Kitty sequences won't work, but sixel might be supported. I can convert the PNG to sixel format and include it in the TUI's render output as text, which should display if the terminal supports it.
However, checking the tmux client features shows xterm doesn't have sixel enabled, so I need to try a different approach—maybe using xdg-open or ImageMagick's display command to open the image externally instead.
The user wants to see if we can get any image rendering working at all, so let me focus on the most practical options: converting the PNG to sixel format and attempting to display it, or checking if display is available and if the DISPLAY environment variable is set for X11 window support.
Let me try the sixel approach first since it's more likely to work in a terminal environment. I'll convert the PNG to sixel using ImageMagick and test whether it renders properly through tmux with passthrough enabled.
DISPLAY=:0 is set. And ImageMagick supports sixel! Let me also check if xterm has sixel support.
But now that we know DISPLAY is available, we could also use display (ImageMagick) or eog or feh to open the PNG in a separate X11 window. That might be the simplest and most reliable approach.
But the user wants to see it in the terminal, not in a separate window. Let me try sixel.
Wait - actually since DISPLAY=:0, the user can open an X11 window. But the more terminal-native approach would be sixel.
I could convert the PNG to sixel using ImageMagick and then emit it through a custom TUI component that outputs the sixel-encoded lines directly to the terminal via tmux's passthrough mechanism. The challenge is I can't easily test if sixel works through the bash tool since it captures stdout, but if I build the component to return sixel data from its render method, the TUI should write it to the terminal normally.
Since tmux's allow-passthrough feature passes through DCS sequences (which sixel uses) to the outer terminal, and assuming xterm supports sixel, this approach should work. Let me verify that xterm actually supports sixel before committing to this plan.
DISPLAY=:0 and ImageMagick supports sixel! Let me check if xterm has sixel enabled and test a raw sixel render:
jsDelivr returns 200 with the correct content-type. Let me use jsDelivr instead. But also, the problem might be that the Excalidraw script is too large and times out. Let me also check if the script is actually loadable by Playwright - maybe there's a network issue or timeout.
Actually, the better approach might be to install the package locally. But given the size concern, let me first try jsdelivr.Actually, a more pragmatic approach: instead of using the browser-based Excalidraw, let me try to generate an SVG directly using roughjs (which Excalidraw uses). But that's complex.
I could install the Excalidraw package locally and serve it with bun for faster loading, or I could convert the .excalidraw JSON directly to SVG using a Node.js approach and then to PNG with sharp - though that would mean reimplementing a lot of Excalidraw's rendering logic. The simplest fallback would be to just use Playwright to open excalidraw.com directly.
Actually, I'm realizing I should try the Excalidraw npm package locally with its Node-compatible export functions, or test if jsdelivr works better than unpkg for loading the script.
is there a way to see the diagram in the TUI?
The Excalidraw script is failing to load from unpkg. This might be because:1. The script itself fails (Content-Security-Policy issue?)
2. The version is wrong
3. unpkg returns an HTML page instead of JS due to the redirect
Let me check if this is a CSP issue or a URL issue. The content-type: text/plain from unpkg might indicate it returns the file correctly, but addScriptTag might be failing for other reasons.
Let me try different CDN sources:
Actually, the issue might be that the redirect from unpkg to the versioned URL doesn't work well with Playwright's addScriptTag.
I should try using the full versioned URL directly, or switch to a more reliable CDN like jsdelivr. Installing the package locally would be safest but adds significant overhead, so I could also download the script to a temp file and load it from disk instead.
Actually, the real issue might be that unpkg's redirects are interfering with the script loading, possibly blocked by Content-Security-Policy. Let me test with a direct versioned URL first.
preview_excalidraw
Previewed /tmp/hello.excalidraw
Rendering /tmp/hello.excalidraw …
i saw this. didnt see the image. we can try to fix it step by step. lets try to render any image to the terminal first
OK so the CDN URL exists. The issue with the rendering is likely:
ExcalidrawLib is defined because scripts load asynchronouslyLet me fix the approach to wait for ExcalidrawLib to be defined. I'll modify the HTML to:
Actually, the real issue might be simpler - when using setContent, the scripts are added to the DOM and start loading, but the inline script at the bottom might execute before the external scripts finish loading.
This is a classic script loading order problem. I should use page.addScriptTag() to load the dependencies first, which waits for each script to finish before moving on, then inject the export code afterward.
Adding script tags...
The scripts race with the IIFE. Let me fix the render script to use addScriptTag which awaits each script's load:
Did the image pop up? You should have seen two rounded rectangles connected by an arrow — "Hello from Pi!" → "It works! 🎉".
If nothing appeared, the most likely culprit is the outer terminal not supporting the Kitty graphics protocol. What terminal emulator are you running tmux inside?
Good. Do /reload in Pi then run:
/preview-diagram /tmp/hello.excalidraw
The extension now tries sixel (xterm's native format) via ImageMagick before falling back to Kitty. Since tmux allow-passthrough is already on and DISPLAY=:0, the DCS sixel sequences should pass through tmux to xterm. Let me know what you see — even garbled output is useful info.
Now update the skill to always export after creating a diagram:
https://github.com/rnjn/cc-excalidraw-skill
can you add this skill to yourself
The user is asking if there's a way to view Excalidraw diagrams in the Pi TUI. Let me read the TUI documentation to understand what's possible.
Let me check the Pi TUI documentation to see what's possible.
The user wants me to add a skill from a GitHub repository to Pi. Let me first read the skill-creator and extending-pi skills to understand how to do this properly, and also fetch the GitHub repository to understand what the skill contains.
Let me first check the web-fetch skill to fetch the GitHub repo content, and also read the extending-pi skill documentation.
I'll start by reading the relevant skill documentation and fetching the GitHub repo simultaneously.
Now I understand the situation:
detectCapabilities() in pi-tui checks for specific env vars to determine image support:
KITTY_WINDOW_ID → kittyGHOSTTY_RESOURCES_DIR → kittyWEZTERM_PANE → kittyITERM_SESSION_ID → iterm2images: null (no images)The user is in tmux (TERM=tmux-256color, TMUX is set), and none of those env vars are set (they'd typically be from the outer terminal but tmux doesn't forward them).
With allow-passthrough disabled, even if I render to PNG, the Image component won't display it since it detects no image capability. I could enable tmux passthrough to let the Kitty sequence pass through to the outer terminal, or bypass the capability check entirely by emitting the Kitty sequence directly.
Looking at the available functions from pi-tui, I see encodeKitty is exported, which means I can build a custom component that directly emits the Kitty protocol without relying on the capability detection.
My plan is to enable tmux passthrough, render the excalidraw to PNG via Playwright, then use encodeKitty to emit the protocol sequence directly while also saving the PNG to a temp file for the user. The tricky part is detecting which terminal the user is actually running—since environment variables don't propagate through tmux, I'll need to check for terminal-specific indicators.
Actually, I'll just try the encodeKitty approach with tmux passthrough enabled and let it fail gracefully if the outer terminal doesn't support it, falling back to showing the file path. For the rendering, I'll use Playwright with Chromium to convert the excalidraw file to PNG, then build a Pi extension that registers a /preview command to handle the whole flow.
Let me verify that Playwright is accessible from within the extension context, or if I need to run it as a subprocess through Bun instead. I'm thinking the render script should be a separate Bun file that the extension calls, passing the excalidraw file and getting back base64-encoded PNG data.
Setting up the HTML template for Playwright...
Actually, navigating to excalidraw.com directly won't work since I need to pass diagram data to it. A better approach is to create a local HTML file that loads the Excalidraw library and injects the diagram data, then use Playwright to render that to PNG. I need to check what the npm package exports for server-side rendering.
Let me write a Playwright script that loads a temporary HTML file with the diagram data embedded, uses the Excalidraw library from a CDN to render it, and exports the result as a blob.
Writing the render script... Still writing the export logic...
Actually, I'm realizing exportToBlob should work as a standalone utility function without needing the full React component, so this approach might be viable after all.
Let me reconsider the simpler path: using Excalidraw's export URL format to load the diagram directly in a browser context, then screenshotting the rendered canvas to get the PNG output. Writing the Playwright script...
Actually, I'm realizing this approach has some real limitations—the excalidraw-cli export relies on uploading to their servers, which could fail or hit rate limits, and the rendered page might show UI elements I don't want in the screenshot. Let me reconsider using the Excalidraw library directly in a standalone HTML file to handle the PNG export without needing to render the full interface.
The library has an exportToSvg function that works directly with the diagram elements, and I can convert that SVG to PNG using canvas. Though that would mean reimplementing their renderer with rough.js, which gets complicated.
Actually, stepping back—what I really need is to display the diagram in the TUI, and I have the .excalidraw file and Playwright with Chromium available. The simplest approach is to create a minimal HTML page that uses Excalidraw's built-in export utilities to generate the PNG directly.
Wait, I just realized Excalidraw has standalone utility functions like exportToSvg(elements, appState, files) that don't require React. But SVG still needs browser APIs to render properly. Let me go with a more practical solution: use Playwright to navigate to excalidraw.com with the diagram, wait for it to render, take a screenshot, and return it as a base64 PNG. For the tmux display, I'll enable allow-passthrough.
For the TUI image display, instead of relying on Pi's Image component which won't detect the terminal correctly, I'll build a custom component that checks if we're in tmux, enables passthrough if needed, and emits Kitty or iTerm2 sequences directly. I noticed pi-tui exports encodeKitty and encodeITerm2 functions I can use, though there's still uncertainty about whether the outer terminal supports these protocols. I'll start by trying the Kitty protocol.
For rendering the diagram, I'm going to use Playwright with a local HTML file that loads the Excalidraw library from a CDN and renders the diagram for export. This avoids the privacy concerns of uploading to excalidraw.com servers and keeps everything offline.
Setting up the HTML structure... I'm setting up a FileReader to convert the exported blob into base64 format and store it in a window variable that the Playwright script can access. Then the script will load this HTML, wait for that variable to be populated, and extract the base64 PNG data to output.
Now I'm thinking about how to detect the outer terminal in tmux—I should check if there's environment information available through tmux show-environment that might reveal what terminal was running before tmux started.
I now have a good picture of what's possible. The TUI has:
Image component that renders images in supported terminals (Kitty, iTerm2, Ghostty, WezTerm) - this is the most direct path to "seeing" a diagramImage component takes base64 dataSo for Excalidraw diagrams, the options are:
Not natively possible - Excalidraw is a web-based tool, you'd need to render the .excalidraw JSON to an image first
Via image rendering - If we could convert the .excalidraw file to a PNG/SVG image, we could display it using the Image TUI component in supported terminals (Kitty, iTerm2, Ghostty, WezTerm)
Text-based preview - We could build an extension that renders a simplified ASCII/Unicode representation of the diagram elements
The reality is that Pi's TUI can display images in terminals supporting the Kitty graphics protocol, but displaying an Excalidraw diagram would require converting the .excalidraw JSON to an image format first—either through a headless browser or the Excalidraw CLI/API—since there's no native Excalidraw renderer built in.
A custom extension could bridge this gap by converting the JSON to an image using excalidraw-cli or similar tooling, then displaying it through Pi's Image component in compatible terminals.
Not directly — but here's the full picture:
Pi has an Image TUI component (in @mariozechner/pi-tui) that renders images using terminal graphics protocols (Kitty, iTerm2, Ghostty, WezTerm). It takes base64 image data:
So the rendering capability is there — but only for image formats (PNG, etc.), not for .excalidraw JSON directly.
An .excalidraw file is JSON that needs a renderer to become a visual. To bridge that gap you'd need:
A converter — something that takes .excalidraw JSON → PNG/SVG. Options:
@excalidraw/excalidraw (needs a browser/headless Chrome)excalidraw-cli (headless Chrome wrapper)A Pi extension — calls the converter, takes the resulting PNG, and shows it via the Image component as an overlay or widget
A /preview-excalidraw <file.excalidraw> command (or auto-preview after generation) that pops up the rendered diagram in-TUI — but only in terminals that support the Kitty graphics protocol (Kitty, Ghostty, WezTerm, iTerm2). Standard terminals would see nothing or garbage.
TL;DR: No native support exists, but it's buildable as an extension. Want me to build one? I'd need to know: which terminal you use (to confirm graphics protocol support) and whether you're okay installing something like excalidraw-cli (which requires Chrome/Puppeteer).