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
14 changes: 1 addition & 13 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
Admins

Check notice on line 1 in .github/actions/spelling/expect.txt

View workflow job for this annotation

GitHub Actions / Report (PR)

File is empty (empty-file)
Bento
CVEs
DFBB
Dists
gpgkeys
hubfs
idx
ircs
ISPs
LGPLv
listinfo
napt
nbaseurl
nenabled
ngpgcheck
ngpgkey
nname
noopener
noreferrer
nsudo
nyum
onsubmit
openemailsurvey
opsz
prerender
proactively
rce
sitemap
SLAs
splitscreen
wght
yeswehack
176 changes: 32 additions & 144 deletions main/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,12 @@

import logoSrc from '$lib/assets/logo.png?enhanced';
import anniversaryImgSrc from '$lib/assets/anniversary.png?enhanced';
import { GitHubIcon } from '@dovecot/shared-ui';
import { GitHubIcon, HeroCarousel } from '@dovecot/shared-ui';

import proLogoSrc from '$lib/assets/pro-logo.png?enhanced';

let activeSlide = $state(0);
let intervalId: any;
let isPaused = $state(false);

const slides = [
{
tagText: "",
tagClass: "text-on-surface-variant border-outline-variant bg-surface-container-low",
badge: "The Secure, High Performance Email Server",
badgeClass: "text-primary",
badgeIcon: "",
Expand All @@ -27,8 +21,6 @@
imageClass: "max-w-l"
},
{
tagText: "",
tagClass: "text-tertiary border-tertiary-container/30 bg-tertiary-container/10",
badge: "Celebrating 25 Years",
badgeClass: "text-tertiary",
badgeIcon: "workspace_premium",
Expand All @@ -39,145 +31,41 @@
imageClass: "shadow-lg border border-outline-variant/30"
}
];

function startInterval() {
if (isPaused) return;
intervalId = setInterval(() => {
activeSlide = (activeSlide + 1) % slides.length;
}, 12000);
}

function resetInterval() {
if (intervalId) {
clearInterval(intervalId);
}
startInterval();
}

function togglePause() {
isPaused = !isPaused;
if (isPaused) {
if (intervalId) clearInterval(intervalId);
} else {
startInterval();
}
}

$effect(() => {
startInterval();
return () => {
if (intervalId) {
clearInterval(intervalId);
}
};
});
</script>

