diff --git a/apps/studio/assets/dmg-background.svg b/apps/studio/assets/dmg-background.svg
new file mode 100644
index 0000000..8ceefd0
--- /dev/null
+++ b/apps/studio/assets/dmg-background.svg
@@ -0,0 +1,31 @@
+
diff --git a/apps/studio/assets/dmg-background.tiff b/apps/studio/assets/dmg-background.tiff
new file mode 100644
index 0000000..182a97a
Binary files /dev/null and b/apps/studio/assets/dmg-background.tiff differ
diff --git a/apps/studio/assets/icon-1024.png b/apps/studio/assets/icon-1024.png
index c46a8df..35acec5 100644
Binary files a/apps/studio/assets/icon-1024.png and b/apps/studio/assets/icon-1024.png differ
diff --git a/apps/studio/assets/icon-256.png b/apps/studio/assets/icon-256.png
index b243609..a68b111 100644
Binary files a/apps/studio/assets/icon-256.png and b/apps/studio/assets/icon-256.png differ
diff --git a/apps/studio/assets/icon-512.png b/apps/studio/assets/icon-512.png
index 75256f7..bf80225 100644
Binary files a/apps/studio/assets/icon-512.png and b/apps/studio/assets/icon-512.png differ
diff --git a/apps/studio/assets/icon-tile-256.png b/apps/studio/assets/icon-tile-256.png
new file mode 100644
index 0000000..c046985
Binary files /dev/null and b/apps/studio/assets/icon-tile-256.png differ
diff --git a/apps/studio/assets/icon.icns b/apps/studio/assets/icon.icns
index 6cb926e..c0f7407 100644
Binary files a/apps/studio/assets/icon.icns and b/apps/studio/assets/icon.icns differ
diff --git a/apps/studio/assets/icon.ico b/apps/studio/assets/icon.ico
index 7a5adf8..b9af402 100644
Binary files a/apps/studio/assets/icon.ico and b/apps/studio/assets/icon.ico differ
diff --git a/apps/studio/assets/icon.svg b/apps/studio/assets/icon.svg
index 26d4625..fab7b73 100644
--- a/apps/studio/assets/icon.svg
+++ b/apps/studio/assets/icon.svg
@@ -23,8 +23,9 @@
-
-
+
+
+
diff --git a/apps/studio/assets/installer/small-110.bmp b/apps/studio/assets/installer/small-110.bmp
index 0fc59a4..db3bfea 100644
Binary files a/apps/studio/assets/installer/small-110.bmp and b/apps/studio/assets/installer/small-110.bmp differ
diff --git a/apps/studio/assets/installer/small-55.bmp b/apps/studio/assets/installer/small-55.bmp
index c54d2ec..2cd04b6 100644
Binary files a/apps/studio/assets/installer/small-55.bmp and b/apps/studio/assets/installer/small-55.bmp differ
diff --git a/apps/studio/assets/installer/small-83.bmp b/apps/studio/assets/installer/small-83.bmp
index 7c6f1ca..61fa09d 100644
Binary files a/apps/studio/assets/installer/small-83.bmp and b/apps/studio/assets/installer/small-83.bmp differ
diff --git a/apps/studio/assets/installer/wizard-164.bmp b/apps/studio/assets/installer/wizard-164.bmp
index 37f0dcc..a3a7687 100644
Binary files a/apps/studio/assets/installer/wizard-164.bmp and b/apps/studio/assets/installer/wizard-164.bmp differ
diff --git a/apps/studio/assets/installer/wizard-246.bmp b/apps/studio/assets/installer/wizard-246.bmp
index e2ae8d6..9dcb450 100644
Binary files a/apps/studio/assets/installer/wizard-246.bmp and b/apps/studio/assets/installer/wizard-246.bmp differ
diff --git a/apps/studio/assets/installer/wizard-328.bmp b/apps/studio/assets/installer/wizard-328.bmp
index 0528f26..c68e9c0 100644
Binary files a/apps/studio/assets/installer/wizard-328.bmp and b/apps/studio/assets/installer/wizard-328.bmp differ
diff --git a/apps/studio/src/main.rs b/apps/studio/src/main.rs
index b747145..abe6972 100644
--- a/apps/studio/src/main.rs
+++ b/apps/studio/src/main.rs
@@ -52,6 +52,13 @@ fn main() -> eframe::Result {
sf_recording::clock::init();
tracing_subscriber::fmt().with_env_filter(tracing_subscriber::EnvFilter::from_default_env()).init();
open_docs::install();
+ // macOS: an empty icon keeps the bundle's icon.icns in the Dock; setting one would swap it for a
+ // single 256 px raster. Windows uses the shadowless tile that icon.ico is built from.
+ #[cfg(target_os = "macos")]
+ let icon = egui::IconData::default();
+ #[cfg(target_os = "windows")]
+ let icon = eframe::icon_data::from_png_bytes(include_bytes!("../assets/icon-tile-256.png")).expect("app icon");
+ #[cfg(not(any(target_os = "macos", target_os = "windows")))]
let icon = eframe::icon_data::from_png_bytes(include_bytes!("../assets/icon-256.png")).expect("app icon");
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
diff --git a/apps/studio/src/tray.rs b/apps/studio/src/tray.rs
index 51e4d2c..6fe4fdf 100644
--- a/apps/studio/src/tray.rs
+++ b/apps/studio/src/tray.rs
@@ -83,7 +83,7 @@ mod windows_tray {
use tray_icon::{Icon, TrayIcon, TrayIconBuilder};
pub fn create(menu: &muda::Menu) -> Option {
- let img = image::load_from_memory(include_bytes!("../assets/icon-256.png")).ok()?.resize_exact(32, 32, image::imageops::FilterType::Lanczos3).to_rgba8();
+ let img = image::load_from_memory(include_bytes!("../assets/icon-tile-256.png")).ok()?.resize_exact(32, 32, image::imageops::FilterType::Lanczos3).to_rgba8();
let (w, h) = img.dimensions();
let icon = Icon::from_rgba(img.into_raw(), w, h).ok()?;
TrayIconBuilder::new().with_menu(Box::new(menu.clone())).with_tooltip("ScreenForge").with_icon(icon).build().ok()
diff --git a/scripts/dmg.sh b/scripts/dmg.sh
index 50d2463..5dc5878 100755
--- a/scripts/dmg.sh
+++ b/scripts/dmg.sh
@@ -8,6 +8,54 @@ STAGE=target/dmg-stage
rm -rf "$STAGE" "$OUT"; mkdir -p "$STAGE"
cp -R target/ScreenForge.app "$STAGE/"
ln -s /Applications "$STAGE/Applications"
-hdiutil create -volname "ScreenForge" -srcfolder "$STAGE" -ov -format UDZO "$OUT" >/dev/null
+RW=target/dmg-rw.dmg
+rm -f "$RW"
+hdiutil create -volname "ScreenForge" -srcfolder "$STAGE" -ov -format UDRW "$RW" >/dev/null
+
+# window layout (background, icon positions) is stored in the volume's .DS_Store, which only Finder
+# writes, so mount where Finder can see it and script it. A failure here (no Finder session) leaves
+# a plain but working DMG.
+VOL=/Volumes/ScreenForge
+[ -d "$VOL" ] && hdiutil detach "$VOL" -force >/dev/null 2>&1 || true
+hdiutil attach "$RW" -noautoopen >/dev/null
+mkdir "$VOL/.background" && cp apps/studio/assets/dmg-background.tiff "$VOL/.background/background.tiff"
+# Finder needs a moment to see a freshly attached disk
+LAYOUT=$(cat <<'OSA'
+tell application "Finder"
+ tell disk "ScreenForge"
+ open
+ set current view of container window to icon view
+ set toolbar visible of container window to false
+ set statusbar visible of container window to false
+ set bounds of container window to {200, 120, 760, 508}
+ set opts to icon view options of container window
+ set arrangement of opts to not arranged
+ set icon size of opts to 112
+ set text size of opts to 12
+ set background picture of opts to file ".background:background.tiff"
+ set position of item "ScreenForge.app" to {150, 190}
+ set position of item "Applications" to {410, 190}
+ close
+ open
+ update without registering applications
+ delay 1
+ close
+ end tell
+end tell
+OSA
+)
+for try in 1 2 3 4 5; do
+ if osascript -e "$LAYOUT" >/dev/null 2>&1; then break; fi
+ [ $try = 5 ] && echo "warning: could not lay out the DMG window" >&2 || sleep 2
+done
+# volume icon: .VolumeIcon.icns plus the custom-icon flag on the root. Done after the Finder step,
+# because Finder removes both when it saves the window layout.
+cp apps/studio/assets/icon.icns "$VOL/.VolumeIcon.icns"
+SetFile -a V "$VOL/.VolumeIcon.icns"
+SetFile -a C "$VOL"
+sync
+hdiutil detach "$VOL" >/dev/null || { sleep 2; hdiutil detach "$VOL" -force >/dev/null; }
+hdiutil convert "$RW" -format UDZO -o "$OUT" >/dev/null
+rm -f "$RW"
if [ -n "$CODESIGN_IDENTITY" ]; then codesign --force --timestamp --sign "$CODESIGN_IDENTITY" "$OUT"; fi
echo "$OUT"
diff --git a/scripts/gen-icons.sh b/scripts/gen-icons.sh
new file mode 100755
index 0000000..1bdf3f3
--- /dev/null
+++ b/scripts/gen-icons.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+# Render every app icon asset from apps/studio/assets/icon.svg (needs rsvg-convert and ImageMagick).
+# icon-{256,512,1024}.png macOS-style tile with margin and drop shadow
+# icon.icns each size rendered from the vector, not downscaled from 1024
+# icon-tile-256.png Windows window/tray icon (same tile as icon.ico)
+# icon.ico Windows: no drop shadow, tile fills the frame, 16..256 px frames
+# installer/*.bmp Inno Setup wizard panel and header images
+# dmg-background.tiff DMG window background, 1x + 2x (from dmg-background.svg)
+set -e
+cd "$(dirname "$0")/../apps/studio/assets"
+TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
+
+render() { rsvg-convert -w "$2" -h "$2" "$1" -o "$3"; }
+
+# Windows/installer variant: drop the shadow and crop to the tile (small margin for antialiasing)
+sed -e '/filter="url(#shadow)"/d' -e 's/viewBox="0 0 1024 1024"/viewBox="88 88 848 848"/' icon.svg > "$TMP/tile.svg"
+
+for s in 256 512 1024; do render icon.svg $s icon-$s.png; done
+
+mkdir "$TMP/icon.iconset"
+for s in 16 32 128 256 512; do
+ render icon.svg $s "$TMP/icon.iconset/icon_${s}x${s}.png"
+ render icon.svg $((s * 2)) "$TMP/icon.iconset/icon_${s}x${s}@2x.png"
+done
+iconutil -c icns "$TMP/icon.iconset" -o icon.icns
+
+ICO=""
+for s in 16 20 24 32 40 48 64 96 128 256; do render "$TMP/tile.svg" $s "$TMP/ico-$s.png"; ICO="$ICO $TMP/ico-$s.png"; done
+# shellcheck disable=SC2086
+magick $ICO icon.ico
+cp "$TMP/ico-256.png" icon-tile-256.png
+
+# Inno Setup: 24-bit BMPs. Wizard panel = dark backdrop with the tile; header = tile on white.
+render "$TMP/tile.svg" 1024 "$TMP/tile-1024.png"
+for wh in 164x314 246x459 328x628; do
+ w=${wh%x*}; h=${wh#*x}; t=$((w * 46 / 100))
+ magick -size ${w}x${h} 'xc:rgb(23,22,37)' \( "$TMP/tile-1024.png" -resize ${t}x${t} \) -gravity center -composite \
+ -type TrueColor BMP3:installer/wizard-$w.bmp
+done
+for w in 55 83 110; do
+ t=$((w * 84 / 100))
+ magick -size ${w}x${w} xc:white \( "$TMP/tile-1024.png" -resize ${t}x${t} \) -gravity center -composite \
+ -type TrueColor BMP3:installer/small-$w.bmp
+done
+rsvg-convert -w 560 -h 360 dmg-background.svg -o "$TMP/bg.png"
+rsvg-convert -w 1120 -h 720 dmg-background.svg -o "$TMP/bg@2x.png"
+tiffutil -cathidpicheck "$TMP/bg.png" "$TMP/bg@2x.png" -out dmg-background.tiff >/dev/null 2>&1
+echo "icons regenerated in $(pwd)"