mirror of
https://codeberg.org/Kankys/ApolloNvim2025.git
synced 2026-11-11 08:34:36 +01:00
refactor code
This commit is contained in:
1 parent
9a38077576
commit
b13422d4af
13 files changed
+11934
No files matched your search
@@ -15,6 +15,62 @@
|
||||
- Rainbow Vim - plugin co vytvoří baravné zaávorky
|
||||
- Gitgutter - plugin pro správu git
|
||||
|
||||
## Refactor květen 2026
|
||||
|
||||
✅ Co bylo vytvořeno
|
||||
|
||||
### Struktura `web/`
|
||||
|
||||
```ApolloNvim2025/web/
|
||||
├── html/
|
||||
│ ├── index.html — Hlavní stránka s hero, features, editions, stats, terminal ukázky
|
||||
│ ├── instalace.html — Průvodce instalací (Starter + FrontEnd), CZ/EN, FAQ
|
||||
│ ├── pluginy.html — Všechny pluginy s filtrem, Coc extensions, 19 témat
|
||||
│ ├── tutorialy.html — 20 tutoriálů v kartách + tipy z Hint.md
|
||||
│ ├── novinky.html — Timeline changelog + roadmap vývoje
|
||||
│ └── zkratky.html — Klávesové zkratky v tabulkách + tisk
|
||||
├── css/
|
||||
│ ├── style.css — ~2600 řádků (kompletní dark space design)
|
||||
│ └── animations.css — Keyframes, scroll animace, efekty
|
||||
├── js/
|
||||
│ ├── particles.js — Vesmírný particle systém na Canvas
|
||||
│ └── main.js — Navigace, tabye, copy to clipboard, filtr pluginů, ...
|
||||
└── img/
|
||||
├── apollonvim.jpg
|
||||
└── neovim.jpeg
|
||||
```
|
||||
|
||||
### Klíčové funkce webu
|
||||
- 🌌 **Vesmírný particle systém** – 200 hvězd, blikání, parallax, padající hvězdy
|
||||
- 🎨 **Dracula/dark kosmický design** – purple/pink/cyan paleta, glassmorphism navbar
|
||||
- 📱 **Plně responsivní** – hamburger menu na mobile, přizpůsobení pro všechny velikosti
|
||||
- ⚡ **Scroll animace** – IntersectionObserver, plynulé fade-in/slide-up efekty
|
||||
- 🔌 **Filtr pluginů** – filtrování dle kategorie (Editor, UI, Git, AI, LSP...)
|
||||
- 🇨🇿🇬🇧 **CZ/EN přepínač** – na instalaci a pluginech
|
||||
- 📋 **Copy to clipboard** – u všech terminálových bloků
|
||||
- 🔧 **FAQ accordion** – na stránce instalace
|
||||
- 🎨 **19 barevných témat** – s preview swatchi barev
|
||||
- ⌨️ **Klávesové zkratky** – s print podporou pro PDF
|
||||
|
||||
web/
|
||||
├── index.html ← Hlavní stránka (kořen webu)
|
||||
├── css/
|
||||
│ ├── style.css ← Kompletní CSS (~2800 řádků)
|
||||
│ └── animations.css ← Keyframes a efekty
|
||||
├── html/
|
||||
│ ├── instalace.html ← Průvodce instalací
|
||||
│ ├── pluginy.html ← Seznam pluginů + barevná témata
|
||||
│ ├── tutorialy.html ← Tutoriály
|
||||
│ ├── novinky.html ← Changelog + roadmap
|
||||
│ └── zkratky.html ← Klávesové zkratky
|
||||
├── js/
|
||||
│ ├── particles.js ← Vesmírný particle systém
|
||||
│ └── main.js ← Veškerá JS logika
|
||||
└── img/
|
||||
├── apollonvim.jpg
|
||||
└── neovim.jpeg
|
||||
|
||||
|
||||
<div style="text-align: center;">
|
||||
Copyright © 2024 - 2025 🚀 ApolloNvim / Lukáš Kaňka
|
||||
</div>
|
||||
@@ -0,0 +1,684 @@
|
||||
/* ============================================================
|
||||
ApolloNvim – animations.css
|
||||
Web: apollo-nvim.lukan.cz
|
||||
All keyframes, scroll animations, stagger delays,
|
||||
micro-interactions and loading screen transitions.
|
||||
============================================================ */
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
1. CORE KEYFRAMES
|
||||
──────────────────────────────────────────────────────────── */
|
||||
|
||||
/* 1.1 fadeIn ─ simple opacity */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
/* 1.2 slideUp ─ rise + fade */
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(40px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 1.3 slideInLeft ─ enter from left */
|
||||
@keyframes slideInLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-60px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 1.4 slideInRight ─ enter from right */
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(60px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 1.5 float ─ gentle levitation for hero images / icons */
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
33% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
66% {
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 1.6 pulse-glow ─ pulsing purple box-shadow */
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% {
|
||||
box-shadow:
|
||||
0 0 0 0 rgba(189, 147, 249, 0),
|
||||
0 0 20px 2px rgba(189, 147, 249, 0.15);
|
||||
}
|
||||
50% {
|
||||
box-shadow:
|
||||
0 0 0 4px rgba(189, 147, 249, 0.08),
|
||||
0 0 40px 8px rgba(189, 147, 249, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
/* 1.7 typing ─ blinking cursor caret */
|
||||
@keyframes typing {
|
||||
0%, 100% { border-right-color: rgba(189, 147, 249, 1); }
|
||||
50% { border-right-color: transparent; }
|
||||
}
|
||||
|
||||
/* Typewriter text reveal (pair with .typewriter class) */
|
||||
@keyframes typewriter {
|
||||
from { width: 0; }
|
||||
to { width: 100%; }
|
||||
}
|
||||
|
||||
/* 1.8 spin-slow ─ slow full rotation (used by loading logo) */
|
||||
@keyframes spin-slow {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 1.9 glitch ─ subtle horizontal glitch for headings */
|
||||
@keyframes glitch {
|
||||
0% { clip-path: inset(0 0 100% 0); transform: translate(0); }
|
||||
10% { clip-path: inset(10% 0 60% 0); transform: translate(-3px, 1px); }
|
||||
20% { clip-path: inset(60% 0 10% 0); transform: translate(3px, -1px); }
|
||||
30% { clip-path: inset(30% 0 40% 0); transform: translate(-2px, 0); }
|
||||
40% { clip-path: inset(0 0 80% 0); transform: translate(2px, 1px); }
|
||||
50% { clip-path: inset(80% 0 0 0); transform: translate(-1px, -2px); }
|
||||
60% { clip-path: inset(20% 0 50% 0); transform: translate(1px, 0); }
|
||||
70% { clip-path: inset(50% 0 20% 0); transform: translate(-2px, 1px); }
|
||||
80% { clip-path: inset(5% 0 70% 0); transform: translate(2px, -1px); }
|
||||
90% { clip-path: inset(70% 0 5% 0); transform: translate(0, 2px); }
|
||||
100% { clip-path: inset(0 0 0 0); transform: translate(0); }
|
||||
}
|
||||
|
||||
/* 1.10 rocket-launch ─ rocket rises with shrinking trail */
|
||||
@keyframes rocket-launch {
|
||||
0% {
|
||||
transform: translateY(0) scale(1);
|
||||
opacity: 1;
|
||||
filter: drop-shadow(0 8px 16px rgba(189, 147, 249, 0.6));
|
||||
}
|
||||
30% {
|
||||
transform: translateY(-20px) scale(1.05);
|
||||
filter: drop-shadow(0 16px 32px rgba(255, 121, 198, 0.7));
|
||||
}
|
||||
70% {
|
||||
transform: translateY(-80px) scale(0.9);
|
||||
opacity: 0.85;
|
||||
filter: drop-shadow(0 4px 8px rgba(139, 233, 253, 0.5));
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-200px) scale(0.4);
|
||||
opacity: 0;
|
||||
filter: drop-shadow(0 0 0 transparent);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rocket trail (::after pseudo) */
|
||||
@keyframes rocket-trail {
|
||||
0% { opacity: 0.8; transform: scaleY(1) scaleX(1); }
|
||||
50% { opacity: 0.5; transform: scaleY(1.4) scaleX(0.7); }
|
||||
100% { opacity: 0; transform: scaleY(2) scaleX(0.3); }
|
||||
}
|
||||
|
||||
/* 1.11 star-twinkle ─ random opacity blink for star dots */
|
||||
@keyframes star-twinkle {
|
||||
0%, 100% { opacity: 0.15; transform: scale(0.85); }
|
||||
25% { opacity: 0.9; transform: scale(1.1); }
|
||||
50% { opacity: 0.4; transform: scale(0.95); }
|
||||
75% { opacity: 1; transform: scale(1.05); }
|
||||
}
|
||||
|
||||
/* 1.12 gradient-shift ─ animated gradient background */
|
||||
@keyframes gradient-shift {
|
||||
0% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
100% { background-position: 0% 50%; }
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
2. ANIMATION UTILITY CLASSES
|
||||
──────────────────────────────────────────────────────────── */
|
||||
|
||||
/* Basic named animations */
|
||||
.anim-fade-in { animation: fadeIn 0.5s ease both; }
|
||||
.anim-slide-up { animation: slideUp 0.55s ease both; }
|
||||
.anim-slide-left { animation: slideInLeft 0.55s ease both; }
|
||||
.anim-slide-right { animation: slideInRight 0.55s ease both; }
|
||||
.anim-float { animation: float 4s ease-in-out infinite; }
|
||||
.anim-pulse-glow { animation: pulse-glow 2.5s ease-in-out infinite; }
|
||||
.anim-spin-slow { animation: spin-slow 20s linear infinite; }
|
||||
.anim-star-twinkle { animation: star-twinkle 3s ease-in-out infinite; }
|
||||
|
||||
/* Gradient-shift background (set background-size: 200% 200% on element) */
|
||||
.anim-gradient-bg {
|
||||
background-size: 200% 200%;
|
||||
animation: gradient-shift 6s ease infinite;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
3. TYPEWRITER EFFECT
|
||||
──────────────────────────────────────────────────────────── */
|
||||
.typewriter {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
border-right: 2px solid;
|
||||
/* combine reveal + cursor blink */
|
||||
animation:
|
||||
typewriter 2s steps(30, end) both,
|
||||
typing 0.7s step-end infinite;
|
||||
}
|
||||
|
||||
/* Standalone cursor only (no text reveal) */
|
||||
.cursor-blink {
|
||||
display: inline-block;
|
||||
width: 2px;
|
||||
height: 1.1em;
|
||||
background: #bd93f9;
|
||||
vertical-align: text-bottom;
|
||||
animation: typing 0.7s step-end infinite;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
4. GLITCH EFFECT
|
||||
──────────────────────────────────────────────────────────── */
|
||||
.glitch-text {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Glitch layers via pseudo-elements */
|
||||
.glitch-text::before,
|
||||
.glitch-text::after {
|
||||
content: attr(data-text);
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.glitch-text::before {
|
||||
color: #ff79c6;
|
||||
animation: glitch 3.5s infinite linear alternate-reverse;
|
||||
animation-delay: 0.1s;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.glitch-text::after {
|
||||
color: #8be9fd;
|
||||
animation: glitch 2.8s infinite linear alternate;
|
||||
animation-delay: 0.25s;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* On hover only */
|
||||
.glitch-text--hover::before,
|
||||
.glitch-text--hover::after {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.glitch-text--hover:hover::before,
|
||||
.glitch-text--hover:hover::after {
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
5. ROCKET LAUNCH
|
||||
──────────────────────────────────────────────────────────── */
|
||||
.rocket {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.rocket--launch {
|
||||
animation: rocket-launch 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
||||
}
|
||||
|
||||
/* Trail element */
|
||||
.rocket__trail {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 6px;
|
||||
height: 20px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
background: linear-gradient(180deg, rgba(255, 121, 198, 0.8), transparent);
|
||||
transform-origin: top center;
|
||||
animation: rocket-trail 1.2s ease forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.rocket--launch .rocket__trail {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
6. STAR FIELD
|
||||
──────────────────────────────────────────────────────────── */
|
||||
.star {
|
||||
position: absolute;
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
background: #f8f8f2;
|
||||
border-radius: 50%;
|
||||
animation: star-twinkle var(--duration, 3s) ease-in-out infinite;
|
||||
animation-delay: var(--delay, 0s);
|
||||
}
|
||||
|
||||
.star--sm { width: 1.5px; height: 1.5px; }
|
||||
.star--md { width: 2.5px; height: 2.5px; }
|
||||
.star--lg { width: 4px; height: 4px; box-shadow: 0 0 4px 1px rgba(248, 248, 242, 0.4); }
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
7. SCROLL-TRIGGERED ANIMATIONS
|
||||
──────────────────────────────────────────────────────────── */
|
||||
|
||||
/* Base hidden state – applied by JS IntersectionObserver */
|
||||
.animate-on-scroll {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
transition: opacity 0.6s ease, transform 0.6s ease;
|
||||
}
|
||||
|
||||
/* Revealed state – JS adds .visible class */
|
||||
.animate-on-scroll.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Directional variants */
|
||||
.animate-on-scroll--left {
|
||||
opacity: 0;
|
||||
transform: translateX(-40px);
|
||||
transition: opacity 0.6s ease, transform 0.6s ease;
|
||||
}
|
||||
|
||||
.animate-on-scroll--left.visible {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.animate-on-scroll--right {
|
||||
opacity: 0;
|
||||
transform: translateX(40px);
|
||||
transition: opacity 0.6s ease, transform 0.6s ease;
|
||||
}
|
||||
|
||||
.animate-on-scroll--right.visible {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.animate-on-scroll--scale {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
transition: opacity 0.6s ease, transform 0.6s ease;
|
||||
}
|
||||
|
||||
.animate-on-scroll--scale.visible {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
8. STAGGER DELAYS
|
||||
──────────────────────────────────────────────────────────── */
|
||||
.delay-1 { animation-delay: 100ms; transition-delay: 100ms; }
|
||||
.delay-2 { animation-delay: 200ms; transition-delay: 200ms; }
|
||||
.delay-3 { animation-delay: 300ms; transition-delay: 300ms; }
|
||||
.delay-4 { animation-delay: 400ms; transition-delay: 400ms; }
|
||||
.delay-5 { animation-delay: 500ms; transition-delay: 500ms; }
|
||||
|
||||
/* Extended stagger for large grids */
|
||||
.delay-6 { animation-delay: 600ms; transition-delay: 600ms; }
|
||||
.delay-7 { animation-delay: 700ms; transition-delay: 700ms; }
|
||||
.delay-8 { animation-delay: 800ms; transition-delay: 800ms; }
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
9. HOVER MICRO-ANIMATIONS
|
||||
──────────────────────────────────────────────────────────── */
|
||||
|
||||
/* ── Buttons ── */
|
||||
/* Primary button shine sweep */
|
||||
.btn-primary {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-primary::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -75%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
120deg,
|
||||
transparent 20%,
|
||||
rgba(255, 255, 255, 0.22) 50%,
|
||||
transparent 80%
|
||||
);
|
||||
transform: skewX(-20deg);
|
||||
transition: left 0.55s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.btn-primary:hover::after {
|
||||
left: 135%;
|
||||
}
|
||||
|
||||
/* Secondary button border glow pulse */
|
||||
.btn-secondary {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn-secondary::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(135deg, transparent, rgba(189, 147, 249, 0.3), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.btn-secondary:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ── Links ── */
|
||||
.link-underline {
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.link-underline::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, #bd93f9, #ff79c6);
|
||||
transform: scaleX(0);
|
||||
transform-origin: right;
|
||||
transition: transform 0.35s ease;
|
||||
}
|
||||
|
||||
.link-underline:hover::after {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left;
|
||||
}
|
||||
|
||||
/* ── Card tilt (subtle) ── */
|
||||
.card-tilt {
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Managed by JS mousemove if desired – CSS fallback hover */
|
||||
.card-tilt:hover {
|
||||
transform: perspective(800px) rotateX(2deg) rotateY(-2deg) translateY(-4px);
|
||||
}
|
||||
|
||||
/* ── Icon bounce ── */
|
||||
.icon-bounce {
|
||||
display: inline-block;
|
||||
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.icon-bounce:hover {
|
||||
transform: scale(1.25) translateY(-2px);
|
||||
}
|
||||
|
||||
/* ── Icon spin on hover ── */
|
||||
.icon-spin-hover {
|
||||
transition: transform 0.4s ease;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.icon-spin-hover:hover {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* ── Pill/badge pop ── */
|
||||
.badge {
|
||||
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.badge:hover {
|
||||
transform: scale(1.08) translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(189, 147, 249, 0.3);
|
||||
}
|
||||
|
||||
/* ── Nav link underline (already in style.css, reinforced here) ── */
|
||||
.navbar__link {
|
||||
will-change: color;
|
||||
}
|
||||
|
||||
/* ── Feature card shimmer border ── */
|
||||
.feature-card {
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
10. ANIMATED GRADIENT HERO BACKGROUND
|
||||
──────────────────────────────────────────────────────────── */
|
||||
.hero-gradient-bg {
|
||||
background: linear-gradient(
|
||||
-45deg,
|
||||
#0d0d1a,
|
||||
#12122a,
|
||||
#1a0a2e,
|
||||
#0d1a2e
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: gradient-shift 12s ease infinite;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
11. LOADING SCREEN – FADE OUT
|
||||
──────────────────────────────────────────────────────────── */
|
||||
@keyframes loading-fade-out {
|
||||
0% { opacity: 1; transform: scale(1); }
|
||||
80% { opacity: 1; transform: scale(1.02); }
|
||||
100% { opacity: 0; transform: scale(0.98); }
|
||||
}
|
||||
|
||||
/* Trigger by adding .is-hiding to .loading-screen via JS */
|
||||
.loading-screen.is-hiding {
|
||||
animation: loading-fade-out 0.55s ease forwards;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Orbiting dots spinner (used inside loading screen) */
|
||||
@keyframes orbit {
|
||||
from { transform: rotate(0deg) translateX(28px) rotate(0deg); }
|
||||
to { transform: rotate(360deg) translateX(28px) rotate(-360deg); }
|
||||
}
|
||||
|
||||
.loader-orbit {
|
||||
position: relative;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.loader-orbit__dot {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--purple, #bd93f9);
|
||||
margin: -4px 0 0 -4px;
|
||||
animation: orbit 1.4s linear infinite;
|
||||
}
|
||||
|
||||
.loader-orbit__dot:nth-child(2) {
|
||||
background: var(--pink, #ff79c6);
|
||||
animation-delay: -0.46s;
|
||||
}
|
||||
|
||||
.loader-orbit__dot:nth-child(3) {
|
||||
background: var(--cyan, #8be9fd);
|
||||
animation-delay: -0.93s;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
12. REVEAL ANIMATIONS FOR PAGE SECTIONS
|
||||
──────────────────────────────────────────────────────────── */
|
||||
|
||||
/* Used by JS: section fades in as a block */
|
||||
.section-reveal {
|
||||
opacity: 0;
|
||||
transition: opacity 0.8s ease, transform 0.8s ease;
|
||||
transform: translateY(24px);
|
||||
}
|
||||
|
||||
.section-reveal.in-view {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Hero content staggered entrance */
|
||||
.hero__content > * {
|
||||
opacity: 0;
|
||||
animation: slideUp 0.65s ease both;
|
||||
}
|
||||
|
||||
.hero__content > *:nth-child(1) { animation-delay: 0.1s; }
|
||||
.hero__content > *:nth-child(2) { animation-delay: 0.25s; }
|
||||
.hero__content > *:nth-child(3) { animation-delay: 0.4s; }
|
||||
.hero__content > *:nth-child(4) { animation-delay: 0.55s; }
|
||||
.hero__content > *:nth-child(5) { animation-delay: 0.7s; }
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
13. PROGRESS / SKILL BARS
|
||||
──────────────────────────────────────────────────────────── */
|
||||
@keyframes bar-fill {
|
||||
from { width: 0%; }
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 4px;
|
||||
background: var(--border, rgba(189, 147, 249, 0.2));
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar__fill {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(90deg, var(--purple, #bd93f9), var(--pink, #ff79c6));
|
||||
width: var(--progress, 0%);
|
||||
animation: bar-fill 1.2s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
animation-delay: var(--delay, 0.2s);
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
14. NOTIFICATION / TOAST ENTRANCE
|
||||
──────────────────────────────────────────────────────────── */
|
||||
@keyframes toast-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(calc(100% + 24px));
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes toast-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(calc(100% + 24px));
|
||||
}
|
||||
}
|
||||
|
||||
.toast {
|
||||
animation: toast-in 0.35s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
}
|
||||
|
||||
.toast.closing {
|
||||
animation: toast-out 0.3s ease forwards;
|
||||
}
|
||||
|
||||
/* ────────────────────────────────────────────────────────────
|
||||
15. REDUCED MOTION OVERRIDES
|
||||
──────────────────────────────────────────────────────────── */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
/* Disable all custom animations */
|
||||
.anim-fade-in,
|
||||
.anim-slide-up,
|
||||
.anim-slide-left,
|
||||
.anim-slide-right,
|
||||
.anim-float,
|
||||
.anim-pulse-glow,
|
||||
.anim-spin-slow,
|
||||
.anim-star-twinkle,
|
||||
.anim-gradient-bg,
|
||||
.typewriter,
|
||||
.cursor-blink,
|
||||
.glitch-text::before,
|
||||
.glitch-text::after,
|
||||
.hero__content > *,
|
||||
.rocket--launch,
|
||||
.rocket__trail,
|
||||
.loader-orbit__dot,
|
||||
.loading-screen__bar-fill,
|
||||
.loading-screen__logo,
|
||||
.section-reveal,
|
||||
.progress-bar__fill,
|
||||
.toast,
|
||||
.toast.closing {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
/* Scroll-triggered: instantly reveal instead */
|
||||
.animate-on-scroll,
|
||||
.animate-on-scroll--left,
|
||||
.animate-on-scroll--right,
|
||||
.animate-on-scroll--scale {
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
/* Hero items also visible immediately */
|
||||
.hero__content > * {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
+3026
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,822 @@
|
||||
<!doctype html>
|
||||
<html lang="cs">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>📰 Novinky | ApolloNvim</title>
|
||||
|
||||
<!-- Meta description & SEO -->
|
||||
<meta
|
||||
name="description"
|
||||
content="Changelog a novinky ApolloNvim – sledujte vývoj distribuce, nové verze a plány do budoucna."
|
||||
/>
|
||||
<meta name="author" content="Lukáš Kaňka" />
|
||||
<meta
|
||||
name="keywords"
|
||||
content="ApolloNvim, Neovim, changelog, novinky, aktualizace, verze, vývoj"
|
||||
/>
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta
|
||||
property="og:url"
|
||||
content="https://apollo-nvim.lukan.cz/novinky.html"
|
||||
/>
|
||||
<meta property="og:title" content="📰 Novinky | ApolloNvim" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Changelog a novinky ApolloNvim – sledujte vývoj distribuce a nové verze."
|
||||
/>
|
||||
<meta property="og:image" content="../img/apollonvim.jpg" />
|
||||
<meta property="og:locale" content="cs_CZ" />
|
||||
<meta property="og:site_name" content="ApolloNvim" />
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="📰 Novinky | ApolloNvim" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="Changelog a novinky ApolloNvim – sledujte vývoj distribuce."
|
||||
/>
|
||||
<meta name="twitter:image" content="../img/apollonvim.jpg" />
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<!-- Stylesheets -->
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<link rel="stylesheet" href="../css/animations.css" />
|
||||
|
||||
<!-- Canonical -->
|
||||
<link
|
||||
rel="canonical"
|
||||
href="https://apollo-nvim.lukan.cz/novinky.html"
|
||||
/>
|
||||
|
||||
<style>
|
||||
/* ── Timeline ── */
|
||||
.timeline-wrap {
|
||||
max-width: 760px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
position: relative;
|
||||
padding-left: 2.5rem;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.timeline::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0.55rem;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
var(--purple),
|
||||
var(--cyan),
|
||||
transparent
|
||||
);
|
||||
border-radius: 99px;
|
||||
}
|
||||
|
||||
.timeline__item {
|
||||
position: relative;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.timeline__item::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -2.05rem;
|
||||
top: 0.35rem;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: var(--purple);
|
||||
border: 3px solid var(--bg);
|
||||
box-shadow: 0 0 0 2px var(--purple);
|
||||
transition:
|
||||
box-shadow var(--transition),
|
||||
background var(--transition);
|
||||
}
|
||||
|
||||
.timeline__item:hover::before {
|
||||
background: var(--cyan);
|
||||
box-shadow:
|
||||
0 0 0 2px var(--cyan),
|
||||
0 0 12px rgba(6, 182, 212, 0.4);
|
||||
}
|
||||
|
||||
.timeline__item--current::before {
|
||||
background: var(--green, #22c55e);
|
||||
box-shadow:
|
||||
0 0 0 2px var(--green, #22c55e),
|
||||
0 0 14px rgba(34, 197, 94, 0.5);
|
||||
animation: pulse-dot 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-dot {
|
||||
0%,
|
||||
100% {
|
||||
box-shadow:
|
||||
0 0 0 2px var(--green, #22c55e),
|
||||
0 0 8px rgba(34, 197, 94, 0.4);
|
||||
}
|
||||
50% {
|
||||
box-shadow:
|
||||
0 0 0 2px var(--green, #22c55e),
|
||||
0 0 20px rgba(34, 197, 94, 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
.timeline__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.6rem;
|
||||
margin-bottom: 0.85rem;
|
||||
}
|
||||
|
||||
.timeline__version {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.timeline__date {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.timeline__card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.5rem 1.75rem;
|
||||
transition:
|
||||
border-color var(--transition),
|
||||
box-shadow var(--transition);
|
||||
}
|
||||
|
||||
.timeline__item:hover .timeline__card {
|
||||
border-color: var(--purple);
|
||||
box-shadow: 0 4px 24px rgba(139, 92, 246, 0.1);
|
||||
}
|
||||
|
||||
.timeline__card ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
}
|
||||
|
||||
.timeline__card li {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.6rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary, var(--text-muted));
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.timeline__card li::before {
|
||||
content: "▸";
|
||||
color: var(--purple);
|
||||
flex-shrink: 0;
|
||||
margin-top: 0.05rem;
|
||||
}
|
||||
|
||||
/* ── Roadmap cards ── */
|
||||
.roadmap-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 1.25rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.roadmap-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.6rem;
|
||||
transition:
|
||||
transform var(--transition),
|
||||
border-color var(--transition),
|
||||
box-shadow var(--transition);
|
||||
}
|
||||
|
||||
.roadmap-card:hover {
|
||||
transform: translateY(-3px);
|
||||
border-color: var(--cyan);
|
||||
box-shadow: 0 6px 24px rgba(6, 182, 212, 0.12);
|
||||
}
|
||||
|
||||
.roadmap-card__icon {
|
||||
font-size: 1.75rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.roadmap-card__title {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.roadmap-card__desc {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.55;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.roadmap-card__status {
|
||||
margin-top: auto;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* ── CTA / Odber ── */
|
||||
.news-cta {
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(139, 92, 246, 0.08) 0%,
|
||||
rgba(6, 182, 212, 0.05) 100%
|
||||
);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.news-cta__title {
|
||||
font-size: clamp(1.25rem, 3vw, 1.75rem);
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.news-cta__subtitle {
|
||||
font-size: 1rem;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* Animate on scroll */
|
||||
.animate-on-scroll {
|
||||
opacity: 0;
|
||||
transform: translateY(24px);
|
||||
transition:
|
||||
opacity 0.55s ease,
|
||||
transform 0.55s ease;
|
||||
}
|
||||
|
||||
.animate-on-scroll.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.roadmap-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.roadmap-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- ===================== NAVBAR ===================== -->
|
||||
<header class="navbar" id="navbar">
|
||||
<nav class="navbar__inner" aria-label="Hlavní navigace">
|
||||
<a
|
||||
href="../index.html"
|
||||
class="navbar__logo"
|
||||
aria-label="ApolloNvim – Domů"
|
||||
>🚀 <span>ApolloNvim</span></a
|
||||
>
|
||||
<ul class="navbar__links" id="nav-links" role="list">
|
||||
<li><a href="../index.html">Domů</a></li>
|
||||
<li><a href="./instalace.html">Instalace</a></li>
|
||||
<li><a href="./pluginy.html">Pluginy</a></li>
|
||||
<li><a href="./tutorialy.html">Tutoriály</a></li>
|
||||
<li>
|
||||
<a
|
||||
href="./novinky.html"
|
||||
class="active"
|
||||
aria-current="page"
|
||||
>Novinky</a
|
||||
>
|
||||
</li>
|
||||
<li><a href="./zkratky.html">Zkratky</a></li>
|
||||
</ul>
|
||||
<button
|
||||
id="hamburger"
|
||||
class="navbar__hamburger"
|
||||
aria-label="Otevřít menu"
|
||||
aria-expanded="false"
|
||||
aria-controls="mobile-menu"
|
||||
>
|
||||
<span></span><span></span><span></span>
|
||||
</button>
|
||||
</nav>
|
||||
<div class="navbar__mobile" id="mobile-menu" hidden>
|
||||
<ul role="list">
|
||||
<li><a href="../index.html">Domů</a></li>
|
||||
<li><a href="./instalace.html">Instalace</a></li>
|
||||
<li><a href="./pluginy.html">Pluginy</a></li>
|
||||
<li><a href="./tutorialy.html">Tutoriály</a></li>
|
||||
<li><a href="./novinky.html" class="active">Novinky</a></li>
|
||||
<li><a href="./zkratky.html">Zkratky</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main-content">
|
||||
<!-- ===================== HERO ===================== -->
|
||||
<section
|
||||
class="hero"
|
||||
aria-labelledby="hero-title"
|
||||
style="min-height: 42vh"
|
||||
>
|
||||
<canvas id="particles-canvas" aria-hidden="true"></canvas>
|
||||
<div
|
||||
class="hero__content"
|
||||
style="padding-top: 6rem; padding-bottom: 4rem"
|
||||
>
|
||||
<div class="hero__badge">
|
||||
<span>📰 Changelog & Aktualizace</span>
|
||||
</div>
|
||||
<h1 id="hero-title" class="hero__title">Novinky</h1>
|
||||
<p class="hero__subtitle">Changelog a novinky ApolloNvim</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== CHANGELOG TIMELINE ===================== -->
|
||||
<section class="section" aria-labelledby="changelog-title">
|
||||
<div class="container">
|
||||
<header class="section-title section-title--center">
|
||||
<h2 id="changelog-title">📋 Changelog</h2>
|
||||
<p class="section__subtitle section__subtitle--center">
|
||||
Historie verzí a provedených změn
|
||||
</p>
|
||||
<div class="section-title__line"></div>
|
||||
</header>
|
||||
|
||||
<div class="timeline-wrap" style="margin-top: 3.5rem">
|
||||
<ol class="timeline" aria-label="Changelog ApolloNvim">
|
||||
<!-- v25.05 – Aktuální -->
|
||||
<li
|
||||
class="timeline__item timeline__item--current animate-on-scroll"
|
||||
>
|
||||
<div class="timeline__header">
|
||||
<span class="timeline__version"
|
||||
>v26.05</span
|
||||
>
|
||||
<span class="badge badge--green"
|
||||
>Aktuální</span
|
||||
>
|
||||
<time
|
||||
class="timeline__date"
|
||||
datetime="2026-05"
|
||||
>Květen 2026</time
|
||||
>
|
||||
</div>
|
||||
<div class="timeline__card">
|
||||
<ul>
|
||||
<li>Nový redesign webu ApolloNvim</li>
|
||||
<li>
|
||||
Vícestrankový web s HTML/CSS/JS
|
||||
strukturou
|
||||
</li>
|
||||
<li>
|
||||
Temný vesmírný design inspirovaný
|
||||
Dracula tématem
|
||||
</li>
|
||||
<li>
|
||||
Stránky: Domů, Instalace, Pluginy,
|
||||
Tutoriály, Novinky, Zkratky
|
||||
</li>
|
||||
<li>
|
||||
Particle systém s animovanými
|
||||
hvězdičkami
|
||||
</li>
|
||||
<li>
|
||||
Kopírovatelné terminálové bloky,
|
||||
responzivní design
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- v25.04 -->
|
||||
<li class="timeline__item animate-on-scroll">
|
||||
<div class="timeline__header">
|
||||
<span class="timeline__version"
|
||||
>v25.04</span
|
||||
>
|
||||
<span class="badge badge--purple"
|
||||
>Duben 2025</span
|
||||
>
|
||||
<time
|
||||
class="timeline__date"
|
||||
datetime="2025-04"
|
||||
>Duben 2025</time
|
||||
>
|
||||
</div>
|
||||
<div class="timeline__card">
|
||||
<ul>
|
||||
<li>
|
||||
Verze 25.04 – aktuální stabilní
|
||||
vydání
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- v25.02 -->
|
||||
<li class="timeline__item animate-on-scroll">
|
||||
<div class="timeline__header">
|
||||
<span class="timeline__version"
|
||||
>v25.02</span
|
||||
>
|
||||
<span class="badge badge--purple"
|
||||
>Únor 2025</span
|
||||
>
|
||||
<time
|
||||
class="timeline__date"
|
||||
datetime="2025-02"
|
||||
>Únor 2025</time
|
||||
>
|
||||
</div>
|
||||
<div class="timeline__card">
|
||||
<ul>
|
||||
<li>Nový web ApolloNvim</li>
|
||||
<li>Lokalizace do angličtiny</li>
|
||||
<li>
|
||||
Vylepšená instalace pro jednodušší
|
||||
onboarding
|
||||
</li>
|
||||
<li>Podpora pouze pro Linux a Mac</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- v25.01 -->
|
||||
<li class="timeline__item animate-on-scroll">
|
||||
<div class="timeline__header">
|
||||
<span class="timeline__version"
|
||||
>v25.01</span
|
||||
>
|
||||
<span class="badge badge--purple"
|
||||
>Leden 2025</span
|
||||
>
|
||||
<time
|
||||
class="timeline__date"
|
||||
datetime="2025-01"
|
||||
>Leden 2025</time
|
||||
>
|
||||
</div>
|
||||
<div class="timeline__card">
|
||||
<ul>
|
||||
<li>
|
||||
Nová instalace s přepracovaným
|
||||
procesem nastavení
|
||||
</li>
|
||||
<li>
|
||||
Nový website – moderní design a
|
||||
lepší přehlednost
|
||||
</li>
|
||||
<li>
|
||||
Nový obrázek ApolloNvim –
|
||||
aktualizovaná vizuální identita
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- v24.10 -->
|
||||
<li class="timeline__item animate-on-scroll">
|
||||
<div class="timeline__header">
|
||||
<span class="timeline__version"
|
||||
>v24.10</span
|
||||
>
|
||||
<span class="badge badge--purple"
|
||||
>Říjen 2024</span
|
||||
>
|
||||
<time
|
||||
class="timeline__date"
|
||||
datetime="2024-10"
|
||||
>Říjen 2024</time
|
||||
>
|
||||
</div>
|
||||
<div class="timeline__card">
|
||||
<ul>
|
||||
<li>
|
||||
Podpora PHP – LSP a snippety pro PHP
|
||||
vývojáře
|
||||
</li>
|
||||
<li>
|
||||
Automatická instalace pluginů po
|
||||
přidání do <code>init.vim</code>
|
||||
</li>
|
||||
<li>
|
||||
Rainbow Vim – plugin pro barevné
|
||||
závorky pro lepší čitelnost kódu
|
||||
</li>
|
||||
<li>
|
||||
Gitgutter – plugin pro správu a
|
||||
zobrazení git změn v editoru
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<!-- /.timeline-wrap -->
|
||||
</div>
|
||||
<!-- /.container -->
|
||||
</section>
|
||||
|
||||
<!-- ===================== ROADMAP ===================== -->
|
||||
<section
|
||||
class="section section--alt"
|
||||
aria-labelledby="roadmap-title"
|
||||
>
|
||||
<div class="container">
|
||||
<header class="section-title section-title--center">
|
||||
<h2 id="roadmap-title">🗺️ Plán vývoje</h2>
|
||||
<p class="section__subtitle section__subtitle--center">
|
||||
Co připravujeme pro budoucí verze ApolloNvim
|
||||
</p>
|
||||
<div class="section-title__line"></div>
|
||||
</header>
|
||||
|
||||
<div class="roadmap-grid">
|
||||
<div class="roadmap-card animate-on-scroll">
|
||||
<div class="roadmap-card__icon" aria-hidden="true">
|
||||
🎨
|
||||
</div>
|
||||
<h3 class="roadmap-card__title">
|
||||
Nová uvítací obrazovka
|
||||
</h3>
|
||||
<p class="roadmap-card__desc">
|
||||
Vlastní dashboard při startu – rychlé otevření
|
||||
posledních souborů, zkratky a statistiky.
|
||||
</p>
|
||||
<div class="roadmap-card__status">
|
||||
<span class="badge badge--yellow"
|
||||
>Plánováno</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="roadmap-card animate-on-scroll">
|
||||
<div class="roadmap-card__icon" aria-hidden="true">
|
||||
🔌
|
||||
</div>
|
||||
<h3 class="roadmap-card__title">Nové pluginy</h3>
|
||||
<p class="roadmap-card__desc">
|
||||
Rozšiřování knihovny pluginů o nejlepší nástroje
|
||||
z ekosystému Neovim komunity.
|
||||
</p>
|
||||
<div class="roadmap-card__status">
|
||||
<span class="badge badge--yellow"
|
||||
>Plánováno</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="roadmap-card animate-on-scroll">
|
||||
<div class="roadmap-card__icon" aria-hidden="true">
|
||||
📚
|
||||
</div>
|
||||
<h3 class="roadmap-card__title">
|
||||
Návody k pluginům
|
||||
</h3>
|
||||
<p class="roadmap-card__desc">
|
||||
Podrobné tutoriály pro každý zahrnutý plugin s
|
||||
příklady a tipy pro efektivní použití.
|
||||
</p>
|
||||
<div class="roadmap-card__status">
|
||||
<span class="badge badge--cyan">Probíhá</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="roadmap-card animate-on-scroll">
|
||||
<div class="roadmap-card__icon" aria-hidden="true">
|
||||
⚡
|
||||
</div>
|
||||
<h3 class="roadmap-card__title">
|
||||
Instalátor přes link z Git
|
||||
</h3>
|
||||
<p class="roadmap-card__desc">
|
||||
Jednořádkový instalátor přímo z Codeberg – bez
|
||||
klonování, bez komplikací.
|
||||
</p>
|
||||
<div class="roadmap-card__status">
|
||||
<span class="badge badge--yellow"
|
||||
>Plánováno</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.roadmap-grid -->
|
||||
</div>
|
||||
<!-- /.container -->
|
||||
</section>
|
||||
|
||||
<!-- ===================== CTA / ODBER ===================== -->
|
||||
<section class="section" aria-labelledby="news-cta-title">
|
||||
<div class="container">
|
||||
<div class="news-cta animate-on-scroll">
|
||||
<p
|
||||
style="font-size: 2.5rem; margin-bottom: 1rem"
|
||||
aria-hidden="true"
|
||||
>
|
||||
✈️
|
||||
</p>
|
||||
<h2 id="news-cta-title" class="news-cta__title">
|
||||
Sledujte novinky na Telegramu
|
||||
</h2>
|
||||
<p class="news-cta__subtitle">
|
||||
Buďte první, kdo se dozví o nových verzích,
|
||||
pluginech a tutoriálech ApolloNvim.
|
||||
</p>
|
||||
<a
|
||||
href="https://t.me/apollonvim"
|
||||
class="btn-primary"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Připojit se na Telegram ApolloNvim (otevře se v novém okně)"
|
||||
>
|
||||
<span>✈️</span> Připojit se na Telegram
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- ===================== FOOTER ===================== -->
|
||||
<footer class="footer" role="contentinfo">
|
||||
<div class="container">
|
||||
<div class="footer__grid">
|
||||
<div class="footer__brand">
|
||||
<a
|
||||
href="../index.html"
|
||||
class="footer__logo"
|
||||
aria-label="ApolloNvim – Domů"
|
||||
>
|
||||
🚀 ApolloNvim
|
||||
</a>
|
||||
<p class="footer__tagline">
|
||||
Rychlý, lehký a plně konfigurovatelný Neovim pro
|
||||
Front-End vývojáře.
|
||||
</p>
|
||||
<p
|
||||
class="footer__love"
|
||||
aria-label="Vytvořeno s láskou a klávesnicí"
|
||||
>
|
||||
Made with ❤️ and ⌨️
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<nav class="footer__nav" aria-label="Patička navigace">
|
||||
<h4 class="footer__nav-title">Navigace</h4>
|
||||
<ul class="footer__nav-list" role="list">
|
||||
<li>
|
||||
<a href="../index.html" class="footer__link"
|
||||
>Domů</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./instalace.html" class="footer__link"
|
||||
>Instalace</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./pluginy.html" class="footer__link"
|
||||
>Pluginy</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./tutorialy.html" class="footer__link"
|
||||
>Tutoriály</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./novinky.html" class="footer__link"
|
||||
>Novinky</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./zkratky.html" class="footer__link"
|
||||
>Zkratky</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="footer__links">
|
||||
<h4 class="footer__nav-title">Externe</h4>
|
||||
<ul class="footer__nav-list" role="list">
|
||||
<li>
|
||||
<a
|
||||
href="https://t.me/apollonvim"
|
||||
class="footer__link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Telegram (otevře se v novém okně)"
|
||||
>
|
||||
✈️ Telegram
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://codeberg.org/ApolloNvim/ApolloNvim2025"
|
||||
class="footer__link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Codeberg repozitář (otevře se v novém okně)"
|
||||
>
|
||||
🏔️ Codeberg
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://lukaskanka.cz/"
|
||||
class="footer__link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Web autora (otevře se v novém okně)"
|
||||
>
|
||||
🧑💻 Web autora
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer__bottom">
|
||||
<p class="footer__copy">
|
||||
Copyright © 2024 – 2025 🚀 ApolloNvim / Lukáš
|
||||
Kaňka
|
||||
</p>
|
||||
<p class="footer__version">
|
||||
<span class="badge badge--version">v25.04</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- ===================== SCRIPTS ===================== -->
|
||||
<script src="../js/particles.js"></script>
|
||||
<script src="../js/main.js"></script>
|
||||
<script>
|
||||
// Animate on scroll observer
|
||||
(function () {
|
||||
const elements =
|
||||
document.querySelectorAll(".animate-on-scroll");
|
||||
if (!elements.length) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add("visible");
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1, rootMargin: "0px 0px -40px 0px" },
|
||||
);
|
||||
|
||||
elements.forEach((el, i) => {
|
||||
el.style.transitionDelay = i * 100 + "ms";
|
||||
observer.observe(el);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 372 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
+442
@@ -0,0 +1,442 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="cs">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>🚀 ApolloNvim | Neovim distribuce pro Front-End</title>
|
||||
|
||||
<!-- Meta description & SEO -->
|
||||
<meta name="description" content="ApolloNvim je rychlá a lehká Neovim distribuce pro moderní Front-End vývoj. Postavená na Plug.vim, Coc a Lua. Dvě edice: Starter a FrontEnd." />
|
||||
<meta name="author" content="Lukáš Kaňka" />
|
||||
<meta name="keywords" content="ApolloNvim, Neovim, konfigurace, front-end, HTML, CSS, JavaScript, TypeScript, Lua, Coc, Plug.vim" />
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://apollo-nvim.lukan.cz/" />
|
||||
<meta property="og:title" content="🚀 ApolloNvim | Neovim distribuce pro Front-End" />
|
||||
<meta property="og:description" content="Rychlá, lehká a plně konfigurovatelná Neovim distribuce pro moderní Front-End vývoj. Postavená na Plug.vim, Coc a Lua." />
|
||||
<meta property="og:image" content="img/apollonvim.jpg" />
|
||||
<meta property="og:locale" content="cs_CZ" />
|
||||
<meta property="og:site_name" content="ApolloNvim" />
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="🚀 ApolloNvim | Neovim distribuce pro Front-End" />
|
||||
<meta name="twitter:description" content="Rychlá, lehká a plně konfigurovatelná Neovim distribuce pro moderní Front-End vývoj." />
|
||||
<meta name="twitter:image" content="img/apollonvim.jpg" />
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<!-- Stylesheets -->
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<link rel="stylesheet" href="css/animations.css" />
|
||||
|
||||
<!-- Canonical -->
|
||||
<link rel="canonical" href="https://apollo-nvim.lukan.cz/" />
|
||||
<!-- Umami -->
|
||||
<script defer src="https://analytics.lukan.cz/script.js" data-website-id="6bd59e17-6d55-483e-8e7a-8ec92fb4ede4"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Loading Screen -->
|
||||
<div id="loading-screen" class="loading-screen">
|
||||
<div class="loading-screen__content">
|
||||
<span class="loading-rocket">🚀</span>
|
||||
<div class="loading-bar"><div class="loading-bar__fill"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===================== NAVBAR ===================== -->
|
||||
<header class="navbar" id="navbar" role="banner">
|
||||
<nav class="navbar__inner" aria-label="Hlavní navigace">
|
||||
<a href="./index.html" class="navbar__logo" aria-label="ApolloNvim – Domů">
|
||||
🚀 <span>ApolloNvim</span>
|
||||
</a>
|
||||
|
||||
<ul class="navbar__links" id="nav-links" role="list">
|
||||
<li><a href="./index.html" class="navbar__link active" aria-current="page">Domů</a></li>
|
||||
<li><a href="html/instalace.html" class="navbar__link">Instalace</a></li>
|
||||
<li><a href="html/pluginy.html" class="navbar__link">Pluginy</a></li>
|
||||
<li><a href="html/tutorialy.html" class="navbar__link">Tutoriály</a></li>
|
||||
<li><a href="html/novinky.html" class="navbar__link">Novinky</a></li>
|
||||
<li><a href="html/zkratky.html" class="navbar__link">Zkratky</a></li>
|
||||
</ul>
|
||||
|
||||
<button
|
||||
id="hamburger"
|
||||
class="navbar__hamburger"
|
||||
aria-label="Otevřít menu"
|
||||
aria-expanded="false"
|
||||
aria-controls="mobile-menu"
|
||||
>
|
||||
<span></span><span></span><span></span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="navbar__mobile" id="mobile-menu" hidden>
|
||||
<ul role="list">
|
||||
<li><a href="./index.html" class="active">Domů</a></li>
|
||||
<li><a href="html/instalace.html">Instalace</a></li>
|
||||
<li><a href="html/pluginy.html">Pluginy</a></li>
|
||||
<li><a href="html/tutorialy.html">Tutoriály</a></li>
|
||||
<li><a href="html/novinky.html">Novinky</a></li>
|
||||
<li><a href="html/zkratky.html">Zkratky</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main-content">
|
||||
|
||||
<!-- ===================== HERO ===================== -->
|
||||
<section id="hero" class="hero" aria-labelledby="hero-title">
|
||||
|
||||
<!-- Particles canvas -->
|
||||
<canvas id="particles-canvas" aria-hidden="true"></canvas>
|
||||
|
||||
<div class="hero__content">
|
||||
|
||||
<div class="hero__badge animate-on-scroll">
|
||||
<span class="badge badge--purple">Verze 26.05</span>
|
||||
</div>
|
||||
|
||||
<h1 id="hero-title" class="hero__title animate-on-scroll delay-1">
|
||||
<span class="gradient-text">🚀 ApolloNvim</span>
|
||||
</h1>
|
||||
|
||||
<p class="hero__subtitle animate-on-scroll delay-2" aria-live="polite">
|
||||
<span class="typing-text" data-typewriter="Neovim distribuce pro moderní Front-End vývoj"></span>
|
||||
</p>
|
||||
|
||||
<p class="hero__description animate-on-scroll delay-3">
|
||||
Rychlý, lehký a plně konfigurovatelný.<br />
|
||||
Postavený na <strong>Plug.vim</strong>, <strong>Coc</strong> a <strong>Lua</strong>.
|
||||
</p>
|
||||
|
||||
<div class="hero__cta animate-on-scroll delay-4" role="group" aria-label="Hlavní akce">
|
||||
<a href="html/instalace.html" class="btn-primary">
|
||||
<span aria-hidden="true">🛠️</span> Začít instalaci
|
||||
</a>
|
||||
<a href="html/pluginy.html" class="btn-secondary">
|
||||
<span aria-hidden="true">🔌</span> Prohlédnout pluginy
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Scroll down indicator -->
|
||||
<div class="hero__scroll" aria-hidden="true">
|
||||
<span>Scroll</span>
|
||||
<div class="hero__scroll-arrow"></div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ===================== FEATURES ===================== -->
|
||||
<section id="features" class="section section--alt" aria-labelledby="features-title">
|
||||
<div class="container">
|
||||
|
||||
<header class="section-header">
|
||||
<h2 id="features-title" class="section-title animate-on-scroll">
|
||||
Proč ApolloNvim?
|
||||
<span class="section-title__line"></span>
|
||||
</h2>
|
||||
<p class="section__subtitle animate-on-scroll">
|
||||
Vše co potřebuješ pro moderní vývoj – přehledně na jednom místě.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="feature-grid">
|
||||
|
||||
<article class="feature-card animate-on-scroll" role="listitem">
|
||||
<div class="feature-icon" aria-hidden="true">⚡</div>
|
||||
<h3 class="feature-card__title">Rychlost & Lehkost</h3>
|
||||
<p class="feature-card__body">Minimální nároky na systém, maximální výkon. Spustí se během okamžiku.</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card animate-on-scroll delay-1" role="listitem">
|
||||
<div class="feature-icon" aria-hidden="true">🎨</div>
|
||||
<h3 class="feature-card__title">Krásný design</h3>
|
||||
<p class="feature-card__body">19+ barevných témat. Výchozí Dracula téma pro příjemné prostředí při každé práci.</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card animate-on-scroll delay-2" role="listitem">
|
||||
<div class="feature-icon" aria-hidden="true">🤖</div>
|
||||
<h3 class="feature-card__title">AI asistence</h3>
|
||||
<p class="feature-card__body">Codeium – zdarma alternativa ke GitHub Copilotu. Také TabNine pro inteligentní doplňování.</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card animate-on-scroll delay-3" role="listitem">
|
||||
<div class="feature-icon" aria-hidden="true">🔌</div>
|
||||
<h3 class="feature-card__title">Bohatá sada pluginů</h3>
|
||||
<p class="feature-card__body">AutoSave, NERDTree, TreeSitter, Colorizer a mnoho dalšího. Vše přednastaveno a připraveno.</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card animate-on-scroll" role="listitem">
|
||||
<div class="feature-icon" aria-hidden="true">🌐</div>
|
||||
<h3 class="feature-card__title">Front-End zaměření</h3>
|
||||
<p class="feature-card__body">Plná podpora HTML, CSS, JS, TypeScript, PHP, TailwindCSS. Editor navržený pro webové vývojáře.</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card animate-on-scroll delay-1" role="listitem">
|
||||
<div class="feature-icon" aria-hidden="true">📖</div>
|
||||
<h3 class="feature-card__title">Skvělá dokumentace</h3>
|
||||
<p class="feature-card__body">Podrobné návody v češtině i angličtině. Začneš produktivně pracovat od prvního dne.</p>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== EDITIONS ===================== -->
|
||||
<section id="editions" class="section" aria-labelledby="editions-title">
|
||||
<div class="container">
|
||||
|
||||
<header class="section-header">
|
||||
<h2 id="editions-title" class="section-title animate-on-scroll">
|
||||
Dvě edice pro každého
|
||||
<span class="section-title__line"></span>
|
||||
</h2>
|
||||
<p class="section__subtitle animate-on-scroll">
|
||||
Vyber si edici, která odpovídá tvým potřebám.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="editions-grid">
|
||||
|
||||
<!-- Starter Edition -->
|
||||
<article class="edition-card card animate-on-scroll" aria-labelledby="starter-title">
|
||||
<div class="edition-card__badge">
|
||||
<span class="badge badge--green">Pro začátečníky</span>
|
||||
</div>
|
||||
<h3 id="starter-title" class="edition-card__title">🚀 Starter Edition</h3>
|
||||
<p class="edition-card__desc">
|
||||
Zaměřená na práci s texty a Markdown. Ideální pro každého, kdo chce začít s Neovimem bez zbytečné složitosti.
|
||||
</p>
|
||||
<ul class="edition-card__features" role="list">
|
||||
<li>✅ Podpora Markdown a MDX</li>
|
||||
<li>✅ Zvýraznění syntaxe (TreeSitter)</li>
|
||||
<li>✅ 19+ barevných témat vč. Dracula</li>
|
||||
<li>✅ AutoSave – nikdy neztrácíš práci</li>
|
||||
<li>✅ Minimalistické, rychlé prostředí</li>
|
||||
<li>✅ NERDTree pro správu souborů</li>
|
||||
</ul>
|
||||
<a href="html/instalace.html" class="btn-secondary edition-card__btn" role="button">
|
||||
🚀 Instalovat Starter
|
||||
</a>
|
||||
</article>
|
||||
|
||||
<!-- FrontEnd Edition -->
|
||||
<article class="edition-card edition-card--highlighted card animate-on-scroll delay-1" aria-labelledby="frontend-title">
|
||||
<div class="edition-card__badge">
|
||||
<span class="badge badge--purple">⭐ Doporučeno</span>
|
||||
</div>
|
||||
<h3 id="frontend-title" class="edition-card__title">🌐 FrontEnd Edition</h3>
|
||||
<p class="edition-card__desc">
|
||||
Pro vývoj webů a webových aplikací. Inspirováno VSCode a WebStorm. Kompletní prostředí pro Front-End vývojáře.
|
||||
</p>
|
||||
<ul class="edition-card__features" role="list">
|
||||
<li>✅ Vše ze Starter Edition</li>
|
||||
<li>✅ LSP server přes Coc.nvim</li>
|
||||
<li>✅ HTML, CSS, JS, TS, PHP, TailwindCSS</li>
|
||||
<li>✅ AI: Codeium & TabNine (zdarma)</li>
|
||||
<li>✅ Emmet, Autopairs, Colorizer</li>
|
||||
<li>✅ Git integrace (fugitive + gitsigns)</li>
|
||||
</ul>
|
||||
<a href="html/instalace.html" class="btn-primary edition-card__btn" role="button">
|
||||
🛠️ Instalovat FrontEnd
|
||||
</a>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== STATS ===================== -->
|
||||
<section class="section section--alt stats-section" aria-label="Statistiky projektu">
|
||||
<div class="container">
|
||||
<div class="stats-grid">
|
||||
|
||||
<div class="stat-item animate-on-scroll" aria-label="19 a více barevných témat">
|
||||
<span class="stat-item__number" data-count="19" data-count-suffix="+">0+</span>
|
||||
<span class="stat-item__label">Barevných témat</span>
|
||||
</div>
|
||||
|
||||
<div class="stat-item animate-on-scroll delay-1" aria-label="20 a více pluginů">
|
||||
<span class="stat-item__number" data-count="20" data-count-suffix="+">0+</span>
|
||||
<span class="stat-item__label">Pluginů</span>
|
||||
</div>
|
||||
|
||||
<div class="stat-item animate-on-scroll delay-2" aria-label="20 a více tutoriálů">
|
||||
<span class="stat-item__number" data-count="20" data-count-suffix="+">0+</span>
|
||||
<span class="stat-item__label">Tutoriálů</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== TERMINAL PREVIEW ===================== -->
|
||||
<section id="install-preview" class="section" aria-labelledby="terminal-title">
|
||||
<div class="container">
|
||||
|
||||
<header class="section-header">
|
||||
<h2 id="terminal-title" class="section-title animate-on-scroll">
|
||||
Jednoduchá instalace
|
||||
<span class="section-title__line"></span>
|
||||
</h2>
|
||||
<p class="section__subtitle animate-on-scroll">
|
||||
Nastav si ApolloNvim ve třech krocích. Více detailů na stránce
|
||||
<a href="html/instalace.html">Instalace</a>.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="animate-on-scroll">
|
||||
|
||||
<div class="terminal-block" style="margin-bottom:16px;">
|
||||
<div class="terminal-block__header">
|
||||
<span class="terminal-block__dot terminal-block__dot--red"></span>
|
||||
<span class="terminal-block__dot terminal-block__dot--yellow"></span>
|
||||
<span class="terminal-block__dot terminal-block__dot--green"></span>
|
||||
<span class="terminal-block__title">Krok 1 – Klonování repozitáře</span>
|
||||
<button class="copy-btn" data-clipboard-text="git clone -b frontend https://codeberg.org/ApolloNvim/ApolloNvim2025.git ~/.config/nvim">Kopírovat</button>
|
||||
</div>
|
||||
<pre><code>git clone -b frontend https://codeberg.org/ApolloNvim/ApolloNvim2025.git ~/.config/nvim</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="terminal-block" style="margin-bottom:16px;">
|
||||
<div class="terminal-block__header">
|
||||
<span class="terminal-block__dot terminal-block__dot--red"></span>
|
||||
<span class="terminal-block__dot terminal-block__dot--yellow"></span>
|
||||
<span class="terminal-block__dot terminal-block__dot--green"></span>
|
||||
<span class="terminal-block__title">Krok 2 – Instalace pluginů (uvnitř Neovimu)</span>
|
||||
<button class="copy-btn" data-clipboard-text=":PlugInstall">Kopírovat</button>
|
||||
</div>
|
||||
<pre><code><span class="token-command">:PlugInstall</span></code></pre>
|
||||
</div>
|
||||
|
||||
<div class="terminal-block">
|
||||
<div class="terminal-block__header">
|
||||
<span class="terminal-block__dot terminal-block__dot--red"></span>
|
||||
<span class="terminal-block__dot terminal-block__dot--yellow"></span>
|
||||
<span class="terminal-block__dot terminal-block__dot--green"></span>
|
||||
<span class="terminal-block__title">Krok 3 – Instalace LSP serverů (FrontEnd Edition)</span>
|
||||
<button class="copy-btn" data-clipboard-text=":CocInstall coc-html coc-css coc-tsserver coc-phpls coc-emmet coc-prettier coc-eslint">Kopírovat</button>
|
||||
</div>
|
||||
<pre><code><span class="token-command">:CocInstall</span> coc-html coc-css coc-tsserver coc-phpls coc-emmet coc-prettier coc-eslint</code></pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="animate-on-scroll" style="margin-top:2rem;text-align:center;">
|
||||
<a href="html/instalace.html" class="btn-primary">
|
||||
📖 Kompletní průvodce instalací
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===================== COMMUNITY / LINKS ===================== -->
|
||||
<section id="community" class="section section--alt" aria-labelledby="community-title">
|
||||
<div class="container">
|
||||
|
||||
<header class="section-header">
|
||||
<h2 id="community-title" class="section-title animate-on-scroll">
|
||||
Komunita & Zdroje
|
||||
<span class="section-title__line"></span>
|
||||
</h2>
|
||||
<p class="section__subtitle animate-on-scroll">
|
||||
Připoj se ke komunitě nebo si prohlédni zdrojový kód.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="community-grid">
|
||||
|
||||
<a href="https://t.me/apollonvim" class="community-card card animate-on-scroll"
|
||||
target="_blank" rel="noopener noreferrer"
|
||||
aria-label="Telegram komunita ApolloNvim (otevře se v novém okně)">
|
||||
<div class="community-card__icon" aria-hidden="true">✈️</div>
|
||||
<h3 class="community-card__title">Telegram</h3>
|
||||
<p class="community-card__desc">Připoj se do Telegram skupiny. Sdílej tipy, ptej se a buď součástí komunity.</p>
|
||||
<span class="community-card__link">t.me/apollonvim →</span>
|
||||
</a>
|
||||
|
||||
<a href="https://codeberg.org/ApolloNvim/ApolloNvim2025" class="community-card card animate-on-scroll delay-1"
|
||||
target="_blank" rel="noopener noreferrer"
|
||||
aria-label="Codeberg repozitář ApolloNvim2025 (otevře se v novém okně)">
|
||||
<div class="community-card__icon" aria-hidden="true">🏔️</div>
|
||||
<h3 class="community-card__title">Codeberg</h3>
|
||||
<p class="community-card__desc">Zdrojový kód, issues a pull requesty. Otevřený a svobodný vývoj.</p>
|
||||
<span class="community-card__link">codeberg.org/ApolloNvim →</span>
|
||||
</a>
|
||||
|
||||
<a href="https://lukaskanka.cz/" class="community-card card animate-on-scroll delay-2"
|
||||
target="_blank" rel="noopener noreferrer"
|
||||
aria-label="Web autora Lukáše Kaňky (otevře se v novém okně)">
|
||||
<div class="community-card__icon" aria-hidden="true">🧑💻</div>
|
||||
<h3 class="community-card__title">Web autora</h3>
|
||||
<p class="community-card__desc">Osobní web Lukáše Kaňky – autora ApolloNvim. Další projekty a články.</p>
|
||||
<span class="community-card__link">lukaskanka.cz →</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- ===================== FOOTER ===================== -->
|
||||
<footer class="footer" role="contentinfo">
|
||||
<div class="footer__inner">
|
||||
<div class="footer__brand">
|
||||
<span class="footer__logo">🚀 ApolloNvim</span>
|
||||
<p>Neovim distribuce pro moderní Front-End vývoj.</p>
|
||||
<p class="footer__made">Made with ❤️ and ⌨️</p>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h4>Stránky</h4>
|
||||
<ul>
|
||||
<li><a href="./index.html">Domů</a></li>
|
||||
<li><a href="html/instalace.html">Instalace</a></li>
|
||||
<li><a href="html/pluginy.html">Pluginy</a></li>
|
||||
<li><a href="html/tutorialy.html">Tutoriály</a></li>
|
||||
<li><a href="html/novinky.html">Novinky</a></li>
|
||||
<li><a href="html/zkratky.html">Zkratky</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h4>Komunita</h4>
|
||||
<ul>
|
||||
<li><a href="https://t.me/apollonvim" target="_blank" rel="noopener noreferrer">Telegram</a></li>
|
||||
<li><a href="https://codeberg.org/ApolloNvim/ApolloNvim2025" target="_blank" rel="noopener noreferrer">Codeberg</a></li>
|
||||
<li><a href="https://lukaskanka.cz/" target="_blank" rel="noopener noreferrer">Web autora</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h4>Projekty</h4>
|
||||
<ul>
|
||||
<li><a href="https://arch-linux.cz/" target="_blank" rel="noopener noreferrer">ArchLinux CZ</a></li>
|
||||
<li><a href="https://searxng.cz/" target="_blank" rel="noopener noreferrer">SearXNG CZ</a></li>
|
||||
<li><a href="https://endeavouros.cz/" target="_blank" rel="noopener noreferrer">EndeavourOS CZ</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer__bottom">
|
||||
<p>Copyright © 2024 - 2025 🚀 ApolloNvim / Lukáš Kaňka</p>
|
||||
<span class="badge badge--purple">v25.04</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- ===================== SCRIPTS ===================== -->
|
||||
<script src="js/particles.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+647
@@ -0,0 +1,647 @@
|
||||
/**
|
||||
* main.js — ApolloNvim Web Interaktivita
|
||||
* Vanilla ES6+ JavaScript bez externích závislostí.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 8. LOADING SCREEN — fade-out při načtení stránky
|
||||
// (registrujeme jako první, aby běžel co nejdříve)
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initLoadingScreen() {
|
||||
const loader = document.getElementById("loading-screen");
|
||||
if (!loader) return;
|
||||
|
||||
const hide = () => {
|
||||
loader.classList.add("fade-out");
|
||||
// Po dokončení animace element skryjeme úplně
|
||||
loader.addEventListener(
|
||||
"transitionend",
|
||||
() => {
|
||||
loader.style.display = "none";
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
};
|
||||
|
||||
if (document.readyState === "complete") {
|
||||
hide();
|
||||
} else {
|
||||
window.addEventListener("load", hide);
|
||||
}
|
||||
}
|
||||
|
||||
initLoadingScreen();
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// Spuštění zbytku funkcí po načtení DOM
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// ── Pořadí inicializace ──────────────────────────────────────────────────
|
||||
initNavigation();
|
||||
initHamburger();
|
||||
initScrollAnimations();
|
||||
initTabs();
|
||||
initTypingEffects();
|
||||
initCounters();
|
||||
initCopyButtons();
|
||||
initPluginFilters();
|
||||
initTooltips();
|
||||
initBackToTop();
|
||||
});
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 1. NAVIGATION
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initNavigation() {
|
||||
const navbar =
|
||||
document.querySelector(".navbar") || document.querySelector("nav");
|
||||
const hamburger =
|
||||
document.querySelector(".hamburger") ||
|
||||
document.querySelector(".menu-toggle");
|
||||
const navMenu =
|
||||
document.querySelector(".nav-menu") || document.querySelector(".nav-links");
|
||||
const navLinks = document.querySelectorAll(".nav-links a, .nav-menu a");
|
||||
|
||||
// ── Třída "scrolled" při odrolování ──
|
||||
if (navbar) {
|
||||
const onScroll = () => {
|
||||
navbar.classList.toggle("scrolled", window.scrollY > 50);
|
||||
};
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
onScroll(); // Okamžitá inicializace
|
||||
}
|
||||
|
||||
// ── Hamburger menu toggle ──
|
||||
if (hamburger && navMenu) {
|
||||
hamburger.addEventListener("click", () => {
|
||||
const isOpen = navMenu.classList.toggle("open");
|
||||
hamburger.classList.toggle("active", isOpen);
|
||||
hamburger.setAttribute("aria-expanded", String(isOpen));
|
||||
// Zabránit scrollování pozadí při otevřeném menu
|
||||
document.body.classList.toggle("menu-open", isOpen);
|
||||
});
|
||||
}
|
||||
|
||||
// ── Smooth scroll pro anchor linky ──
|
||||
document.querySelectorAll('a[href^="#"]').forEach((link) => {
|
||||
link.addEventListener("click", (e) => {
|
||||
const targetId = link.getAttribute("href");
|
||||
if (targetId === "#") return;
|
||||
|
||||
const target = document.querySelector(targetId);
|
||||
if (!target) return;
|
||||
|
||||
e.preventDefault();
|
||||
const navHeight = navbar ? navbar.offsetHeight : 0;
|
||||
const top =
|
||||
target.getBoundingClientRect().top + window.scrollY - navHeight - 16;
|
||||
|
||||
window.scrollTo({ top, behavior: "smooth" });
|
||||
|
||||
// Zavření mobilního menu po kliknutí na odkaz
|
||||
closeNavMenu(navMenu, hamburger);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Zavření menu kliknutím mimo ──
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!navMenu || !hamburger) return;
|
||||
if (!navMenu.contains(e.target) && !hamburger.contains(e.target)) {
|
||||
closeNavMenu(navMenu, hamburger);
|
||||
}
|
||||
});
|
||||
|
||||
// ── Aktivní stav nav linků dle URL ──
|
||||
setActiveNavLink(navLinks);
|
||||
}
|
||||
|
||||
/** Zavře mobilní navigation menu */
|
||||
function closeNavMenu(navMenu, hamburger) {
|
||||
if (!navMenu) return;
|
||||
navMenu.classList.remove("open");
|
||||
document.body.classList.remove("menu-open");
|
||||
if (hamburger) {
|
||||
hamburger.classList.remove("active");
|
||||
hamburger.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
}
|
||||
|
||||
/** Označí aktivní nav link dle aktuální URL */
|
||||
function setActiveNavLink(links) {
|
||||
const path = window.location.pathname;
|
||||
links.forEach((link) => {
|
||||
const href = link.getAttribute("href") || "";
|
||||
// Přesná shoda nebo shoda části cesty (pro víceúrovňové URL)
|
||||
const isActive =
|
||||
href !== "#" &&
|
||||
(path === href ||
|
||||
path.endsWith(href) ||
|
||||
(href !== "/" && path.includes(href)));
|
||||
link.classList.toggle("active", isActive);
|
||||
if (isActive) link.setAttribute("aria-current", "page");
|
||||
});
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 2. SCROLL ANIMACE — Intersection Observer
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initScrollAnimations() {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add("visible");
|
||||
// Odpojit observer po animaci (každý element se animuje jen jednou)
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
threshold: 0.1,
|
||||
rootMargin: "0px 0px -40px 0px", // spustí se dříve, než element dosáhne dna okna
|
||||
},
|
||||
);
|
||||
|
||||
document
|
||||
.querySelectorAll(".animate-on-scroll")
|
||||
.forEach((el) => observer.observe(el));
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 3. TAB SYSTEM — CZ / EN přepínač (a jakékoli další záložky)
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initTabs() {
|
||||
// Podpora více nezávislých tab skupin na stránce
|
||||
document.querySelectorAll("[data-tab-group]").forEach((group) => {
|
||||
const buttons = group.querySelectorAll(".tab-btn");
|
||||
const contents = group.querySelectorAll(".tab-content");
|
||||
const storageKey = `apollo-tab-${group.dataset.tabGroup}`;
|
||||
|
||||
/** Přepne na záložku s daným id */
|
||||
const switchTab = (targetId, saveToStorage = true) => {
|
||||
buttons.forEach((btn) => {
|
||||
const active = btn.dataset.tab === targetId;
|
||||
btn.classList.toggle("active", active);
|
||||
btn.setAttribute("aria-selected", String(active));
|
||||
});
|
||||
|
||||
contents.forEach((content) => {
|
||||
if (content.dataset.tabContent === targetId) {
|
||||
content.classList.add("active");
|
||||
content.classList.remove("hidden");
|
||||
// Fade-in efekt
|
||||
content.style.opacity = "0";
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
content.style.opacity = "1";
|
||||
});
|
||||
});
|
||||
} else {
|
||||
content.classList.remove("active");
|
||||
content.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
if (saveToStorage) {
|
||||
try {
|
||||
localStorage.setItem(storageKey, targetId);
|
||||
} catch (_) {}
|
||||
}
|
||||
};
|
||||
|
||||
// Event listenery
|
||||
buttons.forEach((btn) => {
|
||||
btn.addEventListener("click", () => switchTab(btn.dataset.tab));
|
||||
});
|
||||
|
||||
// Obnova uložené preference
|
||||
let savedTab = null;
|
||||
try {
|
||||
savedTab = localStorage.getItem(storageKey);
|
||||
} catch (_) {}
|
||||
|
||||
const firstBtn = buttons[0];
|
||||
const initTab = savedTab || (firstBtn ? firstBtn.dataset.tab : null);
|
||||
if (initTab) switchTab(initTab, false);
|
||||
});
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 4. TERMINAL TYPING EFFECT
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Animuje psaní textu do elementu znak po znaku.
|
||||
* @param {HTMLElement} element - Cílový element
|
||||
* @param {string} text - Text, který se "napíše"
|
||||
* @param {number} speed - Prodleva mezi znaky v ms (default 60)
|
||||
* @param {Function} callback - Volitelný callback po dokončení
|
||||
*/
|
||||
function typeWriter(element, text, speed = 60, callback = null) {
|
||||
if (!element) return;
|
||||
element.textContent = "";
|
||||
element.style.borderRight = "2px solid currentColor"; // kurzor
|
||||
|
||||
let i = 0;
|
||||
const tick = () => {
|
||||
if (i < text.length) {
|
||||
element.textContent += text.charAt(i);
|
||||
i++;
|
||||
setTimeout(tick, speed + Math.random() * 30); // mírná variabilita
|
||||
} else {
|
||||
// Blikající kurzor po dokončení
|
||||
element.classList.add("typing-done");
|
||||
if (typeof callback === "function") callback();
|
||||
}
|
||||
};
|
||||
tick();
|
||||
}
|
||||
|
||||
/** Aplikuje typing efekt na všechny elementy s atributem [data-typewriter] */
|
||||
function initTypingEffects() {
|
||||
const elements = document.querySelectorAll("[data-typewriter]");
|
||||
if (!elements.length) return;
|
||||
|
||||
// Spustíme postupně s malým zpožděním pro každý element
|
||||
elements.forEach((el, idx) => {
|
||||
const text = el.dataset.typewriter || el.textContent;
|
||||
const speed = parseInt(el.dataset.typeSpeed, 10) || 60;
|
||||
const delay = parseInt(el.dataset.typeDelay, 10) || idx * 300;
|
||||
|
||||
el.textContent = ""; // Skryjeme původní text
|
||||
setTimeout(() => typeWriter(el, text, speed), delay);
|
||||
});
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 5. SMOOTH COUNTER ANIMATION
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initCounters() {
|
||||
const counters = document.querySelectorAll("[data-count]");
|
||||
if (!counters.length) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting) return;
|
||||
observer.unobserve(entry.target);
|
||||
animateCounter(entry.target);
|
||||
});
|
||||
},
|
||||
{ threshold: 0.5 },
|
||||
);
|
||||
|
||||
counters.forEach((el) => observer.observe(el));
|
||||
}
|
||||
|
||||
/**
|
||||
* Animuje čítač od 0 do cílové hodnoty.
|
||||
* @param {HTMLElement} el - Element s atributem data-count
|
||||
*/
|
||||
function animateCounter(el) {
|
||||
const target = parseInt(el.dataset.count, 10);
|
||||
const duration = parseInt(el.dataset.countDuration, 10) || 1500; // ms
|
||||
const suffix = el.dataset.countSuffix || "";
|
||||
const start = performance.now();
|
||||
|
||||
const update = (now) => {
|
||||
const elapsed = now - start;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
// Ease-out cubic
|
||||
const eased = 1 - Math.pow(1 - progress, 3);
|
||||
const current = Math.floor(eased * target);
|
||||
|
||||
el.textContent = current.toLocaleString("cs-CZ") + suffix;
|
||||
|
||||
if (progress < 1) requestAnimationFrame(update);
|
||||
else el.textContent = target.toLocaleString("cs-CZ") + suffix;
|
||||
};
|
||||
|
||||
requestAnimationFrame(update);
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 6. COPY TO CLIPBOARD
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initCopyButtons() {
|
||||
// Podpora pro explicitní tlačítka s třídou .copy-btn
|
||||
document.querySelectorAll(".copy-btn").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const targetSelector = btn.dataset.copyTarget;
|
||||
const codeEl = targetSelector
|
||||
? document.querySelector(targetSelector)
|
||||
: btn.closest(".code-block, pre")?.querySelector("code") ||
|
||||
btn.previousElementSibling;
|
||||
|
||||
if (!codeEl) return;
|
||||
copyText(codeEl.textContent.trim(), btn);
|
||||
});
|
||||
});
|
||||
|
||||
// Automaticky přidá tlačítka ke všem <pre><code> blokům bez tlačítka
|
||||
document.querySelectorAll("pre:not(.no-copy)").forEach((pre) => {
|
||||
if (pre.querySelector(".copy-btn")) return; // Již má tlačítko
|
||||
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "copy-btn copy-btn--auto";
|
||||
btn.textContent = "Kopírovat";
|
||||
btn.setAttribute("aria-label", "Kopírovat kód");
|
||||
pre.style.position = "relative";
|
||||
pre.appendChild(btn);
|
||||
|
||||
btn.addEventListener("click", () => {
|
||||
const code = pre.querySelector("code") || pre;
|
||||
copyText(code.textContent.trim(), btn);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Zkopíruje text do schránky a ukáže vizuální feedback na tlačítku.
|
||||
* @param {string} text - Text ke zkopírování
|
||||
* @param {HTMLElement} btn - Tlačítko pro vizuální feedback
|
||||
*/
|
||||
function copyText(text, btn) {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(() => {
|
||||
const original = btn.textContent;
|
||||
btn.textContent = "✓ Zkopírováno";
|
||||
btn.classList.add("copied");
|
||||
btn.disabled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
btn.textContent = original;
|
||||
btn.classList.remove("copied");
|
||||
btn.disabled = false;
|
||||
}, 2000);
|
||||
})
|
||||
.catch(() => {
|
||||
// Fallback pro starší prohlížeče
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
ta.style.cssText = "position:fixed;top:-9999px;left:-9999px";
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(ta);
|
||||
|
||||
btn.textContent = "✓ Zkopírováno";
|
||||
btn.classList.add("copied");
|
||||
setTimeout(() => {
|
||||
btn.textContent = "Kopírovat";
|
||||
btn.classList.remove("copied");
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 7. THEME PARTICLE COLORS (při hover na sekce)
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initParticleTheme() {
|
||||
if (!window.particleSystem) return;
|
||||
|
||||
// Mapování sekcí na palety barev
|
||||
const sectionThemes = {
|
||||
"#plugins": ["#bd93f9", "#ff79c6", "#ffffff"],
|
||||
"#themes": ["#ff79c6", "#ffb86c", "#ffffff"],
|
||||
"#installation": ["#50fa7b", "#8be9fd", "#ffffff"],
|
||||
"#tutorials": ["#f1fa8c", "#ffb86c", "#ffffff"],
|
||||
};
|
||||
|
||||
Object.entries(sectionThemes).forEach(([selector, colors]) => {
|
||||
const section = document.querySelector(selector);
|
||||
if (!section) return;
|
||||
|
||||
section.addEventListener("mouseenter", () => {
|
||||
window.particleSystem.config.colors = colors;
|
||||
});
|
||||
section.addEventListener("mouseleave", () => {
|
||||
// Obnovení výchozích barev
|
||||
window.particleSystem.config.colors = [
|
||||
"#ffffff",
|
||||
"#8be9fd",
|
||||
"#bd93f9",
|
||||
"#ff79c6",
|
||||
];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Spustíme až po inicializaci particle systému (je v DOMContentLoaded v particles.js)
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Malé zpoždění pro jistotu, že particleSystem je vytvořen
|
||||
setTimeout(initParticleTheme, 100);
|
||||
});
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 9. TOOLTIP SYSTEM
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initTooltips() {
|
||||
let tooltip = document.getElementById("tooltip-global");
|
||||
|
||||
// Vytvoření globálního tooltip elementu pokud neexistuje
|
||||
if (!tooltip) {
|
||||
tooltip = document.createElement("div");
|
||||
tooltip.id = "tooltip-global";
|
||||
tooltip.className = "tooltip";
|
||||
tooltip.setAttribute("role", "tooltip");
|
||||
tooltip.setAttribute("aria-hidden", "true");
|
||||
document.body.appendChild(tooltip);
|
||||
}
|
||||
|
||||
let hideTimer = null;
|
||||
|
||||
/** Zobrazí tooltip */
|
||||
const show = (text, anchorEl) => {
|
||||
clearTimeout(hideTimer);
|
||||
tooltip.textContent = text;
|
||||
tooltip.style.display = "block";
|
||||
tooltip.removeAttribute("aria-hidden");
|
||||
|
||||
// Pozicování nad elementem
|
||||
const rect = anchorEl.getBoundingClientRect();
|
||||
const tRect = tooltip.getBoundingClientRect();
|
||||
let left = rect.left + rect.width / 2 - tRect.width / 2 + window.scrollX;
|
||||
let top = rect.top - tRect.height - 8 + window.scrollY;
|
||||
|
||||
// Zamezit přetečení přes pravý okraj
|
||||
if (left + tRect.width > window.innerWidth - 8) {
|
||||
left = window.innerWidth - tRect.width - 8;
|
||||
}
|
||||
if (left < 8) left = 8;
|
||||
|
||||
tooltip.style.left = `${left}px`;
|
||||
tooltip.style.top = `${top}px`;
|
||||
tooltip.classList.add("visible");
|
||||
};
|
||||
|
||||
/** Skryje tooltip */
|
||||
const hide = () => {
|
||||
hideTimer = setTimeout(() => {
|
||||
tooltip.classList.remove("visible");
|
||||
tooltip.setAttribute("aria-hidden", "true");
|
||||
}, 150);
|
||||
};
|
||||
|
||||
// Event delegation — posloucháme na document
|
||||
document.addEventListener("mouseover", (e) => {
|
||||
const target = e.target.closest("[data-tooltip]");
|
||||
if (target) show(target.dataset.tooltip, target);
|
||||
});
|
||||
|
||||
document.addEventListener("mouseout", (e) => {
|
||||
if (e.target.closest("[data-tooltip]")) hide();
|
||||
});
|
||||
|
||||
// Klávesnicová přístupnost
|
||||
document.addEventListener("focusin", (e) => {
|
||||
const target = e.target.closest("[data-tooltip]");
|
||||
if (target) show(target.dataset.tooltip, target);
|
||||
});
|
||||
|
||||
document.addEventListener("focusout", (e) => {
|
||||
if (e.target.closest("[data-tooltip]")) hide();
|
||||
});
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 10. BACK TO TOP BUTTON
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initBackToTop() {
|
||||
let btn = document.getElementById("back-to-top");
|
||||
|
||||
// Vytvoření tlačítka pokud neexistuje v HTML
|
||||
if (!btn) {
|
||||
btn = document.createElement("button");
|
||||
btn.id = "back-to-top";
|
||||
btn.className = "back-to-top";
|
||||
btn.innerHTML = "⇧"; // ⇧
|
||||
btn.setAttribute("aria-label", "Zpět nahoru");
|
||||
btn.setAttribute("title", "Zpět nahoru");
|
||||
document.body.appendChild(btn);
|
||||
}
|
||||
|
||||
// Zobrazení/skrytí dle scroll pozice
|
||||
const SHOW_THRESHOLD = 300;
|
||||
|
||||
const toggleVisibility = () => {
|
||||
const visible = window.scrollY > SHOW_THRESHOLD;
|
||||
btn.classList.toggle("visible", visible);
|
||||
btn.setAttribute("aria-hidden", String(!visible));
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", toggleVisibility, { passive: true });
|
||||
toggleVisibility();
|
||||
|
||||
// Scroll nahoru po kliknutí
|
||||
btn.addEventListener("click", () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
});
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// UTILITY — veřejné API pro použití z HTML atributů nebo jiných skriptů
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 11. HAMBURGER MENU (pro HTML strukturu s .navbar__hamburger a .navbar__mobile)
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initHamburger() {
|
||||
const hamburger = document.getElementById("hamburger");
|
||||
const mobileMenu = document.getElementById("mobile-menu");
|
||||
if (!hamburger || !mobileMenu) return;
|
||||
|
||||
hamburger.addEventListener("click", () => {
|
||||
const isHidden = mobileMenu.hasAttribute("hidden");
|
||||
if (isHidden) {
|
||||
mobileMenu.removeAttribute("hidden");
|
||||
hamburger.setAttribute("aria-expanded", "true");
|
||||
hamburger.classList.add("open");
|
||||
} else {
|
||||
mobileMenu.setAttribute("hidden", "");
|
||||
hamburger.setAttribute("aria-expanded", "false");
|
||||
hamburger.classList.remove("open");
|
||||
}
|
||||
});
|
||||
|
||||
// Zavřít při kliknutí mimo menu
|
||||
document.addEventListener("click", (e) => {
|
||||
if (
|
||||
!mobileMenu.hasAttribute("hidden") &&
|
||||
!mobileMenu.contains(e.target) &&
|
||||
!hamburger.contains(e.target)
|
||||
) {
|
||||
mobileMenu.setAttribute("hidden", "");
|
||||
hamburger.setAttribute("aria-expanded", "false");
|
||||
hamburger.classList.remove("open");
|
||||
}
|
||||
});
|
||||
|
||||
// Zavřít při kliknutí na odkaz v menu
|
||||
mobileMenu.querySelectorAll("a").forEach((link) => {
|
||||
link.addEventListener("click", () => {
|
||||
mobileMenu.setAttribute("hidden", "");
|
||||
hamburger.setAttribute("aria-expanded", "false");
|
||||
hamburger.classList.remove("open");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// 12. PLUGIN CATEGORY FILTER (pro pluginy.html)
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function initPluginFilters() {
|
||||
const filterContainer = document.getElementById("plugin-filters");
|
||||
const pluginsGrid = document.getElementById("plugins-grid");
|
||||
if (!filterContainer || !pluginsGrid) return;
|
||||
|
||||
const buttons = filterContainer.querySelectorAll(".filter-btn");
|
||||
const cards = pluginsGrid.querySelectorAll(".plugin-card");
|
||||
|
||||
buttons.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
// Aktualizace aktivního tlačítka
|
||||
buttons.forEach((b) => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
|
||||
const filter = btn.dataset.filter;
|
||||
|
||||
// Zobrazit / skrýt karty s animací
|
||||
cards.forEach((card) => {
|
||||
const category = card.dataset.category || "";
|
||||
const match = filter === "all" || category === filter;
|
||||
|
||||
if (match) {
|
||||
card.classList.remove("hidden");
|
||||
// Krátké zpoždění pro plynulý efekt
|
||||
requestAnimationFrame(() => {
|
||||
card.style.opacity = "1";
|
||||
card.style.transform = "";
|
||||
});
|
||||
} else {
|
||||
card.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
window.ApolloNvim = {
|
||||
typeWriter,
|
||||
copyText,
|
||||
};
|
||||
@@ -0,0 +1,358 @@
|
||||
/**
|
||||
* particles.js — ApolloNvim Vesmírný Particle Systém
|
||||
* Canvas-based animace hvězd s parallaxem, blikáním, spojeními a padajícími hvězdami.
|
||||
*/
|
||||
|
||||
class ParticleSystem {
|
||||
constructor(canvasId) {
|
||||
this.canvas = document.getElementById(canvasId);
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
|
||||
// Konfigurace částic
|
||||
this.config = {
|
||||
count: 200,
|
||||
minSize: 0.5,
|
||||
maxSize: 2.5,
|
||||
colors: ['#ffffff', '#8be9fd', '#bd93f9', '#ff79c6'],
|
||||
connectionDist: 100, // px — maximální vzdálenost pro spojovací linky
|
||||
driftSpeed: 0.25, // max px/frame náhodný drift
|
||||
parallaxStrength: 0.04, // síla parallax efektu myši
|
||||
blinkChance: 0.35, // podíl hvězd, které blikají
|
||||
shootingInterval:[5000, 8000], // ms min/max mezi padajícími hvězdami
|
||||
};
|
||||
|
||||
// Stav myši
|
||||
this.mouse = { x: 0, y: 0, active: false };
|
||||
|
||||
// Pole částic a padající hvězdy
|
||||
this.particles = [];
|
||||
this.shootingStars = [];
|
||||
|
||||
this._animFrameId = null;
|
||||
this._shootingTimer = null;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
// ─── Inicializace ─────────────────────────────────────────────────────────
|
||||
|
||||
init() {
|
||||
this.resize();
|
||||
this.createParticles();
|
||||
this._bindEvents();
|
||||
this._scheduleShootingStar();
|
||||
this.animate();
|
||||
}
|
||||
|
||||
/** Nastaví rozměry canvasu dle okna */
|
||||
resize() {
|
||||
this.canvas.width = window.innerWidth;
|
||||
this.canvas.height = window.innerHeight;
|
||||
}
|
||||
|
||||
/** Vytvoří pole hvězdičkových částic */
|
||||
createParticles() {
|
||||
this.particles = [];
|
||||
const { count, minSize, maxSize, colors, blinkChance, driftSpeed } = this.config;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const blinks = Math.random() < blinkChance;
|
||||
this.particles.push({
|
||||
x: Math.random() * this.canvas.width,
|
||||
y: Math.random() * this.canvas.height,
|
||||
baseX: 0, // bude nastaveno níže
|
||||
baseY: 0,
|
||||
size: minSize + Math.random() * (maxSize - minSize),
|
||||
color: colors[Math.floor(Math.random() * colors.length)],
|
||||
// drift – každá hvězda má svůj vektor
|
||||
vx: (Math.random() - 0.5) * driftSpeed,
|
||||
vy: (Math.random() - 0.5) * driftSpeed,
|
||||
// blikání
|
||||
blinks,
|
||||
opacity: blinks ? 0.3 + Math.random() * 0.7 : 0.6 + Math.random() * 0.4,
|
||||
blinkSpeed: blinks ? 0.005 + Math.random() * 0.015 : 0,
|
||||
blinkDir: Math.random() < 0.5 ? 1 : -1,
|
||||
// parallax offset (akumulovaný)
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
});
|
||||
|
||||
// baseX/baseY = počáteční pozice (pro parallax výpočet)
|
||||
this.particles[i].baseX = this.particles[i].x;
|
||||
this.particles[i].baseY = this.particles[i].y;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Hlavní animační smyčka ───────────────────────────────────────────────
|
||||
|
||||
animate() {
|
||||
this._animFrameId = requestAnimationFrame(() => this.animate());
|
||||
const ctx = this.ctx;
|
||||
|
||||
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
// Výpočet parallax posunu středu obrazovky
|
||||
const cx = this.canvas.width / 2;
|
||||
const cy = this.canvas.height / 2;
|
||||
const mouseOffsetX = this.mouse.active
|
||||
? (this.mouse.x - cx) * this.config.parallaxStrength
|
||||
: 0;
|
||||
const mouseOffsetY = this.mouse.active
|
||||
? (this.mouse.y - cy) * this.config.parallaxStrength
|
||||
: 0;
|
||||
|
||||
// ── Aktualizace a kreslení hvězd ──
|
||||
for (const p of this.particles) {
|
||||
// Drift
|
||||
p.baseX += p.vx;
|
||||
p.baseY += p.vy;
|
||||
|
||||
// Wrap-around přes okraje
|
||||
if (p.baseX < -10) p.baseX = this.canvas.width + 10;
|
||||
if (p.baseX > this.canvas.width + 10) p.baseX = -10;
|
||||
if (p.baseY < -10) p.baseY = this.canvas.height + 10;
|
||||
if (p.baseY > this.canvas.height + 10) p.baseY = -10;
|
||||
|
||||
// Parallax offset (každá hvězda reaguje trochu jinak dle velikosti)
|
||||
const parallaxFactor = p.size / this.config.maxSize;
|
||||
p.x = p.baseX + mouseOffsetX * parallaxFactor;
|
||||
p.y = p.baseY + mouseOffsetY * parallaxFactor;
|
||||
|
||||
// Blikání opacity
|
||||
if (p.blinks) {
|
||||
p.opacity += p.blinkSpeed * p.blinkDir;
|
||||
if (p.opacity >= 1.0) { p.opacity = 1.0; p.blinkDir = -1; }
|
||||
if (p.opacity <= 0.15) { p.opacity = 0.15; p.blinkDir = 1; }
|
||||
}
|
||||
|
||||
// Kreslení hvězdy jako malého hvězdičkového tvaru
|
||||
this._drawStar(p);
|
||||
}
|
||||
|
||||
// ── Spojovací linky ──
|
||||
this.drawConnections();
|
||||
|
||||
// ── Padající hvězdy ──
|
||||
this._updateShootingStars();
|
||||
}
|
||||
|
||||
/** Nakreslí hvězdu (5-cípý tvar pro větší, kroužek pro malé) */
|
||||
_drawStar(p) {
|
||||
const ctx = this.ctx;
|
||||
ctx.save();
|
||||
ctx.globalAlpha = p.opacity;
|
||||
ctx.fillStyle = p.color;
|
||||
|
||||
if (p.size >= 1.5) {
|
||||
// Pěticípá hvězdička pro větší částice
|
||||
this._drawStarShape(ctx, p.x, p.y, p.size * 2, p.size, 5);
|
||||
} else {
|
||||
// Jednoduchý kroužek pro malé hvězdičky
|
||||
ctx.beginPath();
|
||||
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// Jemná záře kolem hvězdy
|
||||
if (p.size > 1.2) {
|
||||
const grd = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 4);
|
||||
grd.addColorStop(0, this._hexAlpha(p.color, 0.3 * p.opacity));
|
||||
grd.addColorStop(1, this._hexAlpha(p.color, 0));
|
||||
ctx.fillStyle = grd;
|
||||
ctx.beginPath();
|
||||
ctx.arc(p.x, p.y, p.size * 4, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/** Nakreslí pěticípou hvězdičku */
|
||||
_drawStarShape(ctx, cx, cy, outerR, innerR, points) {
|
||||
const step = Math.PI / points;
|
||||
ctx.beginPath();
|
||||
for (let i = 0; i < points * 2; i++) {
|
||||
const r = i % 2 === 0 ? outerR : innerR;
|
||||
const angle = i * step - Math.PI / 2;
|
||||
const x = cx + Math.cos(angle) * r;
|
||||
const y = cy + Math.sin(angle) * r;
|
||||
i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// ─── Spojovací linky ─────────────────────────────────────────────────────
|
||||
|
||||
drawConnections() {
|
||||
const ctx = this.ctx;
|
||||
const dist = this.config.connectionDist;
|
||||
const ps = this.particles;
|
||||
|
||||
for (let i = 0; i < ps.length; i++) {
|
||||
for (let j = i + 1; j < ps.length; j++) {
|
||||
const dx = ps[i].x - ps[j].x;
|
||||
const dy = ps[i].y - ps[j].y;
|
||||
const d = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (d < dist) {
|
||||
const alpha = (1 - d / dist) * 0.25; // max 25% opacity
|
||||
ctx.save();
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.strokeStyle = '#8be9fd';
|
||||
ctx.lineWidth = 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(ps[i].x, ps[i].y);
|
||||
ctx.lineTo(ps[j].x, ps[j].y);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Padající hvězdy ─────────────────────────────────────────────────────
|
||||
|
||||
/** Naplánuje další padající hvězdu s náhodným zpožděním */
|
||||
_scheduleShootingStar() {
|
||||
const [min, max] = this.config.shootingInterval;
|
||||
const delay = min + Math.random() * (max - min);
|
||||
this._shootingTimer = setTimeout(() => {
|
||||
this.createShootingStar();
|
||||
this._scheduleShootingStar(); // naplánuje další
|
||||
}, delay);
|
||||
}
|
||||
|
||||
/** Vytvoří novou padající hvězdu */
|
||||
createShootingStar() {
|
||||
const angle = (15 + Math.random() * 25) * (Math.PI / 180); // 15°–40°
|
||||
const speed = 6 + Math.random() * 8;
|
||||
const length = 80 + Math.random() * 120;
|
||||
|
||||
this.shootingStars.push({
|
||||
x: Math.random() * this.canvas.width * 0.8,
|
||||
y: Math.random() * this.canvas.height * 0.3,
|
||||
vx: Math.cos(angle) * speed,
|
||||
vy: Math.sin(angle) * speed,
|
||||
length,
|
||||
opacity: 1,
|
||||
trail: [], // pole minulých pozic pro ocas
|
||||
maxTrail: Math.floor(length / speed),
|
||||
done: false,
|
||||
});
|
||||
}
|
||||
|
||||
/** Aktualizuje a kreslí všechny padající hvězdy */
|
||||
_updateShootingStars() {
|
||||
const ctx = this.ctx;
|
||||
this.shootingStars = this.shootingStars.filter(s => !s.done);
|
||||
|
||||
for (const s of this.shootingStars) {
|
||||
// Uloží pozici do ocasu
|
||||
s.trail.push({ x: s.x, y: s.y });
|
||||
if (s.trail.length > s.maxTrail) s.trail.shift();
|
||||
|
||||
// Pohyb
|
||||
s.x += s.vx;
|
||||
s.y += s.vy;
|
||||
s.opacity -= 0.012;
|
||||
|
||||
if (s.opacity <= 0 || s.x > this.canvas.width + 50 || s.y > this.canvas.height + 50) {
|
||||
s.done = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Kreslení ocasu (gradient linka)
|
||||
if (s.trail.length > 1) {
|
||||
const grad = ctx.createLinearGradient(
|
||||
s.trail[0].x, s.trail[0].y,
|
||||
s.x, s.y
|
||||
);
|
||||
grad.addColorStop(0, `rgba(255,255,255,0)`);
|
||||
grad.addColorStop(1, `rgba(255,255,255,${s.opacity})`);
|
||||
|
||||
ctx.save();
|
||||
ctx.strokeStyle = grad;
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.shadowColor = '#ffffff';
|
||||
ctx.shadowBlur = 6;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(s.trail[0].x, s.trail[0].y);
|
||||
for (const pt of s.trail) ctx.lineTo(pt.x, pt.y);
|
||||
ctx.lineTo(s.x, s.y);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// Jasná tečka na špičce
|
||||
ctx.save();
|
||||
ctx.globalAlpha = s.opacity;
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.shadowColor = '#8be9fd';
|
||||
ctx.shadowBlur = 10;
|
||||
ctx.beginPath();
|
||||
ctx.arc(s.x, s.y, 1.5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Pomocné metody ───────────────────────────────────────────────────────
|
||||
|
||||
/** Převede HEX barvu na rgba string s danou průhledností */
|
||||
_hexAlpha(hex, alpha) {
|
||||
const r = parseInt(hex.slice(1, 3), 16);
|
||||
const g = parseInt(hex.slice(3, 5), 16);
|
||||
const b = parseInt(hex.slice(5, 7), 16);
|
||||
return `rgba(${r},${g},${b},${alpha})`;
|
||||
}
|
||||
|
||||
/** Naváže posluchače událostí */
|
||||
_bindEvents() {
|
||||
// Resize
|
||||
window.addEventListener('resize', () => {
|
||||
this.resize();
|
||||
// Po resize přepočítá baseX/baseY aby hvězdy zůstaly proporcionálně
|
||||
const scaleX = this.canvas.width / (this.canvas.width || 1);
|
||||
const scaleY = this.canvas.height / (this.canvas.height || 1);
|
||||
for (const p of this.particles) {
|
||||
p.baseX = Math.random() * this.canvas.width;
|
||||
p.baseY = Math.random() * this.canvas.height;
|
||||
}
|
||||
});
|
||||
|
||||
// Mouse parallax
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
this.mouse.x = e.clientX;
|
||||
this.mouse.y = e.clientY;
|
||||
this.mouse.active = true;
|
||||
});
|
||||
|
||||
// Dotyk (mobile)
|
||||
window.addEventListener('touchmove', (e) => {
|
||||
if (e.touches.length > 0) {
|
||||
this.mouse.x = e.touches[0].clientX;
|
||||
this.mouse.y = e.touches[0].clientY;
|
||||
this.mouse.active = true;
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
window.addEventListener('mouseleave', () => {
|
||||
this.mouse.active = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** Zastaví animaci a časovače */
|
||||
destroy() {
|
||||
if (this._animFrameId) cancelAnimationFrame(this._animFrameId);
|
||||
if (this._shootingTimer) clearTimeout(this._shootingTimer);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Auto-init při načtení DOM ─────────────────────────────────────────────
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (document.getElementById('particles-canvas')) {
|
||||
window.particleSystem = new ParticleSystem('particles-canvas');
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user