<!-- Hero Section -->
<section class="relative pt-16 pb-16 px-gutter overflow-hidden bg-grid-pattern transition-colors duration-300">
<div class="absolute inset-0 bg-gradient-to-b from-transparent to-background z-0 pointer-events-none"></div>
{#snippet heroTitle()}
<span class="font-headline-xl text-5xl md:text-7xl font-extrabold tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-primary to-primary-container select-none" role="heading" aria-level="1">
Dovecot
</span>
{/snippet}

<!-- Slide Container (relative, overlays) -->
<div class="max-w-container-max mx-auto relative z-10 grid grid-cols-1 lg:grid-cols-2 gap-gutter items-center">
<!-- Left: Static Title + Rotating Slide Content -->
<div class="flex flex-col items-start gap-gutter w-full">
<!-- Static Title -->
<span class="font-headline-xl text-5xl md:text-7xl font-extrabold tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-primary to-primary-container select-none">
Dovecot
</span>
<!-- Rotating Slide Content (badge, headline, description) -->
<div class="grid grid-cols-1 grid-rows-1 items-start w-full">
{#each slides as slide, idx}
<div class="col-start-1 row-start-1 flex flex-col items-start gap-gutter transition-all duration-700 ease-in-out transform {activeSlide === idx ? 'opacity-100 translate-x-0 z-10 pointer-events-auto' : 'opacity-0 -translate-x-4 z-0 pointer-events-none'}" aria-hidden={activeSlide !== idx}>
<div class="flex flex-col items-start gap-3">
<div class="flex flex-wrap items-center gap-x-3 gap-y-2">
{#if slide.tagText}
<span class="text-xs uppercase tracking-widest font-bold border px-2.5 py-0.5 rounded {slide.tagClass}">
{slide.tagText}
</span>
{/if}
</div>
<div class="inline-block bg-surface-container-highest px-3 py-1 rounded-full border border-outline-variant/30">
<span class="font-label-md text-label-md tracking-widest uppercase flex items-center gap-1.5 {slide.badgeClass}">
{#if slide.badgeIcon}
<span aria-hidden="true" class="material-symbols text-sm" style="font-variation-settings: 'FILL' 1;">{slide.badgeIcon}</span>
{/if}
{slide.badge}
</span>
</div>
</div>
<h1 class="font-headline-xl text-headline-lg-mobile md:text-headline-xl text-on-background max-w-2xl font-bold leading-tight">
{@html slide.headline}
</h1>
<p class="font-body-md text-body-md text-on-surface-variant max-w-xl">
{slide.description}
</p>
</div>
{/each}
</div>
<!-- Static Buttons (Same for all slides) -->
<div class="flex flex-wrap gap-4 mt-2">
<a
class="inline-flex items-center justify-center bg-primary hover:bg-primary/90 text-on-primary font-body-md text-body-md font-semibold px-6 py-3 rounded-lg transition-colors shadow-sm cursor-pointer gap-2"
href="{base}/download"
>
<span aria-hidden="true" class="material-symbols text-xl">download</span>
Download
</a>
<a
class="inline-flex items-center justify-center bg-surface hover:bg-surface-dim text-primary border border-outline-variant font-body-md text-body-md font-semibold px-6 py-3 rounded-lg transition-colors cursor-pointer gap-2"
href="https://github.com/dovecot/core"
target="_blank"
rel="noopener noreferrer"
>
<GitHubIcon iconClass="w-5 h-5 fill-current" />
Code
</a>
<a
class="inline-flex items-center justify-center bg-surface hover:bg-surface-dim text-primary border border-outline-variant font-body-md text-body-md font-semibold px-6 py-3 rounded-lg transition-colors cursor-pointer"
href="https://doc.dovecot.org/"
>
Documentation
</a>
</div>
</div>

<!-- Right: Graphic Grid (cross-faded with zoom effect) -->
<div class="flex flex-col items-center gap-4 w-full">
<div class="relative w-full grid grid-cols-1 grid-rows-1 justify-center items-center">
<div class="absolute inset-0 bg-primary-container/10 rounded-full blur-3xl scale-150 -z-10"></div>
{#each slides as slide, idx}
<div class="col-start-1 row-start-1 flex justify-center items-center transition-all duration-700 ease-in-out transform {activeSlide === idx ? 'opacity-100 scale-100 z-10 pointer-events-auto' : 'opacity-0 scale-95 z-0 pointer-events-none'}" aria-hidden={activeSlide !== idx}>
<enhanced:img alt={slide.imageAlt} class="max-w-md w-full h-auto object-contain rounded-2xl {slide.imageClass}" src={slide.image} />
</div>
{/each}
</div>
</div>
</div>

<!-- Dot Indicators + Pause/Play (WCAG 2.2.1 Timing Adjustable) -->
<div class="absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-2 z-25">
{#each slides as _, idx}
<button
onclick={() => { activeSlide = idx; resetInterval(); }}
class="w-2.5 h-2.5 rounded-full transition-all duration-300 cursor-pointer {activeSlide === idx ? 'bg-primary w-6' : 'bg-outline-variant/60 hover:bg-outline-variant'}"
aria-label="Go to slide {idx + 1}"
></button>
{/each}
<button
onclick={togglePause}
class="w-6 h-6 rounded-full bg-white/10 hover:bg-white/20 text-on-surface transition-colors cursor-pointer flex items-center justify-center"
aria-label={isPaused ? 'Resume carousel' : 'Pause carousel'}
>
<span aria-hidden="true" class="material-symbols text-xs">{isPaused ? 'play_arrow' : 'pause'}</span>
</button>
</div>
{#snippet heroActions()}
<a
class="inline-flex items-center justify-center bg-primary hover:bg-primary/90 text-on-primary font-body-md text-body-md font-semibold px-6 py-3 rounded-lg transition-colors shadow-sm cursor-pointer gap-2"
href="{base}/download"
>
<span aria-hidden="true" class="material-symbols text-xl">download</span>
Download
</a>
<a
class="inline-flex items-center justify-center bg-surface hover:bg-surface-dim text-primary border border-outline-variant font-body-md text-body-md font-semibold px-6 py-3 rounded-lg transition-colors cursor-pointer gap-2"
href="https://github.com/dovecot/core"
target="_blank"
rel="noopener noreferrer"
>
<GitHubIcon iconClass="w-5 h-5 fill-current" />
Code
</a>
<a
class="inline-flex items-center justify-center bg-surface hover:bg-surface-dim text-primary border border-outline-variant font-body-md text-body-md font-semibold px-6 py-3 rounded-lg transition-colors cursor-pointer"
href="https://doc.dovecot.org/"
>
Documentation
</a>
{/snippet}

<!-- Aria-live for slide changes (WCAG 4.1.3) -->
<div aria-live="polite" class="sr-only">
Slide {activeSlide + 1} of {slides.length}: {slides[activeSlide].badge}
</div>
</section>
<!-- Hero Section -->
<HeroCarousel slides={slides} title={heroTitle} actions={heroActions} />

<!-- Dovecot Pro Promo Section -->
<section class="bg-gradient-to-r from-[#0e0d10] via-[#2d1543] to-[#3c036d] relative overflow-hidden">
Expand Down
6 changes: 0 additions & 6 deletions main/src/routes/download/RepoSection.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@
let {
version, // '2.4' | '2.3'
pgpKeyId, // key id to show
copyIdSuffix = '', // appended to copy IDs
pgpNote, // custom text after "packages" in PGP description
navExtra, // optional extra nav links snippet
}: {
version: string;
pgpKeyId: string;
copyIdSuffix?: string;
pgpNote?: string;
navExtra?: Snippet;
} = $props();
Expand Down Expand Up @@ -140,7 +138,6 @@
<CodeBlock
label={'1. Setup GPG keyring (' + rel.gpgPath + ')'}
code={rel.gpgCmd}
copyId={rel.codename + '_gpg' + copyIdSuffix}
ariaLabel="Copy GPG Command"
/>
{/if}
Expand All @@ -149,7 +146,6 @@
<CodeBlock
label={'2. Configure sources config path file (' + rel.sourcesPath + ')'}
code={rel.sourcesContent}
copyId={rel.codename + '_sources' + copyIdSuffix}
ariaLabel="Copy Sources Configuration"
/>
{/if}
Expand All @@ -158,7 +154,6 @@
<CodeBlock
label={'Configure repo path file (' + rel.repoPath + ')'}
code={rel.repoContent}
copyId={rel.codename + '_repo' + copyIdSuffix}
ariaLabel="Copy Repo Setup"
/>
{/if}
Expand All @@ -170,7 +165,6 @@
<CodeBlock
label="Update repository database and install packages:"
code={currentDist.installCmd}
copyId={currentDist.id + '_install' + copyIdSuffix}
ariaLabel="Copy Installation Command"
/>
{#if currentDist.packages}
Expand Down
1 change: 0 additions & 1 deletion main/src/routes/download/eol/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@
<RepoSection
version="2.3"
pgpKeyId="ED409DA1"
copyIdSuffix="_eol"
pgpNote="for Dovecot CE 2.3"
/>
</div>
Expand Down
1 change: 0 additions & 1 deletion main/src/routes/layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
@import 'tailwindcss';
@source "../../../packages/shared-ui/src/**/*.svelte";
@import "../../../packages/shared-ui/src/lib/layout.css";
@plugin '@tailwindcss/forms';

@theme {
--color-on-surface: #1a1c1c;
Expand Down
26 changes: 25 additions & 1 deletion main/src/routes/security/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
</div>
<div class="mt-6">
<a
href="https://vdp.open-xchange.com/"
href="https://yeswehack.com/programs/dovecot"
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
target="_blank"
rel="noopener noreferrer"
class="w-full md:w-auto inline-flex items-center justify-center bg-error hover:bg-error/90 text-white font-semibold px-6 py-3 rounded-lg shadow transition-colors text-sm cursor-pointer"
Expand Down Expand Up @@ -78,6 +78,30 @@
</div>
</div>

<!-- Dovecot Pro Advisories Banner -->
<div class="bg-gradient-to-r from-[#0e0d10] via-[#2d1543] to-[#3c036d] relative overflow-hidden rounded-2xl p-6 mb-12 max-w-4xl mx-auto">
<div class="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-[#861be4]/20 via-transparent to-transparent pointer-events-none"></div>
<div class="absolute inset-0 flex items-center justify-center pointer-events-none overflow-hidden">
<enhanced:img alt="" class="w-[200px] opacity-[0.06] blur-[1px] select-none" src="$lib/assets/pro-logo.png" />
</div>
<div class="relative z-10 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
<div class="flex-1">
<h3 class="font-['Space_Grotesk',sans-serif] text-base font-medium tracking-[0.02em] text-white mb-1">Dovecot Pro Security Advisories</h3>
<p class="font-['Ubuntu_Sans',sans-serif] text-sm leading-relaxed text-white/85">
Dovecot Pro is the commercial product and maintains its own security advisory program. Pro advisories may also be applicable to Dovecot Community Edition.
</p>
</div>
<a
class="font-['Space_Grotesk',sans-serif] text-sm font-medium tracking-[0.02em] shrink-0 inline-flex items-center justify-center bg-[#861be4]/50 hover:bg-[#4a117b]/70 text-white px-6 py-2.5 rounded-[14px] transition-all shadow-lg shadow-[#861be4]/30 hover:shadow-[#861be4]/50 cursor-pointer border border-white/10"
href="https://documentation.open-xchange.com/dovecot/security/advisories/"
target="_blank"
rel="noopener noreferrer"
>
View Advisories
</a>
</div>
</div>

<!-- Advisories Title and Filter -->
<div class="mb-8 space-y-4">
<h2 class="font-headline-lg text-2xl text-on-background font-bold">Security Advisories</h2>
Expand Down
Loading