Vampir 0.3: Native views, Design choices, Optimizations and a cautionary tale for Agentic Shortcuts.


While I was working on Vamprowser, Vampir seemed to have a problem with rendering things on top of native views, like the WebView. The most unorthodox shit was pulled, like screenshotting the page, hiding the native view, among other stuff.

GPUI isn’t made to interop with native views, far from it, it’s DESIGNED to be the view itself, which is why it’s been struggling with blending WebKit with itself for a while now

And so, a patch began as a PR on top of a 0.2.x checkout. It attempted to generalize native views, but omitted one big step: how to actually do it without lagging the application, consuming memory and handing over a shittier result.

It grew from “yeah lemme design this” into “Let me actually take a look”. The codebase was a MESS. But that’s what incentivized me to better look at my agents and how they’re doing. And so far, so good.

0.3 will include a GPUI-ce upgrade, platform-window fixes, code cleanup and optimization, as well as a way to deal with native resources without having to resort to unorthodox ways.

The Command Palette can now live outside of the main window in its own child

With how native views can be embedded within Vampir, people would want a system that would work with them. Albeit, in unorthodox ways.

With 0.3, you have the option to offload tooltips and other stuff, like the Palette to a child window behind a scrim (fancy word for that dim you see). This makes your Vampir app feel more within your desktop as opposed to it feeling like a self-contained application.

This took a lot of work, but what I am confident in saying is that the change is so broad and rigorously tested against so many edge case scenarios that Linux support is also guaranteed. With how X’s window positioning works, it works flawlessly.

With how Vampir works, you don’t really have to change so much of your codebase to enable it. You do have to worry about edge cases if you’re willing to uphold this separation, as some people wouldn’t like the change. That’s why this is an option, rather than a breaking change.

Speaking of GPUI-CE…

GPUI-CE has been upgraded to 0.3.0. Which means that many APIs that Vampir used are now offloaded into GPUI itself.

This also means that some glyphs started rendering incorrectly due to wrong API calls. Now, they’re doing better.

GPUI-CE also defines an anchored-popup primitive that basically defines what’s present within the Palette. However, it’s a Wayland-only feature.

NOTE: You must enable the patch yourself if you wish to use the window-child command bar. Dependency patches are not applied unless you apply yourself in cargo.toml under a [patch] clause;

[dependencies]
vampir = { path = "third-party/vampir", features = ["command-bar-window"] }
gpui = { package = "gpui-ce", version = "0.3" }
gpui_ce_platform = { version = "0.3", features = ["font-kit", "runtime_shaders", "x11"] }

[patch.crates-io]
gpui-ce = { path = "third-party/vampir/vendor/gpui-ce" }
gpui_ce_macos = { path = "third-party/vampir/vendor/gpui_ce_macos" }
gpui_ce_windows = { path = "third-party/vampir/vendor/gpui_ce_windows" }
gpui_ce_linux = { path = "third-party/vampir/vendor/gpui_ce_linux" }

Vampir’s GPUI-CE patch aims to make it universal. I’m planning to put a PR for it, but I doubt it would get accepted by the team because it’s too risky of a patch to delocalize it from Vampir. It’s tested within its own usecases, and with enough maturity, could be further improved.

The Vampir-Platform relation looks like this:

PlatformImplementation
macOSA child panel attached to the host window.
WindowsAn owned popup tool window.
X11An override-redirect popup-menu window associated with its parent.
WaylandThe existing popup methods, with keyboard and dismissal fixes. Experimental, wontfix anything further.

Overall, this shouldn’t pose much of an issue, but it is still an optional feature rather than a new paradigm imposed on users.

Windows is tended to as well.

On GPUI for Windows, embedded native widgets could fail and crash the app, and in certain cases also draw over the embedded view.

There’s also some platform issues that had to get fixed, like scaling issues and font rendering. That’s fixed as well.

Bug fixes galore!

Keyboard focus is fixed, so both the Vampir view and the native view get it, however this depends on whether it’s warranted. Of course, a GPUI input and a Native input will respond to keystrokes and combinations accordingly, so it’s up to you to take care of what gets what.

There’s also scaling issues, workspace issues, and more that have been fixed. App-crashing upstream bugs have also been resolved.

Oh yeah, menus have been fixed as well.

Menu actions in Vampir previously depended on string-based names for action lookup which is an extremely inefficient paradigm that could lead to errors.

Now, they’re ID-based, which means you could have the same string do two actions.

Another QoL change is that MenuIcon now accepts custom drawing, which means you can use your own glyphs. There’s also the added enabled field which gives users info into whether a function is usable in reusable menus.

