diff --git a/rhino/_startup_splash.py b/rhino/_startup_splash.py
new file mode 100644
index 0000000..8bc4c7b
--- /dev/null
+++ b/rhino/_startup_splash.py
@@ -0,0 +1,156 @@
+#! python 3
+# -*- coding: utf-8 -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# Copyright (C) 2026 Karim Gabriele Varano
+"""
+_startup_splash.py
+Petrol-grüner Splash-Screen waehrend des DOSSIER-Plugin-Startups.
+Borderless Eto-Form mit WebView + Inline-HTML im selben Stil wie der
+Launcher-Splash. Bedeckt visuell die 3+ Sekunden waehrend Rhino die
+Panels registriert + WindowLayout neu anwendet.
+
+Wird von startup.py beim ersten Idle gezeigt und nach Layout-Apply
+(oder Timeout) wieder versteckt.
+"""
+import scriptcontext as sc
+
+_SPLASH_KEY = "_dossier_startup_splash"
+_SAFETY_TIMEOUT_SEC = 8.0 # spaetestens nach 8s wegmachen, falls Hide-Hook nicht feuert
+
+
+_SPLASH_HTML = '''
+
Dossier laedt
+
+
+
+
+
+
+
DOSSIER.
+
Rhino 8 Plugin
+
+
+
+
+ Plugin laedt — Panels werden platziert
+
+
+
+
+ AGPL-3.0 · Karim Gabriele Varano
+ CPython 3.9
+
+
+'''
+
+
+def show():
+ """Zeigt den Splash. Idempotent — zweiter Aufruf bringt das bestehende
+ Fenster nur in den Vordergrund. Auto-Hide nach _SAFETY_TIMEOUT_SEC
+ als Fallback falls hide() vergessen wird."""
+ if sc.sticky.get(_SPLASH_KEY) is not None:
+ return
+ try:
+ import Eto.Forms as ef
+ import Eto.Drawing as ed
+ form = ef.Form()
+ form.Title = "Dossier"
+ # WindowStyle.None — "None" ist Python-keyword, daher via getattr
+ try: form.WindowStyle = getattr(ef.WindowStyle, "None")
+ except Exception: pass
+ try: form.Resizable = False
+ except Exception: pass
+ try: form.Topmost = True
+ except Exception: pass
+ try: form.ShowInTaskbar = False
+ except Exception: pass
+ try: form.Size = ed.Size(420, 160)
+ except Exception: pass
+ try:
+ # Hintergrund weiss, das WebView-content hat seine eigene
+ # gerundete Petrol-Box — Form muss nur kein graues Border zeigen
+ form.BackgroundColor = ed.Color(0.18, 0.50, 0.45)
+ except Exception: pass
+ wv = ef.WebView()
+ try:
+ wv.LoadHtml(_SPLASH_HTML)
+ except Exception as ex:
+ print("[SPLASH] LoadHtml:", ex)
+ form.Content = wv
+ try:
+ # Center on screen
+ screen = ef.Screen.PrimaryScreen
+ sb = screen.Bounds
+ x = int(sb.X + (sb.Width - form.Size.Width) / 2)
+ y = int(sb.Y + (sb.Height - form.Size.Height) / 2 - 100)
+ form.Location = ed.Point(x, y)
+ except Exception as ex:
+ print("[SPLASH] center:", ex)
+ try: form.Show()
+ except Exception as ex:
+ print("[SPLASH] Show:", ex); return
+ sc.sticky[_SPLASH_KEY] = form
+ # Safety-Timeout — wenn nach 8s niemand hide() ruft, automatisch weg
+ try:
+ import threading
+ def _auto_hide():
+ try: hide()
+ except Exception: pass
+ threading.Timer(_SAFETY_TIMEOUT_SEC, _auto_hide).start()
+ except Exception: pass
+ except Exception as ex:
+ print("[SPLASH] show:", ex)
+
+
+def hide():
+ """Versteckt + entsorgt den Splash. Idempotent."""
+ form = sc.sticky.get(_SPLASH_KEY)
+ if form is None:
+ return
+ try:
+ sc.sticky[_SPLASH_KEY] = None
+ try: form.Close()
+ except Exception:
+ try: form.Visible = False
+ except Exception: pass
+ except Exception as ex:
+ print("[SPLASH] hide:", ex)
diff --git a/rhino/oberleiste.py b/rhino/oberleiste.py
index ee3f4ec..e317519 100644
--- a/rhino/oberleiste.py
+++ b/rhino/oberleiste.py
@@ -1380,6 +1380,15 @@ class OberleisteBridge(panel_base.BaseBridge):
sc.sticky["_dossier_view_colors_applied"] = True
except Exception as ex:
print("[OBERLEISTE] auto-apply (layout/colors):", ex)
+ # Splash-Screen (falls noch offen) jetzt wegmachen — Layout-Apply ist
+ # durch, Panels sind in finaler Position. Lazy via Timer 200ms damit
+ # die Layout-Animation kurz auf den finalen Panels sichtbar wird.
+ try:
+ import threading
+ import _startup_splash as _ss
+ threading.Timer(0.2, _ss.hide).start()
+ except Exception as ex:
+ print("[OBERLEISTE] splash hide:", ex)
self._send_state(force=True)
def handle(self, data):
diff --git a/rhino/startup.py b/rhino/startup.py
index eb38489..b3bfda4 100644
--- a/rhino/startup.py
+++ b/rhino/startup.py
@@ -240,6 +240,13 @@ def _load_all(sender, e):
Rhino.RhinoApp.Idle -= _load_all
except Exception:
pass
+ # Splash zeigen bevor irgendwas laeuft — verdeckt visuell die ~3s
+ # Panel-Init + WindowLayout-Apply
+ try:
+ import _startup_splash
+ _startup_splash.show()
+ except Exception as ex:
+ print("[STARTUP] splash show:", ex)
print("[STARTUP] Lade DOSSIER-Panels...")
# Migration einmal fuer das beim Start aktive Doc
_migrate_active_doc()