Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

A two-page website for [LinuxPlayer](https://github.com/teamkrope/linuxplayer). It uses real application screenshots, the final bird mark shared with the desktop app, and an illustrative frame for the canvas demo. The site is self-contained and has no runtime or build dependencies beyond Node.js.

`index.html` presents the player as a product showcase. Shared components and the download page use `styles.css`; homepage presentation lives in `showcase.css`. `motion.js` handles blur reveals and subtle scene movement, with a reduced-motion fallback. Keep the real screenshots and interactive gallery/ratio demo intact when changing the layout.

## Preview locally

```bash
Expand Down
32 changes: 14 additions & 18 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<link rel="icon" href="assets/linuxplayer-bird.png" type="image/png">
<link rel="preload" href="assets/screenshots/playback-v1-coast.png" as="image" fetchpriority="high">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="showcase.css">
<script type="application/ld+json">{"@context":"https://schema.org","@type":"SoftwareApplication","name":"LinuxPlayer","applicationCategory":"MultimediaApplication","operatingSystem":"Linux","description":"An open-source desktop video player for Linux with local playback, subtitles, mini-player, and canvas ratio previews.","license":"https://github.com/teamkrope/linuxplayer/blob/main/LICENSE","softwareHelp":"https://github.com/teamkrope/linuxplayer/blob/main/SUPPORT.md","downloadUrl":"https://github.com/teamkrope/linuxplayer/releases/latest/download/linuxplayer_amd64.deb","isAccessibleForFree":true,"author":{"@type":"Organization","name":"TeamKrope"}}</script>
<script defer src="motion.js"></script>
<script defer src="script.js"></script>
Expand All @@ -33,33 +34,28 @@
</div></header>
<main id="main">
<section class="hero" aria-labelledby="hero-title"><div class="container hero-inner">
<div class="hero-copy"><h1 id="hero-title">Video playback,<br><em>refined for Linux.</em></h1><div class="hero-actions"><a class="button button-primary" href="https://github.com/teamkrope/linuxplayer/releases/latest/download/linuxplayer_amd64.deb">Download <span aria-hidden="true">↓</span></a><a class="button button-secondary" href="https://github.com/teamkrope/linuxplayer" target="_blank" rel="noopener noreferrer">View source</a></div></div>
<figure class="hero-shot"><div class="shot-frame"><img src="assets/screenshots/playback-v1-coast.png" alt="Actual LinuxPlayer 1.0 window playing a local video with controls visible" width="1100" height="680" fetchpriority="high"></div></figure>
<div class="hero-copy" data-reveal="copy"><span class="section-kicker">LinuxPlayer</span><h1 id="hero-title">Get lost in the video.<br><em>Not the player.</em></h1><p>Beautiful playback, built for Linux.</p></div>
<figure class="hero-shot" data-reveal="visual" data-scroll-scene><div class="shot-frame"><img src="assets/screenshots/playback-v1-coast.png" alt="Actual LinuxPlayer 1.0 window playing a local video with controls visible" width="1100" height="680" fetchpriority="high"></div></figure>
<div class="hero-actions" data-reveal="copy"><a class="button button-primary" href="https://github.com/teamkrope/linuxplayer/releases/latest/download/linuxplayer_amd64.deb">Download <span aria-hidden="true">↓</span></a><a class="button button-secondary" href="#features">Explore the player <span aria-hidden="true">↓</span></a></div>
</div></section>
<section class="section features-section" id="features" aria-labelledby="features-title"><div class="container">
<div class="section-head"><h2 id="features-title">Everything within reach.</h2></div>
<div class="feature-layout"><figure class="feature-shot"><img src="assets/screenshots/hidden-v1-coast.png" alt="Actual LinuxPlayer screenshot with playback controls hidden" width="1100" height="680" loading="lazy"></figure><div class="feature-list">
<article><div><h3>Focused playback</h3><p>Seek, skip, loop, change speed, and save frames.</p></div></article>
<article><div><h3>Floating mini-player</h3><p>Keep the video visible while you work.</p></div></article>
<article><div><h3>Audio &amp; subtitles</h3><p>Choose tracks or load subtitles.</p></div></article>
<article><div><h3>Desktop integration</h3><p>Open files from your desktop and set LinuxPlayer as default.</p></div></article>
<article><div><h3>Responsive feedback</h3><p>Subtle cues confirm every action.</p></div></article>
<article><div><h3>Personal controls</h3><p>Shortcuts, canvas ratios, and your system accent.</p></div></article>
</div></div>
<section class="section features-section" id="features" aria-labelledby="features-title"><div class="container focus-layout">
<div class="focus-intro" data-reveal="copy"><span class="section-kicker">The viewing experience</span><h2 id="features-title">Only the video,<br><em>when you want it.</em></h2></div>
<figure class="focus-visual" data-reveal="visual" data-scroll-scene><img src="assets/screenshots/hidden-v1-coast.png" alt="Actual LinuxPlayer screenshot with playback controls hidden" width="1100" height="680" loading="lazy"></figure>
<div class="focus-points" aria-label="Playback highlights"><article data-reveal="copy"><span>01</span><h3>Controls on demand.</h3><p>They disappear as playback begins.</p></article><article data-reveal="copy"><span>02</span><h3>Stay in the picture.</h3><p>Lock the interface to show only video.</p></article><article data-reveal="copy"><span>03</span><h3>Every action feels clear.</h3><p>Subtle feedback for play, pause, and seeking.</p></article></div>
</div></section>
<section class="section pip-section" aria-labelledby="pip-title"><div class="container pip-grid">
<div class="pip-copy"><h2 id="pip-title">Watch while you work.</h2></div>
<figure class="workspace-figure"><div class="workspace-stage"><div class="work-window window-back"><div class="work-window-bar"><span>Files</span><i></i><i></i></div><div class="file-list"><span><b></b> Project notes</span><span><b></b> Media</span><span><b></b> Downloads</span></div></div><div class="work-window window-front"><div class="work-window-bar"><span>Workspace</span><i></i><i></i></div><div class="document-lines"><strong>Today’s work</strong><span></span><span></span><span></span><span></span><span></span></div></div><img class="pip-capture" src="assets/screenshots/mini-player-v1-coast.png" alt="Actual LinuxPlayer mini-player screenshot floating over an illustrative desktop workspace" width="420" height="256" loading="lazy"></div></figure>
<div class="pip-copy" data-reveal="copy"><span class="section-kicker">Mini-player</span><h2 id="pip-title">Your video<br>comes along.</h2><p>Keep watching in a compact window while you work.</p></div>
<figure class="workspace-figure" data-reveal="visual" data-scroll-scene><div class="workspace-stage"><div class="work-window window-back"><div class="work-window-bar"><span>Files</span><i></i><i></i></div><div class="file-list"><span><b></b> Project notes</span><span><b></b> Media</span><span><b></b> Downloads</span></div></div><div class="work-window window-front"><div class="work-window-bar"><span>Workspace</span><i></i><i></i></div><div class="document-lines"><strong>Today’s work</strong><span></span><span></span><span></span><span></span><span></span></div></div><img class="pip-capture" src="assets/screenshots/mini-player-v1-coast.png" alt="Actual LinuxPlayer mini-player screenshot floating over an illustrative desktop workspace" width="420" height="256" loading="lazy"></div></figure>
</div></section>
<section class="section canvas-section" id="canvas" aria-labelledby="canvas-title"><div class="container">
<div class="section-head canvas-head"><h2 id="canvas-title">Preview any shape.</h2></div>
<div class="ratio-demo" aria-label="Interactive illustration of the canvas preview feature">
<div class="section-head canvas-head" data-reveal="copy"><span class="section-kicker">Canvas preview</span><h2 id="canvas-title">One video.<br><em>Every frame.</em></h2><p>Try a new canvas ratio without changing the file.</p></div>
<div class="ratio-demo" aria-label="Interactive illustration of the canvas preview feature" data-reveal="visual">
<div class="demo-source"><div class="demo-label"><strong>Source video</strong><span>16:9 · unchanged</span></div><div class="source-frame"><img src="assets/ratio-source.webp" alt="Illustrative widescreen frame showing a person on a coastal overlook and a lighthouse" width="1672" height="941" loading="lazy"></div></div>
<div class="demo-preview"><div class="demo-label"><strong>Canvas preview</strong><span id="ratio-readout">16:9 · Fit</span></div><div class="ratio-stage"><div class="demo-canvas" id="demo-canvas"><img id="demo-image" src="assets/ratio-source.webp" alt="Same illustrative frame inside the selected canvas" width="1672" height="941" loading="lazy"></div></div></div>
<div class="demo-controls"><div><span class="control-label">Canvas ratio</span><div class="segmented ratio-controls" role="group" aria-label="Canvas ratio"><button type="button" class="active" data-ratio="16/9" aria-pressed="true">16:9</button><button type="button" data-ratio="9/16" aria-pressed="false">9:16</button><button type="button" data-ratio="1/1" aria-pressed="false">1:1</button><button type="button" data-ratio="4/5" aria-pressed="false">4:5</button><button type="button" data-ratio="2/1" aria-pressed="false">2:1</button></div></div><div><span class="control-label">Video in canvas</span><div class="segmented fit-controls" role="group" aria-label="Video fit mode"><button type="button" class="active" data-fit="contain" aria-pressed="true">Fit</button><button type="button" data-fit="cover" aria-pressed="false">Fill</button></div></div></div>
</div><p class="demo-note">Preview only. Your video stays untouched. <a href="https://github.com/teamkrope/linuxplayer/blob/main/docs/INTERFACE.md" target="_blank" rel="noopener noreferrer">Feature guide</a></p>
</div><p class="demo-note">Your video stays untouched. <a href="https://github.com/teamkrope/linuxplayer/blob/main/docs/INTERFACE.md" target="_blank" rel="noopener noreferrer">Feature guide</a></p>
</div></section>
<section class="section preview-section" id="preview" aria-labelledby="preview-title"><div class="container"><div class="section-head"><h2 id="preview-title">See the player.</h2></div><div class="gallery"><div class="gallery-tabs" role="tablist" aria-label="Application screenshots"><button type="button" class="active" role="tab" id="tab-playback" aria-selected="true" aria-controls="gallery-panel" data-shot="playback">Playback</button><button type="button" role="tab" id="tab-options" aria-selected="false" aria-controls="gallery-panel" data-shot="options" tabindex="-1">Controls</button><button type="button" role="tab" id="tab-hidden" aria-selected="false" aria-controls="gallery-panel" data-shot="hidden" tabindex="-1">Video only</button><button type="button" role="tab" id="tab-mini" aria-selected="false" aria-controls="gallery-panel" data-shot="mini" tabindex="-1">Mini-player</button></div><div class="gallery-panel" id="gallery-panel" role="tabpanel" aria-labelledby="tab-playback"><div class="gallery-image-wrap" id="gallery-image-wrap"><div class="gallery-workspace" aria-hidden="true"><div></div><div></div><div></div></div><img id="gallery-image" src="assets/screenshots/playback-v1-coast.png" alt="LinuxPlayer playback interface" width="1100" height="680" loading="lazy"></div><div class="gallery-caption"><div><strong id="gallery-title">Playback</strong></div><a id="gallery-source" href="https://github.com/teamkrope/linuxplayer/blob/main/docs/screenshots/playback-v1-coast.png" target="_blank" rel="noopener noreferrer">View original screenshot</a></div></div></div></div></section>
<section class="section preview-section" id="preview" aria-labelledby="preview-title"><div class="container"><div class="section-head" data-reveal="copy"><span class="section-kicker">A closer look</span><h2 id="preview-title">See it in action.</h2></div><div class="gallery" data-reveal="visual"><div class="gallery-tabs" role="tablist" aria-label="Application screenshots"><button type="button" class="active" role="tab" id="tab-playback" aria-selected="true" aria-controls="gallery-panel" data-shot="playback">Playback</button><button type="button" role="tab" id="tab-options" aria-selected="false" aria-controls="gallery-panel" data-shot="options" tabindex="-1">Controls</button><button type="button" role="tab" id="tab-hidden" aria-selected="false" aria-controls="gallery-panel" data-shot="hidden" tabindex="-1">Video only</button><button type="button" role="tab" id="tab-mini" aria-selected="false" aria-controls="gallery-panel" data-shot="mini" tabindex="-1">Mini-player</button></div><div class="gallery-panel" id="gallery-panel" role="tabpanel" aria-labelledby="tab-playback"><div class="gallery-image-wrap" id="gallery-image-wrap"><div class="gallery-workspace" aria-hidden="true"><div></div><div></div><div></div></div><img id="gallery-image" src="assets/screenshots/playback-v1-coast.png" alt="LinuxPlayer playback interface" width="1100" height="680" loading="lazy"></div><div class="gallery-caption"><div><strong id="gallery-title">Playback</strong></div><a id="gallery-source" href="https://github.com/teamkrope/linuxplayer/blob/main/docs/screenshots/playback-v1-coast.png" target="_blank" rel="noopener noreferrer">View original screenshot</a></div></div></div></div></section>
<section class="section download-section" id="download" aria-labelledby="download-title"><div class="container download-grid"><div><h2 id="download-title">Install LinuxPlayer.</h2><p>64-bit x86 .deb package</p><div class="download-actions"><a class="button button-primary" href="https://github.com/teamkrope/linuxplayer/releases/latest/download/linuxplayer_amd64.deb">Download ↓</a><a class="text-link" href="download.html">Other install options →</a></div></div><div class="install-card"><div class="install-head"><span>Terminal install</span><button type="button" id="copy-command" class="copy-button" aria-label="Copy install commands">Copy command</button></div><pre><code id="install-command">curl -fsSLo /tmp/linuxplayer-install.sh \
https://github.com/teamkrope/linuxplayer/releases/latest/download/install.sh
bash /tmp/linuxplayer-install.sh</code></pre></div></div></section>
Expand Down
32 changes: 26 additions & 6 deletions web/motion.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
if (!('IntersectionObserver' in window) || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

const targets = document.querySelectorAll([
'main .hero-copy', 'main .hero-shot', 'main .section-head',
'main .feature-shot', 'main .feature-list article',
'main .pip-copy', 'main .workspace-figure', 'main .ratio-demo',
'main .gallery', 'main .download-grid > *', 'main .source-grid > *',
'main .faq-grid > *', 'main .package-card', 'main .download-help-grid > *'
'main [data-reveal]', 'main .packages-section .section-head',
'main .download-grid > *', 'main .source-grid > *',
'main .faq-grid > *', 'main .package-card',
'main .download-help-grid > *'
].join(', '));

targets.forEach(target => {
Expand All @@ -21,7 +20,7 @@
if (target.getBoundingClientRect().top < window.innerHeight * 0.94) {
target.classList.add('is-visible');
}
if (target.matches('.feature-list article')) {
if (target.matches('.focus-points article')) {
const index = [...target.parentElement.children].indexOf(target);
target.style.setProperty('--reveal-delay', `${Math.min(index, 5) * 55}ms`);
}
Expand All @@ -36,4 +35,25 @@
});
}, { threshold: 0.08, rootMargin: '0px 0px -6% 0px' });
targets.forEach(target => observer.observe(target));

const scenes = [...document.querySelectorAll('[data-scroll-scene]')];
if (!scenes.length) return;
let scheduled = false;
const updateScenes = () => {
const viewport = window.innerHeight;
scenes.forEach(scene => {
const rect = scene.getBoundingClientRect();
const progress = Math.max(0, Math.min(1, (viewport - rect.top) / (viewport + rect.height)));
scene.style.setProperty('--scene-shift', `${(-18 * progress).toFixed(1)}px`);
});
scheduled = false;
};
const scheduleScenes = () => {
if (scheduled) return;
scheduled = true;
requestAnimationFrame(updateScenes);
};
window.addEventListener('scroll', scheduleScenes, { passive: true });
window.addEventListener('resize', scheduleScenes);
scheduleScenes();
})();
1 change: 1 addition & 0 deletions web/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ await rm(output, { recursive: true, force: true });
await mkdir(output, { recursive: true });
await cp(path.join(root, 'assets'), path.join(output, 'assets'), { recursive: true });
await cp(path.join(root, 'styles.css'), path.join(output, 'styles.css'));
await cp(path.join(root, 'showcase.css'), path.join(output, 'showcase.css'));
await cp(path.join(root, 'script.js'), path.join(output, 'script.js'));
await cp(path.join(root, 'motion.js'), path.join(output, 'motion.js'));
await cp(path.join(root, 'download.js'), path.join(output, 'download.js'));
Expand Down
4 changes: 3 additions & 1 deletion web/scripts/check.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ if ([html, download].some(page => page.includes('Download for Linux'))) throw ne
if (!html.includes('playback-v1-coast.png') || !script.includes('options-v1-coast.png')) throw new Error('Current screenshots are not linked from the site');
if (html.includes('Start screen') || script.includes('empty-v1-coast.png')) throw new Error('Removed gallery tab is still present');
if (![html, download].every(page => page.includes('motion.js') && page.includes('footer-line'))) throw new Error('Shared motion or compact footer missing');
if (!html.includes('showcase.css') || !html.includes('class="hero-shot"') || !html.includes('data-scroll-scene')) throw new Error('Product showcase is missing');
if (!(html.indexOf('class="hero-copy"') < html.indexOf('class="hero-shot"') && html.indexOf('class="hero-shot"') < html.indexOf('class="hero-actions"'))) throw new Error('Hero must show title, product image, then actions');
for (const text of ['LinuxPlayer', 'id="download"', 'id="features"', 'id="preview"', 'id="open-source"', 'application/ld+json', 'releases/latest/download/install.sh']) {
if (!html.includes(text)) throw new Error(`Missing required site content: ${text}`);
}
for (const asset of ['linuxplayer_amd64.deb', 'linuxplayer_arm64.deb']) {
if (!download.includes(`/releases/latest/download/${asset}`)) throw new Error(`Missing latest direct download: ${asset}`);
}
for (const file of ['styles.css', 'motion.js', 'script.js', 'download.js', 'robots.txt', 'LICENSE', 'assets/linuxplayer-bird.png', 'assets/external-link.svg', 'assets/ratio-source.webp', 'assets/screenshots/playback-v1-coast.png', 'assets/screenshots/options-v1-coast.png', 'assets/screenshots/hidden-v1-coast.png', 'assets/screenshots/mini-player-v1-coast.png']) {
for (const file of ['styles.css', 'showcase.css', 'motion.js', 'script.js', 'download.js', 'robots.txt', 'LICENSE', 'assets/linuxplayer-bird.png', 'assets/external-link.svg', 'assets/ratio-source.webp', 'assets/screenshots/playback-v1-coast.png', 'assets/screenshots/options-v1-coast.png', 'assets/screenshots/hidden-v1-coast.png', 'assets/screenshots/mini-player-v1-coast.png']) {
if (!(await stat(path.join(root, file))).isFile()) throw new Error(`Missing asset: ${file}`);
}
console.log('Site content and required assets verified.');
Loading