An issue where rapidly clicking the menu while motionless has also been fixed.

Design issues have been fixed!

The footer had misplacement, the selection text contrast was horribly put. It’s simply a disastrous look that should’ve been fixed.

I did mention that Windows font rendering has been fixed. DirectWrite had something to do with it. I hate ClearType.

GPUI-CE 0.3.0 removed some glyphs that were crucial, including the tab/search close glyphs. They’re now rendered as vector paths. Some glyphs need to be replaced, one thing in particular is that you have to use .font(vampir::ui_font_face()) for a fallback. Of course, you don’t need that if your glyphs were custom to begin with, but it’s a good measure regardless.

Vampir’s colorways are also changed. Saturation now goes to 3 (300%) which means you can saturate your app more. But the way it worked is completely incompatible with moving up and beyond compatible values, so from 100% onward the algorithm raises some elements more, some elements less, and overall saturates the entire app to the point where you see the color, but it’s not deep-fried.

Optimizations galore! (The interesting part)

Oh boy. There’s a LOT to discuss.

First of all, Agents aren’t used to Rust. Despite it being a mature language, it’s not really agent-friendly. Not that I expect it to, for a language as serious as Rust, but a lot of the things that it gets wrong with Rust largely come from the fact that it’s used to taking shortcuts from other languages that have been optimized to all hell, like JS.

This isn’t JS. This is a statically typed object-oriented memory-strict language, that Rust often forgets to remember.

Now, I told my agents to scrub the code, and it found nothing. That’s what I’m worried about. Agents can’t tell their shit’s slow without an expert view. So I did a little lookup on Kagi and went into compiling something along the lines of “Rust Best Practices 101” to try and mitigate some shortcuts I was perfectly aware both Codex and Claude were pulling. And wouldn’t you believe it, applying it gave me a noticeable speedup.

Consult the charts below for an accurate representation of what exactly is sped up and how much

Menus and ownership

Before After % of baseline
100
24.9
Flat menu
100
22
Nested menu
100
16
Styled label
100
56.5
Callback
100
54.6
Membership
Exact values
Operation Before After
Flat menu 100 24.8551
Nested menu 100 22.0086
Styled label 100 16.0201
Callback 100 56.487
Membership 100 54.6151

Search highlighting

Before After 碌s
0.2
0.0036
Empty
0.5
0.4
Whole word
0.6
0.5
Fuzzy
0.5
0.5
Failed
0.5
0.3
Overlap
0.5
0.4
Unicode
0.4
0.3
Detail
Exact values
Operation Before After
Empty 0.1651 0.0036
Whole word 0.526 0.3775
Fuzzy 0.5777 0.4915
Failed 0.5369 0.5363
Overlap 0.518 0.3157
Unicode 0.5271 0.3867
Detail 0.3868 0.2939

Allocations

Before After % of baseline
100
0.0497
Command
empty
100
62.6
Command
match
100
28.6
Ranked
unmatched
100
23.5
Nested menu
100
33.3
Styled label
100
0
Highlight
empty
Exact values
Operation Before After
Command: empty 100 0.0497
Command: match 100 62.5872
Ranked: unmatched 100 28.6143
Nested menu 100 23.5294
Styled label 100 33.3333
Highlight: empty 100 0

Command cache

Before After 碌s
12.2
0.0105
Empty query
76.1
0.0106
Matching query
Exact values
Operation Before After
Empty query 12.2164 0.0105
Matching query 76.1341 0.0106

The biggest (non)issue that separates programmers from vibecoders is the fact that we learn. We grow. We pick design choices that obviously benefit (and not mess our flow up) and we use the tools at our disposal to speed things up without going the wrong direction.

agents.md

Speaking of Agents, I often saw (from my friends’ experience) that agents were simply just failing to create coherent Vampir apps.

Vampir is an Agent-first framework, where from 0.3 onwards it will get progressively better, to the point where Agents have guidelines in making applications from a single prompt, even on open-weights.

Before 0.3 it would struggle trying to make applications, and oftentimes copy from the Gallery, while omitting elements that could better the app. I have not seen a single Vampir app utilize the Radio button, or columns, and on horrible models it messes up the bottom bar, the top bar has unnecessary taglines.

0.3 includes “tutorials”, something your agents can base their work off of. It basically takes care of de-sloppifying your app, turning it into a clean, usable and fast application.

And?

That’s it!

Lots of things have to be added into Vampir but, like the previous release’s sentiment, It will only progressively get better, faster and feature-complete. Currently there’s no ideas on improving the framework, but GitHub is your friend. Issues and Pull Requests are always open, so you can go ahead and submit changes and keep the software going.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.