Compare commits
3 Commits
1bcc3d0f57
...
f28f090b06
| Author | SHA1 | Date |
|---|---|---|
|
|
f28f090b06 | |
|
|
35fcc5a576 | |
|
|
d89cc0290d |
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": "0.0.1",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "streamlit",
|
||||||
|
"runtimeExecutable": "venv/Scripts/python.exe",
|
||||||
|
"runtimeArgs": ["-m", "streamlit", "run", "main.py", "--server.headless=true", "--server.port=8501"],
|
||||||
|
"port": 8501
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
[server]
|
||||||
|
enableStaticServing = true
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
import streamlit.components.v1 as components
|
||||||
|
|
||||||
|
# Animationslaenge 16,4 s + ~1 s Puffer fuers "Unpacking" der Bundler-Animation.
|
||||||
|
INTRO_DURATION_MS = 17400
|
||||||
|
# Statische URL (Streamlit Static Serving). Beim Live-Test ggf. auf "./static/intro.html"
|
||||||
|
# oder "/static/intro.html" korrigieren, falls "app/static/..." 404 liefert.
|
||||||
|
_INTRO_SRC = "app/static/intro.html"
|
||||||
|
_SEEN_KEY = "einrechnung_intro_seen"
|
||||||
|
|
||||||
|
|
||||||
|
def render_intro_overlay(duration_ms: int = INTRO_DURATION_MS,
|
||||||
|
intro_src: str = _INTRO_SRC,
|
||||||
|
seen_key: str = _SEEN_KEY) -> None:
|
||||||
|
"""Injiziert ein einmaliges Intro-Overlay in die Streamlit-Elternseite (Ansatz A).
|
||||||
|
|
||||||
|
Ein unsichtbarer components.html-Baustein (Hoehe 0) fuehrt JS aus, das ein
|
||||||
|
position:fixed-Overlay in window.parent.document einfuegt - ein echtes Overlay ueber der
|
||||||
|
Seite. Autoplay nur einmal pro Browser (localStorage), "Ueberspringen"-Button + Auto-Fade,
|
||||||
|
Replay-Button oben rechts (aendert den "gesehen"-Merker nicht). Die Animation laeuft
|
||||||
|
isoliert in einem inneren iframe (eigenes CSS/JS, keine Kollision mit Streamlit).
|
||||||
|
"""
|
||||||
|
html = f"""
|
||||||
|
<script>
|
||||||
|
(function() {{
|
||||||
|
var doc = window.parent.document;
|
||||||
|
if (window.parent.__introInstalled) return; // Rerun-Schutz: nur einmal pro Seitenladen
|
||||||
|
window.parent.__introInstalled = true;
|
||||||
|
|
||||||
|
var DURATION = {duration_ms};
|
||||||
|
var SRC = {intro_src!r};
|
||||||
|
var SEEN_KEY = {seen_key!r};
|
||||||
|
|
||||||
|
var style = doc.createElement('style');
|
||||||
|
style.textContent = `
|
||||||
|
#eir-backdrop {{
|
||||||
|
position: fixed; inset: 0; z-index: 100000;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
background: rgba(8,8,10,0.82); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
|
||||||
|
opacity: 0; transition: opacity .45s ease; pointer-events: none;
|
||||||
|
}}
|
||||||
|
#eir-backdrop.eir-show {{ opacity: 1; pointer-events: auto; }}
|
||||||
|
#eir-panel {{
|
||||||
|
position: relative; width: min(50vw, 1100px); aspect-ratio: 16 / 9;
|
||||||
|
max-width: 94vw; max-height: 88vh;
|
||||||
|
background: #0a0a0a; border-radius: 16px; overflow: hidden;
|
||||||
|
box-shadow: 0 24px 80px rgba(0,0,0,0.6), 0 0 0 1px rgba(245,126,26,0.25);
|
||||||
|
}}
|
||||||
|
@media (max-width: 820px) {{ #eir-panel {{ width: 92vw; }} }}
|
||||||
|
#eir-frame {{ width: 100%; height: 100%; border: 0; display: block; background: #0a0a0a; }}
|
||||||
|
#eir-skip {{
|
||||||
|
position: absolute; top: 10px; right: 12px; z-index: 2;
|
||||||
|
font: 600 13px/1 -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
|
color: #eee; background: rgba(0,0,0,0.45); border: 1px solid rgba(255,255,255,0.18);
|
||||||
|
padding: 7px 12px; border-radius: 999px; cursor: pointer; user-select: none;
|
||||||
|
}}
|
||||||
|
#eir-skip:hover {{ background: rgba(0,0,0,0.7); }}
|
||||||
|
#eir-replay {{
|
||||||
|
position: fixed; top: 14px; right: 16px; z-index: 99999;
|
||||||
|
width: 40px; height: 40px; border-radius: 50%;
|
||||||
|
display: flex; align-items: center; justify-content: center; cursor: pointer;
|
||||||
|
background: #17171a; border: 1px solid rgba(245,126,26,0.55);
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.35);
|
||||||
|
}}
|
||||||
|
#eir-replay:hover {{ background: #202024; }}
|
||||||
|
#eir-replay svg {{ width: 16px; height: 16px; }}
|
||||||
|
`;
|
||||||
|
doc.head.appendChild(style);
|
||||||
|
|
||||||
|
var replay = doc.createElement('div');
|
||||||
|
replay.id = 'eir-replay';
|
||||||
|
replay.title = 'Intro erneut abspielen';
|
||||||
|
replay.innerHTML = '<svg viewBox="0 0 24 24" fill="#F57E1A"><path d="M8 5v14l11-7z"/></svg>';
|
||||||
|
doc.body.appendChild(replay);
|
||||||
|
|
||||||
|
var backdrop = doc.createElement('div');
|
||||||
|
backdrop.id = 'eir-backdrop';
|
||||||
|
backdrop.innerHTML =
|
||||||
|
'<div id="eir-panel">' +
|
||||||
|
'<div id="eir-skip">Überspringen ✕</div>' +
|
||||||
|
'<iframe id="eir-frame" title="Intro-Animation"></iframe>' +
|
||||||
|
'</div>';
|
||||||
|
doc.body.appendChild(backdrop);
|
||||||
|
|
||||||
|
var frame = doc.getElementById('eir-frame');
|
||||||
|
var skip = doc.getElementById('eir-skip');
|
||||||
|
var hideTimer = null;
|
||||||
|
|
||||||
|
// Der Bundler-Export bringt eine eigene Player-Leiste mit (Play/Pause, Zeitleiste,
|
||||||
|
// "Export video"). Fuer ein sauberes Intro blenden wir sie aus. Die Leiste ist das
|
||||||
|
// Eltern-Element der Transport-Buttons und wird evtl. asynchron aufgebaut -> Retry.
|
||||||
|
function stripChrome() {{
|
||||||
|
var idoc;
|
||||||
|
try {{ idoc = frame.contentDocument; }} catch (e) {{ return; }}
|
||||||
|
if (!idoc || (frame.src || '').indexOf('about:blank') !== -1) return;
|
||||||
|
var tries = 0;
|
||||||
|
(function attempt() {{
|
||||||
|
var b;
|
||||||
|
try {{ b = idoc.querySelector('button[aria-label*="lay"], button[title*="lay"], button'); }} catch (e) {{ return; }}
|
||||||
|
if (b && b.parentElement) {{ b.parentElement.style.display = 'none'; return; }}
|
||||||
|
if (tries++ < 40) setTimeout(attempt, 150); // bis zu ~6 s auf asynchrone Player-UI warten
|
||||||
|
}})();
|
||||||
|
}}
|
||||||
|
frame.addEventListener('load', stripChrome);
|
||||||
|
|
||||||
|
function hide() {{
|
||||||
|
clearTimeout(hideTimer);
|
||||||
|
backdrop.classList.remove('eir-show');
|
||||||
|
setTimeout(function() {{ frame.src = 'about:blank'; }}, 500);
|
||||||
|
}}
|
||||||
|
|
||||||
|
function show() {{
|
||||||
|
frame.src = 'about:blank';
|
||||||
|
requestAnimationFrame(function() {{ frame.src = SRC; }}); // aus Cache -> Neustart von vorn
|
||||||
|
backdrop.classList.add('eir-show');
|
||||||
|
clearTimeout(hideTimer);
|
||||||
|
hideTimer = setTimeout(hide, DURATION);
|
||||||
|
}}
|
||||||
|
|
||||||
|
skip.addEventListener('click', hide);
|
||||||
|
backdrop.addEventListener('click', function(e) {{ if (e.target === backdrop) hide(); }});
|
||||||
|
replay.addEventListener('click', show);
|
||||||
|
|
||||||
|
var seen;
|
||||||
|
try {{ seen = window.parent.localStorage.getItem(SEEN_KEY); }} catch (e) {{ seen = null; }}
|
||||||
|
if (seen !== '1') {{
|
||||||
|
try {{ window.parent.localStorage.setItem(SEEN_KEY, '1'); }} catch (e) {{}}
|
||||||
|
show();
|
||||||
|
}}
|
||||||
|
}})();
|
||||||
|
</script>
|
||||||
|
"""
|
||||||
|
components.html(html, height=0)
|
||||||
2
main.py
2
main.py
|
|
@ -35,6 +35,7 @@ from invoice_logic import (
|
||||||
extract_referenznummer,
|
extract_referenznummer,
|
||||||
extract_header_metadata_pure_python,
|
extract_header_metadata_pure_python,
|
||||||
)
|
)
|
||||||
|
from intro_overlay import render_intro_overlay
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 0. SITZUNGS-STATUS INITIALISIEREN (ZÄHLER GEGEN WIDGET-CACHING)
|
# 0. SITZUNGS-STATUS INITIALISIEREN (ZÄHLER GEGEN WIDGET-CACHING)
|
||||||
|
|
@ -99,6 +100,7 @@ _FAVICON_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets
|
||||||
_page_icon = Image.open(_FAVICON_PATH) if os.path.exists(_FAVICON_PATH) else "🧾"
|
_page_icon = Image.open(_FAVICON_PATH) if os.path.exists(_FAVICON_PATH) else "🧾"
|
||||||
st.set_page_config(page_title="PDF zu E-Rechnung", page_icon=_page_icon, layout="wide")
|
st.set_page_config(page_title="PDF zu E-Rechnung", page_icon=_page_icon, layout="wide")
|
||||||
st.title("📄 PDF zu E-Rechnung Konverter")
|
st.title("📄 PDF zu E-Rechnung Konverter")
|
||||||
|
render_intro_overlay()
|
||||||
|
|
||||||
col1, col2 = st.columns(2)
|
col1, col2 = st.columns(2)
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue