@@ -21,32 +21,109 @@ const linesEN = [
2121
2222const lines = lang === " pl" ? linesPL : linesEN ;
2323---
24+
2425<div class =" card" style =" padding: 14px 14px 12px;" >
2526 <div style =" display:flex; gap:8px; align-items:center; margin-bottom: 10px;" >
2627 <span style =" width:10px;height:10px;border-radius:999px;background:rgba(255,95,86,.9)" ></span >
2728 <span style =" width:10px;height:10px;border-radius:999px;background:rgba(255,189,46,.9)" ></span >
2829 <span style =" width:10px;height:10px;border-radius:999px;background:rgba(39,201,63,.9)" ></span >
29- <span style =" margin-left:8px; color: rgba(255,255,255,.5); font-family: var(--mono); font-size: 12px;" >deploy.log</span >
30+ <span style =" margin-left:8px; color: rgba(255,255,255,.5); font-family: var(--mono); font-size: 12px;" >
31+ deploy.log
32+ </span >
3033 </div >
3134
32- <pre id =" term" style =" margin:0; font-family: var(--mono); font-size: 13px; color: rgba(255,255,255,.78); white-space: pre-wrap;" ></pre >
35+ <pre
36+ id =" term"
37+ style ="
38+ margin:0;
39+ font-family: var(--mono);
40+ font-size: 13px;
41+ color: rgba(255,255,255,.78);
42+ white-space: pre-wrap;
43+
44+ line-height: var(--term-lh);
45+
46+ min-height: calc((var(--term-lines) * var(--term-lh) + 0.6) * 1em);
47+ max-height: calc((var(--term-lines) * var(--term-lh) + 0.6) * 1em);
3348
34- <script define:vars ={ { lines }} >
49+ overflow: hidden;
50+ "
51+ ></pre >
52+
53+ <script define:vars ={ { termLines: lines }} >
3554 const el = document.getElementById("term");
3655
37- const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
38- if (reduce) {
39- el.textContent = lines.join("\n");
40- } else {
56+ // ustawiamy liczbę linii jako CSS variable do wyliczenia wysokości
57+ el.style.setProperty("--term-lines", String(termLines.length));
58+
59+ const reduce =
60+ window.matchMedia &&
61+ window.matchMedia("(prefers-reduced-motion: reduce)").matches;
62+
63+ // zabezpieczenie przed odpaleniem kilka razy
64+ let started = false;
65+
66+ const renderAll = () => {
67+ el.textContent = termLines.join("\n");
68+ };
69+
70+ const startTyping = () => {
71+ if (started) return;
72+ started = true;
73+
74+ if (reduce) {
75+ renderAll();
76+ return;
77+ }
78+
4179 let i = 0;
4280 let out = "";
81+
4382 const tick = () => {
44- out += (i === 0 ? "" : "\n") + lines [i];
83+ out += (i === 0 ? "" : "\n") + termLines [i];
4584 el.textContent = out;
4685 i++;
47- if (i < lines .length) setTimeout(tick, i === 1 ? 650 : 420);
86+ if (i < termLines .length) setTimeout(tick, i === 1 ? 650 : 420);
4887 };
88+
4989 setTimeout(tick, 220);
90+ };
91+
92+ // jeśli już widać od razu (np. desktop), startuj natychmiast
93+ const isInViewportNow = () => {
94+ const r = el.getBoundingClientRect();
95+ return r.top < window.innerHeight && r.bottom > 0;
96+ };
97+
98+ if (isInViewportNow()) {
99+ startTyping();
100+ } else if ("IntersectionObserver" in window) {
101+ const io = new IntersectionObserver(
102+ (entries) => {
103+ const entry = entries[0];
104+ if (entry && entry.isIntersecting) {
105+ startTyping();
106+ io.disconnect();
107+ }
108+ },
109+ {
110+ // odpali trochę wcześniej, zanim idealnie wejdzie w kadr
111+ root: null,
112+ threshold: 0.2,
113+ rootMargin: "120px 0px",
114+ }
115+ );
116+
117+ io.observe(el);
118+ } else {
119+ // fallback dla bardzo starych przeglądarek: odpal po pierwszym scrollu
120+ const onScroll = () => {
121+ if (isInViewportNow()) {
122+ startTyping();
123+ window.removeEventListener("scroll", onScroll);
124+ }
125+ };
126+ window.addEventListener("scroll", onScroll, { passive: true });
50127 }
51128 </script >
52- </div >
129+ </div >
0 commit comments