diff --git a/configs/releng/airootfs/usr/local/bin/fws-hello b/configs/releng/airootfs/usr/local/bin/fws-hello
new file mode 100755
index 0000000..c286925
--- /dev/null
+++ b/configs/releng/airootfs/usr/local/bin/fws-hello
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# FWS — fenêtre de bienvenue au premier démarrage (pendant post-install de la
+# page récap de l'installateur). Boutons : mise à jour, codecs, VR, raccourcis,
+# Proton-GE. GTK3 : présent sur TOUS les bureaux FWS (Firefox le tire) ;
+# python-gobject est installé avec le bureau par le kickstart.
+#
+# Lancement : /etc/xdg/autostart (KDE/GNOME/XFCE) et exec des configs
+# Hyprland/i3, toujours avec --autostart : on se retire sans bruit si
+# l'utilisateur a coché « ne plus afficher » (~/.config/fws/hello-done).
+
+import os
+import shutil
+import subprocess
+import sys
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk # noqa: E402
+
+_FLAG = os.path.expanduser("~/.config/fws/hello-done")
+
+_CODECS = ("gst-plugins-base gst-plugins-good gst-plugins-bad "
+ "gst-plugins-ugly gst-libav ffmpeg mpv")
+
+# (libellé, description, commande, en_terminal)
+_ACTIONS = [
+ ("Mettre à jour le système",
+ "pacman -Syu + nettoyage du cache",
+ "fws-update", True),
+ ("Installer les codecs multimédia",
+ "lecture vidéo/audio (GStreamer, ffmpeg, mpv)",
+ f"sudo pacman -S --needed {_CODECS}", True),
+ ("Proton-GE pour Steam",
+ "meilleure compatibilité des jeux Windows",
+ "fws-proton-ge", True),
+ ("Configurer la VR",
+ "drivers casque : HTC Vive, Index, Quest…",
+ "fws-vr-setup", True),
+]
+
+_TERMINALS = ["kitty", "konsole", "gnome-terminal", "xfce4-terminal", "xterm"]
+
+
+def _terminal():
+ for term in _TERMINALS:
+ if shutil.which(term):
+ return term
+ return None
+
+
+def _run(command, in_terminal):
+ if in_terminal:
+ term = _terminal()
+ if term is None:
+ return
+ if term == "gnome-terminal":
+ argv = [term, "--", "sh", "-c", command]
+ else:
+ argv = [term, "-e", "sh", "-c", command]
+ else:
+ argv = ["sh", "-c", command]
+ subprocess.Popen(argv) # noqa: S603 — commandes internes FWS
+
+
+class HelloWindow(Gtk.Window):
+ def __init__(self):
+ super().__init__(title="Bienvenue sur FWS")
+ self.set_default_size(460, -1)
+ self.set_position(Gtk.WindowPosition.CENTER)
+ self.set_border_width(18)
+
+ box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
+ self.add(box)
+
+ title = Gtk.Label()
+ title.set_markup("Bienvenue sur FWS !")
+ box.pack_start(title, False, False, 0)
+
+ subtitle = Gtk.Label(
+ label="Ton système est prêt. Quelques réglages utiles pour bien démarrer :")
+ subtitle.set_line_wrap(True)
+ subtitle.set_xalign(0)
+ box.pack_start(subtitle, False, False, 0)
+
+ actions = list(_ACTIONS)
+ # Raccourcis clavier : uniquement sur les sessions qui ont fws-keys
+ # (Hyprland/i3) — les autres bureaux ont leurs propres réglages.
+ session = os.environ.get("XDG_CURRENT_DESKTOP", "").lower()
+ if os.environ.get("HYPRLAND_INSTANCE_SIGNATURE") or "i3" in session:
+ actions.append(("Voir les raccourcis clavier",
+ "aussi disponible à tout moment : Super+K",
+ "fws-keys", False))
+
+ for label, desc, command, in_term in actions:
+ button = Gtk.Button()
+ inner = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
+ lab = Gtk.Label()
+ lab.set_markup(f"{label}")
+ lab.set_xalign(0)
+ sub = Gtk.Label(label=desc)
+ sub.set_xalign(0)
+ sub.get_style_context().add_class("dim-label")
+ inner.pack_start(lab, False, False, 0)
+ inner.pack_start(sub, False, False, 0)
+ button.add(inner)
+ button.connect("clicked",
+ lambda _b, c=command, t=in_term: _run(c, t))
+ box.pack_start(button, False, False, 0)
+
+ self._again = Gtk.CheckButton(label="Afficher cette fenêtre au prochain démarrage")
+ self._again.set_active(False)
+ box.pack_start(self._again, False, False, 6)
+
+ close = Gtk.Button(label="Fermer")
+ close.connect("clicked", lambda _b: self.close())
+ box.pack_start(close, False, False, 0)
+
+ self.connect("destroy", self._on_destroy)
+
+ def _on_destroy(self, _win):
+ if not self._again.get_active():
+ os.makedirs(os.path.dirname(_FLAG), exist_ok=True)
+ with open(_FLAG, "w") as fh:
+ fh.write("ok\n")
+ else:
+ try:
+ os.unlink(_FLAG)
+ except OSError:
+ pass
+ Gtk.main_quit()
+
+
+def main():
+ if "--autostart" in sys.argv and os.path.exists(_FLAG):
+ return 0
+ # Pas d'affichage joignable (lancement hors session graphique) : on se
+ # retire sans traceback plutôt que de crasher.
+ if not Gtk.init_check()[0]:
+ return 1
+ win = HelloWindow()
+ win.show_all()
+ Gtk.main()
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())