Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions desktop/scripts/fix-appimage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,84 @@ echo "==> Symlinking system GStreamer plugin directory"
rm -rf "$LIBDIR/gstreamer-1.0"
ln -s "/usr/lib/$MULTIARCH/gstreamer-1.0" "$LIBDIR/gstreamer-1.0"

# WebKitGTK 2.52 Skia's GPU path goes through Vulkan/radv. On non-conformant
# AMD RDNA4 (gfx1200) that path paints nothing — transparent ghost window —
# while WEBKIT_DISABLE_DMABUF_RENDERER does not help (#2643). Prefer Skia CPU
# raster for AppImage launches; operators can override with =0.
echo "==> Preferring WebKitGTK Skia CPU rendering for AppImage launches"
HOOK_DIR="$WORKDIR/squashfs-root/apprun-hooks"
mkdir -p "$HOOK_DIR"
cat > "$HOOK_DIR/99-buzz-webkit-skia-cpu.sh" <<'EOF'
# Allow operators to override (e.g. WEBKIT_SKIA_ENABLE_CPU_RENDERING=0) if needed.
export WEBKIT_SKIA_ENABLE_CPU_RENDERING="${WEBKIT_SKIA_ENABLE_CPU_RENDERING:-1}"
EOF
if [[ -f "$WORKDIR/squashfs-root/AppRun" ]] \
&& ! grep -q 'WEBKIT_SKIA_ENABLE_CPU_RENDERING' "$WORKDIR/squashfs-root/AppRun"; then
if ! grep -q 'apprun-hooks' "$WORKDIR/squashfs-root/AppRun"; then
tmp_apprun="$(mktemp)"
{
if head -n1 "$WORKDIR/squashfs-root/AppRun" | grep -q '^#!'; then
head -n1 "$WORKDIR/squashfs-root/AppRun"
echo 'export WEBKIT_SKIA_ENABLE_CPU_RENDERING="${WEBKIT_SKIA_ENABLE_CPU_RENDERING:-1}"'
tail -n +2 "$WORKDIR/squashfs-root/AppRun"
else
echo 'export WEBKIT_SKIA_ENABLE_CPU_RENDERING="${WEBKIT_SKIA_ENABLE_CPU_RENDERING:-1}"'
cat "$WORKDIR/squashfs-root/AppRun"
fi
} > "$tmp_apprun"
mv "$tmp_apprun" "$WORKDIR/squashfs-root/AppRun"
chmod +x "$WORKDIR/squashfs-root/AppRun"
fi
fi

