diff --git a/docs/platform-flags.md b/docs/platform-flags.md
index 23fe04d..56a0590 100644
--- a/docs/platform-flags.md
+++ b/docs/platform-flags.md
@@ -230,23 +230,35 @@ initialise on an MTA thread.
view. That is the concrete reason the design uses one WebView hosting N terminals rather than one per
tab: twenty tabs would mean twenty of those trees.
-**The Avalonia WebView on Linux remains unproven, and is still the largest risk in the plan.** The
-package's own release notes say `NativeWebView` gained Linux support via a **WPE** backend
-(`libwpewebkit-2.0`), which is much less widely installed than WebKitGTK — and it ships a separate
-`NativeWebDialog` described as *"particularly useful for platforms like Linux where embedded WebView
-controls might not be available"*, which is the vendor confirming the concern. *Unverified:* a spike
-must cover Ubuntu on both Wayland and X11, Fedora KDE, and macOS 15.
+**The Linux WebView needs `ExperimentalOffscreen`, or the terminal is blank.** This entry used to predict
+that WPE (`libwpewebkit-2.0`) would be the Linux backend and be too rarely installed; the prediction was
+wrong in its details and right about the outcome. Measured on Fedora 44 with
+`Avalonia.Controls.WebView` 12.0.1, by a spike that hosts a `NativeWebView` and reads `AdapterInfo`:
+
+- The adapter is **WebKitGTK 2.52.5**, not WPE, and it reports `IsSupported = True`. Fedora packages no
+ WPE WebKit at all — `dnf search wpe` returns a computer-algebra package and nothing else — so the WPE
+ path is not merely rare there, it is unavailable.
+- In its default mode that adapter reports **`SupportedScenarios = NativeDialog`**: a window of its own,
+ and nothing that can be hosted in place. The same under X11 and under Wayland, so this is the adapter's
+ answer rather than a session-type problem.
+- Setting **`ExperimentalOffscreen`** on `GtkWebViewEnvironmentRequestedEventArgs` changes the same
+ adapter's answer to **`OffscreenRenderer`** — the compositor-drawn mode, which is what
+ `NativeWebViewCompositorHost` (mentioned below as an unknown) exists to host. `MainWindow` sets it; see
+ `OnTerminalEnvironmentRequested`, which is a no-op on Windows and macOS by type rather than by OS check.
+- In both modes the page loads and `InvokeScript` answers, which is the trap: **the failure has no
+ diagnostics.** Everything except the pixels works, so it reads as the terminal being broken rather than
+ as the host having nowhere to draw. `AdapterInfo.SupportedScenarios` is the thing to look at first.
+
+*Still unverified:* whether the offscreen mode actually paints, and how it behaves for input, IME and
+resizing. The spike could not answer it — an XWayland root capture is black under a Wayland compositor
+and `RenderTargetBitmap` does not capture a compositor surface — so it needs eyes on a running client.
+macOS 15 remains untested entirely.
`ITerminalHost` was supposed to be the seam that keeps a backend swap cheap, and it is **declared but not
implemented** — nothing in the application uses it, and the view navigates `NativeWebView.Source` directly.
Swapping backends today means editing `MainWindow.axaml` and its code-behind. That is a small job, but do
not plan around a seam that is currently only a file.
-One more reason the Linux picture may be better than this entry assumes: the package also ships
-`NativeWebViewCompositorHost`, a non-windowed host drawn through Avalonia's compositor. A compositor host
-would not have the airspace problem described below at all. Whether it can be selected deliberately is
-unknown and worth establishing during the spike, because it would change how overlays can be built.
-
**`Avalonia.Diagnostics` has no 12.x release** (latest is 11.3.18), so the developer tools overlay is
unavailable on Avalonia 12. Development-only, so nothing ships differently — but debugging a layout
problem currently means reasoning rather than inspecting.
diff --git a/src/DodoSSH.Client.App/Views/MainWindow.axaml.cs b/src/DodoSSH.Client.App/Views/MainWindow.axaml.cs
index 7922dc6..08aa512 100644
--- a/src/DodoSSH.Client.App/Views/MainWindow.axaml.cs
+++ b/src/DodoSSH.Client.App/Views/MainWindow.axaml.cs
@@ -1,6 +1,7 @@
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.Input;
+using Avalonia.Platform;
using Avalonia.Threading;
using DodoSSH.Client.Shell.ViewModels;
@@ -38,6 +39,9 @@ internal sealed partial class MainWindow : Window
DataContextChanged += (_, _) => Attach(DataContext as MainWindowViewModel);
+ // Before anything navigates: the environment is settled once, when the adapter is built.
+ Terminal.EnvironmentRequested += OnTerminalEnvironmentRequested;
+
Terminal.WebMessageReceived += (_, e) =>
{
// Compared against a constant rather than parsed: the page sends exactly one message and
@@ -50,6 +54,38 @@ internal sealed partial class MainWindow : Window
};
}
+ ///
+ /// Asks the Linux backend for the one mode it can actually draw inside this window.
+ ///
+ ///
+ ///
+ /// Without this the terminal is blank on Linux, and blank in the most confusing way available:
+ /// the page loads, scripts run, the renderer connects — everything except pixels. Measured on Fedora 44
+ /// with Avalonia.Controls.WebView 12.0.1, where the backend is WebKitGTK 2.52.5 (WPE, the backend the
+ /// package's Linux notes describe, is not packaged for Fedora at all). In its default mode that adapter
+ /// reports SupportedScenarios = NativeDialog — it can open a window of its own and nothing else,
+ /// so a control asked to host it in place has nothing to show. Setting ExperimentalOffscreen
+ /// changes the same adapter's answer to OffscreenRenderer, which is the mode Avalonia's
+ /// compositor can draw.
+ ///
+ ///
+ /// Windows and macOS are untouched, and by construction rather than by an OS check: the argument is a
+ /// GTK type there and this method does nothing. WebView2 and WKWebView both host in place already.
+ ///
+ ///
+ /// Experimental is the vendor's word and worth repeating. If a future release makes the GTK
+ /// adapter host in place properly, this becomes unnecessary rather than wrong — and if the flag is
+ /// withdrawn, the terminal goes back to being blank on Linux, which is the thing to check first.
+ ///
+ ///
+ private static void OnTerminalEnvironmentRequested(object? sender, EventArgs e)
+ {
+ if (e is GtkWebViewEnvironmentRequestedEventArgs gtk)
+ {
+ gtk.ExperimentalOffscreen = true;
+ }
+ }
+
///
/// Where the keyboard belongs when the terminal is not holding it.
///