# Fedora's COLRv1 Noto Color Emoji trips an assertion in WebKitGTK/Skia and
# aborts the AppImage after a blank window (#2548). Reject that family so
# emoji fall back to a non-COLRv1 face (or to monochrome glyphs).
echo "==> Rejecting COLRv1 system color-emoji fonts for AppImage launches"
FC_DIR="$WORKDIR/squashfs-root/usr/etc/fonts"
mkdir -p "$FC_DIR"
cat > "$FC_DIR/fonts.conf" <<'EOF'
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
<!-- Keep the host fontconfig, then drop COLRv1 color-emoji faces that crash
bundled WebKitGTK/Skia on Fedora (#2548). -->
<include ignore_missing="yes">/etc/fonts/fonts.conf</include>
<selectfont>
<rejectfont>
<pattern>
<patelt name="family"><string>Noto Color Emoji</string></patelt>
</pattern>
</rejectfont>
</selectfont>
</fontconfig>
EOF
cat > "$HOOK_DIR/98-buzz-fontconfig-no-colrv1.sh" <<'EOF'
# Prefer the AppImage fontconfig that rejects COLRv1 Noto Color Emoji (#2548).
# Operators can point FONTCONFIG_FILE elsewhere to override.
if [ -z "${FONTCONFIG_FILE:-}" ] && [ -n "${APPDIR:-}" ] && [ -f "$APPDIR/usr/etc/fonts/fonts.conf" ]; then
export FONTCONFIG_FILE="$APPDIR/usr/etc/fonts/fonts.conf"
fi
EOF
if [[ -f "$WORKDIR/squashfs-root/AppRun" ]] \
&& ! grep -q 'buzz-fontconfig-no-colrv1\|FONTCONFIG_FILE=.*usr/etc/fonts' "$WORKDIR/squashfs-root/AppRun"; then
if ! grep -q 'apprun-hooks' "$WORKDIR/squashfs-root/AppRun"; then
tmp_apprun="$(mktemp)"
{
if head -n1 "$WORKDIR/squashfs-root/AppRun" | grep -q '^#!'; then
head -n1 "$WORKDIR/squashfs-root/AppRun"
echo 'if [ -z "${FONTCONFIG_FILE:-}" ] && [ -n "${APPDIR:-}" ] && [ -f "$APPDIR/usr/etc/fonts/fonts.conf" ]; then export FONTCONFIG_FILE="$APPDIR/usr/etc/fonts/fonts.conf"; fi'
tail -n +2 "$WORKDIR/squashfs-root/AppRun"
else
echo 'if [ -z "${FONTCONFIG_FILE:-}" ] && [ -n "${APPDIR:-}" ] && [ -f "$APPDIR/usr/etc/fonts/fonts.conf" ]; then export FONTCONFIG_FILE="$APPDIR/usr/etc/fonts/fonts.conf"; fi'
cat "$WORKDIR/squashfs-root/AppRun"
fi
} > "$tmp_apprun"
mv "$tmp_apprun" "$WORKDIR/squashfs-root/AppRun"
chmod +x "$WORKDIR/squashfs-root/AppRun"
fi
fi

echo "==> Repacking AppImage"
# Pass a pinned type2 runtime when provided (CI sets APPIMAGETOOL_RUNTIME_FILE);
# without it appimagetool downloads the runtime from its mutable `continuous`
Expand Down
8 changes: 8 additions & 0 deletions desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ async fn wait_for_stable_initial_window_geometry<R: tauri::Runtime>(window: &tau

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
// WebKitGTK 2.52 Skia GPU/Vulkan paints nothing on non-conformant AMD RDNA4
// (radv gfx1200): transparent ghost window (#2643). Prefer CPU raster when
// unset; operators can force GPU with WEBKIT_SKIA_ENABLE_CPU_RENDERING=0.
#[cfg(target_os = "linux")]
if std::env::var_os("WEBKIT_SKIA_ENABLE_CPU_RENDERING").is_none() {
std::env::set_var("WEBKIT_SKIA_ENABLE_CPU_RENDERING", "1");
}

// mesh-llm's async chains (model download, node start/join) overflow
// tokio's default 2 MiB worker stacks — a stack-guard SIGABRT, not a
// panic. Upstream mesh-llm and mesh-console both run on 8 MiB worker
Expand Down
48 changes: 47 additions & 1 deletion desktop/src-tauri/src/managed_agents/git_bash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,9 @@ fn scan_path_for_command(
) -> Option<PathBuf> {
let needs_exe = name.extension().is_none();
std::env::split_paths(path_env).find_map(|dir| {
if system_root.is_some_and(|root| is_under_dir(&dir, root)) {
// System32 bash and %LOCALAPPDATA%\Microsoft\WindowsApps\bash.exe are
// WSL app-execution aliases, not Git Bash (#2685).
if system_root.is_some_and(|root| is_under_dir(&dir, root)) || is_windows_apps_dir(&dir) {
return None;
}
let candidate = dir.join(name);
Expand All @@ -320,6 +322,19 @@ fn scan_path_for_command(
})
}

/// Windows exposes `bash.exe` app-execution aliases under WindowsApps that
/// launch WSL rather than Git Bash. Skip those PATH entries so discovery does
/// not boot `wsl.exe` on every unresolved command probe.
#[cfg(windows)]
fn is_windows_apps_dir(dir: &Path) -> bool {
dir.components().any(|component| {
component
.as_os_str()
.to_string_lossy()
.eq_ignore_ascii_case("WindowsApps")
})
}

#[cfg(windows)]
fn is_under_dir(dir: &Path, root: &Path) -> bool {
let mut dir_components = dir.components();
Expand Down Expand Up @@ -501,6 +516,37 @@ mod tests {
);
}

#[test]
fn test_windows_apps_bash_alias_is_not_treated_as_git_bash() {
let temp = tempdir().expect("tempdir");
let windows_apps = temp.path().join("Microsoft").join("WindowsApps");
let alias = windows_apps.join("bash.exe");
let git = temp.path().join("Git").join("cmd").join("git.exe");
let bash = temp.path().join("Git").join("bin").join("bash.exe");
std::fs::create_dir_all(&windows_apps).expect("mkdir WindowsApps");
std::fs::create_dir_all(git.parent().expect("git parent")).expect("mkdir git");
std::fs::create_dir_all(bash.parent().expect("bash parent")).expect("mkdir bash");
std::fs::write(alias, []).expect("alias");
std::fs::write(&git, []).expect("git");
std::fs::write(&bash, []).expect("bash");

let path =
std::env::join_paths([windows_apps, git.parent().expect("cmd dir").to_path_buf()])
.expect("PATH");
assert_eq!(
resolve_git_bash_no_registry(
path.to_str().expect("utf8"),
None,
None,
None,
None,
None,
None,
),
Some(bash)
);
}

#[test]
fn test_effective_buzz_shell_override_marks_agent_ready() {
let temp = tempdir().expect("tempdir");
Expand Down
Loading