fix: format code and ensure consistent styling across files

This commit is contained in:
mr. M 2025-01-08 00:19:40 +01:00
parent 2a9df52722
commit d14b44b2c8
No known key found for this signature in database
GPG key ID: CBD57A2AEDBDA1FB
43 changed files with 14294 additions and 11609 deletions

View file

@ -11,7 +11,6 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 22
@ -31,12 +30,10 @@ jobs:
- name: Deploy to BunnyCDN - name: Deploy to BunnyCDN
uses: ayeressian/bunnycdn-storage-deploy@v2.2.4 uses: ayeressian/bunnycdn-storage-deploy@v2.2.4
with: with:
source: "dist" source: 'dist'
destination: "" destination: ''
storageZoneName: "${{ secrets.STORAGE_NAME_BACKUP }}" storageZoneName: '${{ secrets.STORAGE_NAME_BACKUP }}'
storagePassword: "${{ secrets.STORAGE_PASSWORD_BACKUP }}" storagePassword: '${{ secrets.STORAGE_PASSWORD_BACKUP }}'
upload: "true" upload: 'true'
remove: "false" remove: 'false'
purgePullZone: "false" purgePullZone: 'false'

View file

@ -11,7 +11,6 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 22 node-version: 22
@ -31,12 +30,10 @@ jobs:
- name: Deploy to BunnyCDN - name: Deploy to BunnyCDN
uses: ayeressian/bunnycdn-storage-deploy@v2.2.4 uses: ayeressian/bunnycdn-storage-deploy@v2.2.4
with: with:
source: "dist" source: 'dist'
destination: "" destination: ''
storageZoneName: "${{ secrets.STORAGE_NAME }}" storageZoneName: '${{ secrets.STORAGE_NAME }}'
storagePassword: "${{ secrets.STORAGE_PASSWORD }}" storagePassword: '${{ secrets.STORAGE_PASSWORD }}'
upload: "true" upload: 'true'
remove: "false" remove: 'false'
purgePullZone: "false" purgePullZone: 'false'

View file

@ -4,6 +4,6 @@ export default {
tabWidth: 2, tabWidth: 2,
semi: false, semi: false,
singleQuote: true, singleQuote: true,
endOfLine: "lf", endOfLine: 'lf',
plugins: ["prettier-plugin-astro", "prettier-plugin-tailwindcss"], plugins: ['prettier-plugin-astro', 'prettier-plugin-tailwindcss'],
}; }

View file

@ -1,15 +1,19 @@
// @ts-check // @ts-check
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config'
import tailwind from '@astrojs/tailwind'; import tailwind from '@astrojs/tailwind'
import react from '@astrojs/react'; import react from '@astrojs/react'
import sitemap from '@astrojs/sitemap'; import sitemap from '@astrojs/sitemap'
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
integrations: [tailwind(), react(), sitemap({ integrations: [
tailwind(),
react(),
sitemap({
filter: (page) => !page.includes('mods/'), filter: (page) => !page.includes('mods/'),
})], }),
],
site: 'https://zen-browser.app', site: 'https://zen-browser.app',
}); })

3399
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,19 @@
export function getTitleAnimation(delay = 0) { export function getTitleAnimation(delay = 0) {
return { return {
initial: { opacity: 0, translateY: 20, filter: 'blur(4px)' }, initial: { opacity: 0, translateY: 20, filter: 'blur(4px)' },
whileInView: { opacity: 1, translateY: 0, filter: 'blur(0px)', transition: { duration: 0.3, delay } }, whileInView: {
viewport: { once: true } opacity: 1,
}; translateY: 0,
filter: 'blur(0px)',
transition: { duration: 0.3, delay },
},
viewport: { once: true },
}
} }
export function getZoomInAnimation(delay = 0) { export function getZoomInAnimation(delay = 0) {
return { return {
initial: { scale: 0.8, opacity: 0 }, initial: { scale: 0.8, opacity: 0 },
whileInView: { scale: 1, opacity: 1, transition: { duration: 0.2, delay } }, whileInView: { scale: 1, opacity: 1, transition: { duration: 0.2, delay } },
}; }
} }

View file

@ -1,8 +1,11 @@
--- ---
import { ArrowLeft } from "lucide-astro"; import { ArrowLeft } from 'lucide-astro'
--- ---
<button onclick="window.history.back()" class="flex items-center gap-2 w-min mb-8"> <button
onclick="window.history.back()"
class="mb-8 flex w-min items-center gap-2"
>
<ArrowLeft class="size-4" /> <ArrowLeft class="size-4" />
Back Back
</> </button>

View file

@ -1,23 +1,63 @@
--- ---
const { class: className, isPrimary, isAlert, isBordered, href, id, extra } = Astro.props; const {
class: className,
isPrimary,
isAlert,
isBordered,
href,
id,
extra,
} = Astro.props
--- ---
{href ? ( {
<a id={id} {...extra} href={href} class:list={['px-6 py-2 flex gap-2 items-center justify-center rounded-full transition-transform duration-200', className, isPrimary ? 'bg-dark text-paper' : isAlert ? 'bg-red-300 text-dark' : !isBordered ? 'hover:bg-dark border-2 border-dark hover:text-paper' : 'border-2 border-dark hover:bg-dark hover:shadow-sm hover:text-paper !transition-bg']} > href ? (
<a
id={id}
{...extra}
href={href}
class:list={[
'flex items-center justify-center gap-2 rounded-full px-6 py-2 transition-transform duration-200',
className,
isPrimary
? 'bg-dark text-paper'
: isAlert
? 'bg-red-300 text-dark'
: !isBordered
? 'border-2 border-dark hover:bg-dark hover:text-paper'
: '!transition-bg border-2 border-dark hover:bg-dark hover:text-paper hover:shadow-sm',
]}
>
<slot /> <slot />
</a> </a>
) : ( ) : (
<button id={id} {...extra} class:list={['px-6 py-2 flex gap-2 items-center justify-center rounded-full transition-transform duration-200', className, isPrimary ? 'bg-dark text-paper' : isAlert ? 'bg-red-300 text-dark' : !isBordered ? 'hover:bg-dark border-2 border-dark hover:text-paper' : 'border-2 border-dark hover:bg-dark hover:shadow-sm hover:text-paper !transition-bg']} > <button
id={id}
{...extra}
class:list={[
'flex items-center justify-center gap-2 rounded-full px-6 py-2 transition-transform duration-200',
className,
isPrimary
? 'bg-dark text-paper'
: isAlert
? 'bg-red-300 text-dark'
: !isBordered
? 'border-2 border-dark hover:bg-dark hover:text-paper'
: '!transition-bg border-2 border-dark hover:bg-dark hover:text-paper hover:shadow-sm',
]}
>
<slot /> <slot />
</button> </button>
)} )
}
<style> <style>
button, a { button,
font-size: .9rem; a {
font-size: 0.9rem;
&[disabled] { &[disabled] {
cursor: not-allowed; cursor: not-allowed;
opacity: .5; opacity: 0.5;
} }
} }
</style> </style>

View file

@ -1,18 +1,28 @@
--- ---
const { white, multiplier = 0.9, class: classList } = Astro.props; const { white, multiplier = 0.9, class: classList } = Astro.props
const sizes = [216, 396, 576, 756] const sizes = [216, 396, 576, 756]
const borderWidths = [20, 30, 40, 50] const borderWidths = [20, 30, 40, 50]
--- ---
<div id="circles" class:list={["inset-0 overflow-hidden pointer-events-none", classList]}>
<div
id="circles"
class:list={['pointer-events-none inset-0 overflow-hidden', classList]}
>
<div class="mx-auto opacity-10 lg:opacity-100"> <div class="mx-auto opacity-10 lg:opacity-100">
{[...Array(4)].map((_, i) => ( {
<div class:list={["absolute rounded-full -translate-x-1/2 -translate-y-1/2", white ? "border-paper" : "border-coral"]} [...Array(4)].map((_, i) => (
<div
class:list={[
'absolute -translate-x-1/2 -translate-y-1/2 rounded-full',
white ? 'border-paper' : 'border-coral',
]}
style={{ style={{
width: `${multiplier * sizes[i]}px`, width: `${multiplier * sizes[i]}px`,
height: `${multiplier * sizes[i]}px`, height: `${multiplier * sizes[i]}px`,
borderWidth: `${multiplier * borderWidths[i]}px`, borderWidth: `${multiplier * borderWidths[i]}px`,
}} }}
/> />
))} ))
}
</div> </div>
</div> </div>

View file

@ -2,42 +2,52 @@
import Title from '../components/Title.astro' import Title from '../components/Title.astro'
import Description from '../components/Description.astro' import Description from '../components/Description.astro'
import Button from '../components/Button.astro' import Button from '../components/Button.astro'
import { motion } from 'motion/react'; import { motion } from 'motion/react'
import { import { Github, Check } from 'lucide-astro'
Github,
Check,
} from 'lucide-astro'
import { getTitleAnimation } from '../animations' import { getTitleAnimation } from '../animations'
--- ---
<section <section
id="Community" id="Community"
class="flex w-full flex-col items-center text-center relative" class="relative flex w-full flex-col items-center text-center"
> >
<Title> <Title>
<motion.span client:load {...getTitleAnimation(0.2)}> <motion.span client:load {...getTitleAnimation(0.2)}>
Community Community
</motion.span> </motion.span>
<motion.span client:load {...getTitleAnimation(0.4)}> <motion.span client:load {...getTitleAnimation(0.4)}> Driven </motion.span>
Driven
</motion.span>
</Title> </Title>
<motion.p client:load {...getTitleAnimation(0.6)} className="px-4 lg:px-0 lg:w-1/2"> <motion.p
client:load
{...getTitleAnimation(0.6)}
className="px-4 lg:w-1/2 lg:px-0"
>
We are a community-driven project. We listen to our users and build the We are a community-driven project. We listen to our users and build the
features they want. Zen focuses on privacy and customization, not on data collection. features they want. Zen focuses on privacy and customization, not on data
collection.
</motion.p> </motion.p>
<div class="mt-6 gap-3 px-4 sm:px-0 w-full sm:gap-10 flex flex-wrap justify-center"> <div
class="mt-6 flex w-full flex-wrap justify-center gap-3 px-4 sm:gap-10 sm:px-0"
>
<motion.span client:load {...getTitleAnimation(0.8)}> <motion.span client:load {...getTitleAnimation(0.8)}>
<Button class:list={['px-4']} href="https://github.com/zen-browser"> <Button class:list={['px-4']} href="https://github.com/zen-browser">
<Github class="size-4" /> <Github class="size-4" />
<span>View on Github</span> <span>View on Github</span>
</Button> </Button>
</motion.span> </motion.span>
<motion.div client:load {...getTitleAnimation(1)} className="flex items-center gap-4"> <motion.div
client:load
{...getTitleAnimation(1)}
className="flex items-center gap-4"
>
<Check class="size-4" /> <Check class="size-4" />
<span>Fully Customizable</span> <span>Fully Customizable</span>
</motion.div> </motion.div>
<motion.div client:load {...getTitleAnimation(1.2)} className="flex items-center gap-4"> <motion.div
client:load
{...getTitleAnimation(1.2)}
className="flex items-center gap-4"
>
<Check class="size-4" /> <Check class="size-4" />
<span>Privacy Focused</span> <span>Privacy Focused</span>
</motion.div> </motion.div>

View file

@ -1,4 +1,5 @@
--- ---
--- ---
<p {...Astro.props}> <p {...Astro.props}>

View file

@ -1,113 +1,168 @@
--- ---
import Title from '../components/Title.astro' import Title from '../components/Title.astro'
import Description from '../components/Description.astro' import Description from '../components/Description.astro'
import { Image } from 'astro:assets'; import { Image } from 'astro:assets'
import browserWorkspaces from '../assets/browser-workspaces.png'; import browserWorkspaces from '../assets/browser-workspaces.png'
import browserCompactMode from '../assets/browser-compactmode.png'; import browserCompactMode from '../assets/browser-compactmode.png'
import browserSplitViews from '../assets/browser-splitview.png'; import browserSplitViews from '../assets/browser-splitview.png'
import browserSidebar from '../assets/browser-sidebar.png'; import browserSidebar from '../assets/browser-sidebar.png'
import browserGlance from '../assets/browser-glance.webm'; import browserGlance from '../assets/browser-glance.webm'
import { motion } from 'motion/react'; import { motion } from 'motion/react'
import { getTitleAnimation, getZoomInAnimation } from '../animations' import { getTitleAnimation, getZoomInAnimation } from '../animations'
--- ---
<section <section
id="features" id="features"
class="flex w-full px-4 lg:px-12 xl:px-24 py-36 flex-col relative" class="relative flex w-full flex-col px-4 py-36 lg:px-12 xl:px-24"
> >
<div class="flex flex-col lg:flex-row w-full items-start gap-12"> <div class="flex w-full flex-col items-start gap-12 lg:flex-row">
<div id="feature-list" class="lg:w-1/3 flex flex-col gap-4"> <div id="feature-list" class="flex flex-col gap-4 lg:w-1/3">
<motion.span client:load {...getTitleAnimation(0.2)}> <motion.span client:load {...getTitleAnimation(0.2)}>
<Title>Features</Title> <Title>Features</Title>
</motion.span> </motion.span>
<motion.div client:load viewport={{ once: true }} {...getZoomInAnimation(0.4)} className="feature flex hover:bg-coral/5 rounded-xl p-4 w-full cursor-pointer"> <motion.div
client:load
viewport={{ once: true }}
{...getZoomInAnimation(0.4)}
className="feature flex w-full cursor-pointer rounded-xl p-4 hover:bg-coral/5"
>
<div class="flex flex-col"> <div class="flex flex-col">
<Description class="feature-title font-bold text-2xl"> <Description class="feature-title text-2xl font-bold">
Workspaces Workspaces
</Description> </Description>
<p class="desc text-base font-normal mt-2"> <p class="desc mt-2 text-base font-normal">
With Zen, you can create multiple workspaces to keep your tabs organized. With Zen, you can create multiple workspaces to keep your tabs
organized.
</p> </p>
</div> </div>
</motion.div> </motion.div>
<motion.div client:load viewport={{ once: true }} {...getZoomInAnimation(0.6)} className="feature flex hover:bg-coral/5 rounded-xl p-4 w-full cursor-pointer"> <motion.div
client:load
viewport={{ once: true }}
{...getZoomInAnimation(0.6)}
className="feature flex w-full cursor-pointer rounded-xl p-4 hover:bg-coral/5"
>
<div class="flex flex-col"> <div class="flex flex-col">
<Description class="feature-title font-bold text-2xl"> <Description class="feature-title text-2xl font-bold">
Compact Mode Compact Mode
</Description> </Description>
<p class="desc text-base font-normal mt-2"> <p class="desc mt-2 text-base font-normal">
Zen's compact mode allows you to see more of your content at once. Zen's compact mode allows you to see more of your content at once.
</p> </p>
</div> </div>
</motion.div> </motion.div>
<motion.div client:load viewport={{ once: true }} {...getZoomInAnimation(0.8)} className="feature flex hover:bg-coral/5 rounded-xl p-4 w-full cursor-pointer"> <motion.div
client:load
viewport={{ once: true }}
{...getZoomInAnimation(0.8)}
className="feature flex w-full cursor-pointer rounded-xl p-4 hover:bg-coral/5"
>
<div class="flex flex-col"> <div class="flex flex-col">
<Description class="feature-title font-bold text-2xl"> <Description class="feature-title text-2xl font-bold">
Split Views Split Views
</Description> </Description>
<p class="desc text-base font-normal mt-2"> <p class="desc mt-2 text-base font-normal">
Split your browser into multiple views to see more content at once. Split your browser into multiple views to see more content at once.
</p> </p>
</div> </div>
</motion.div> </motion.div>
<motion.div client:load viewport={{ once: true }} {...getZoomInAnimation(1)} className="feature flex hover:bg-coral/5 rounded-xl p-4 w-full cursor-pointer"> <motion.div
client:load
viewport={{ once: true }}
{...getZoomInAnimation(1)}
className="feature flex w-full cursor-pointer rounded-xl p-4 hover:bg-coral/5"
>
<div class="flex flex-col"> <div class="flex flex-col">
<Description class="feature-title font-bold text-2xl"> <Description class="feature-title text-2xl font-bold">
Sidebar Sidebar
</Description> </Description>
<p class="desc text-base font-normal mt-2"> <p class="desc mt-2 text-base font-normal">
Zen's sidebar allows you to quickly access your favourite websites. Zen's sidebar allows you to quickly access your favourite websites.
</p> </p>
</div> </div>
</motion.div> </motion.div>
<motion.div client:load viewport={{ once: true }} {...getZoomInAnimation(1.2)} className="feature flex hover:bg-coral/5 rounded-xl p-4 w-full cursor-pointer"> <motion.div
client:load
viewport={{ once: true }}
{...getZoomInAnimation(1.2)}
className="feature flex w-full cursor-pointer rounded-xl p-4 hover:bg-coral/5"
>
<div class="flex flex-col"> <div class="flex flex-col">
<Description class="feature-title font-bold text-2xl"> <Description class="feature-title text-2xl font-bold">
Zen Glance Zen Glance
</Description> </Description>
<p class="desc text-base font-normal mt-2"> <p class="desc mt-2 text-base font-normal">
Preview your tabs with Zen Glance to quickly find what you're looking for, without switching tabs. Preview your tabs with Zen Glance to quickly find what you're
looking for, without switching tabs.
</p> </p>
</div> </div>
</motion.div> </motion.div>
</div> </div>
<div class="relative lg:w-2/3 lg:pl-20 items-center m-auto"> <div class="relative m-auto items-center lg:w-2/3 lg:pl-20">
<div class="ml-auto" id="feature-list-image"> <div class="ml-auto" id="feature-list-image">
<Image src={browserWorkspaces} alt="Browser Workspaces" class="rounded-xl shadow border-4 border-white" /> <Image
<Image src={browserCompactMode} alt="Browser Compact Mode" class="rounded-xl shadow border-4 border-white" /> src={browserWorkspaces}
<Image src={browserSplitViews} alt="Browser Split Views" class="rounded-xl shadow border-4 border-white" /> alt="Browser Workspaces"
<Image src={browserSidebar} alt="Browser Sidebar" class="rounded-xl shadow border-4 border-white" /> class="rounded-xl border-4 border-white shadow"
<video src={browserGlance} autoplay loop muted playsinline class="rounded-xl shadow border-4 border-white absolute top-0 left-0 w-full h-full object-cover" /> />
<Image
src={browserCompactMode}
alt="Browser Compact Mode"
class="rounded-xl border-4 border-white shadow"
/>
<Image
src={browserSplitViews}
alt="Browser Split Views"
class="rounded-xl border-4 border-white shadow"
/>
<Image
src={browserSidebar}
alt="Browser Sidebar"
class="rounded-xl border-4 border-white shadow"
/>
<video
src={browserGlance}
autoplay
loop
muted
playsinline
class="absolute left-0 top-0 h-full w-full rounded-xl border-4 border-white object-cover shadow"
></video>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<script> <script>
function changeFeature(index: number) { function changeFeature(index: number) {
document.querySelectorAll('#feature-list .feature').forEach((feature: any, i) => { document
.querySelectorAll('#feature-list .feature')
.forEach((feature: any, i) => {
if (i === index) { if (i === index) {
feature.setAttribute('active', ''); feature.setAttribute('active', '')
} else { } else {
feature.removeAttribute('active'); feature.removeAttribute('active')
} }
}); })
document.querySelectorAll('#feature-list-image > *').forEach((img: any, i) => { document
img.style.opacity = i === index ? 1 : 0; .querySelectorAll('#feature-list-image > *')
img.classList.toggle('absolute', i !== index); .forEach((img: any, i) => {
img.classList.toggle('hidden', i !== index); img.style.opacity = i === index ? 1 : 0
}); img.classList.toggle('absolute', i !== index)
img.classList.toggle('hidden', i !== index)
})
} }
document.querySelectorAll('#feature-list .feature').forEach((feature: any, i) => { document
.querySelectorAll('#feature-list .feature')
.forEach((feature: any, i) => {
feature.addEventListener('click', () => { feature.addEventListener('click', () => {
changeFeature(i); changeFeature(i)
}); })
}); })
changeFeature(0); changeFeature(0)
</script> </script>
<style> <style>
.feature { .feature {
@ -116,7 +171,9 @@ import { getTitleAnimation, getZoomInAnimation } from '../animations'
} }
.feature p { .feature p {
transition: max-height 0.2s ease-in-out, margin-top 0.1s ease-in-out; transition:
max-height 0.2s ease-in-out,
margin-top 0.1s ease-in-out;
} }
.feature:not([active]) .desc { .feature:not([active]) .desc {

View file

@ -5,75 +5,100 @@ import Button from '../components/Button.astro'
import Circles from '../components/Circles.astro' import Circles from '../components/Circles.astro'
import { ArrowRight } from 'lucide-astro' import { ArrowRight } from 'lucide-astro'
import { library, icon } from '@fortawesome/fontawesome-svg-core'
import {
faMastodon,
faBluesky,
faGithub,
faTwitter,
faReddit,
} from '@fortawesome/free-brands-svg-icons'
import { library, icon } from "@fortawesome/fontawesome-svg-core"; library.add(faMastodon, faBluesky, faGithub, faTwitter, faReddit)
import { faMastodon, faBluesky, faGithub, faTwitter, faReddit } from "@fortawesome/free-brands-svg-icons"; const Mastodon = icon({ prefix: 'fab', iconName: 'mastodon' })
const Bluesky = icon({ prefix: 'fab', iconName: 'bluesky' })
library.add(faMastodon, faBluesky, faGithub, faTwitter, faReddit); const Github = icon({ prefix: 'fab', iconName: 'github' })
const Mastodon = icon({ prefix: "fab", iconName: "mastodon" }); const Twitter = icon({ prefix: 'fab', iconName: 'twitter' })
const Bluesky = icon({ prefix: "fab", iconName: "bluesky" }); const Reddit = icon({ prefix: 'fab', iconName: 'reddit' })
const Github = icon({ prefix: "fab", iconName: "github" });
const Twitter = icon({ prefix: "fab", iconName: "twitter" });
const Reddit = icon({ prefix: "fab", iconName: "reddit" });
--- ---
<footer <footer
id="footer" id="footer"
class="flex flex-col w-full py-12 px-4 lg:p-24 !pb-0 gap-24 md:gap-[15%] bg-dark text-paper border-t border-dark relative overflow-hidden" class="relative flex w-full flex-col gap-24 overflow-hidden border-t border-dark bg-dark px-4 py-12 !pb-0 text-paper md:gap-[15%] lg:p-24"
> >
<div class="w-full flex justify-between items-center flex-col lg:flex-row"> <div class="flex w-full flex-col items-center justify-between lg:flex-row">
<div> <div>
<Title class="!text-paper">Zen Browser</Title> <Title class="!text-paper">Zen Browser</Title>
<Description class="px-4 lg:px-0 lg:w-2/5"> <Description class="px-4 lg:w-2/5 lg:px-0">
Beautifully designed, privacy-focused, and packed with features. We care Beautifully designed, privacy-focused, and packed with features. We care
about your experience, not your data. about your experience, not your data.
</Description> </Description>
</div> </div>
</div> </div>
<div class="flex flex-col justify-between w-full lg:flex-row gap-6 lg:pr-52 lg:my-12"> <div
<Button href="/download" isPrimary class="h-fit bg-paper w-fit !text-dark"> class="flex w-full flex-col justify-between gap-6 lg:my-12 lg:flex-row lg:pr-52"
>
<Button href="/download" isPrimary class="h-fit w-fit bg-paper !text-dark">
Download Download
<ArrowRight class="size-4" /> <ArrowRight class="size-4" />
</Button> </Button>
<div class="gap-10 flex flex-col"> <div class="flex flex-col gap-10">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Description class="!font-semibold"> <Description class="!font-semibold"> Follow Us </Description>
Follow Us <div class="flex items-center gap-4 opacity-80">
</Description> <a
<div class="opacity-80 flex gap-4 items-center"> href="https://github.com/zen-browser"
<a href="https://github.com/zen-browser" target="_blank" class="font-bold" aria-label="Visit Zen Browser on GitHub"> target="_blank"
class="font-bold"
aria-label="Visit Zen Browser on GitHub"
>
<Fragment set:html={Github.html} /> <Fragment set:html={Github.html} />
</a> </a>
<a href="https://twitter.com/zen_browser" target="_blank" class="font-normal" aria-label="Visit Zen Browser on Twitter"> <a
href="https://twitter.com/zen_browser"
target="_blank"
class="font-normal"
aria-label="Visit Zen Browser on Twitter"
>
<Fragment set:html={Twitter.html} /> <Fragment set:html={Twitter.html} />
</a> </a>
<a href="https://fosstodon.org/@zenbrowser" target="_blank" class="font-normal" aria-label="Visit Zen Browser on Mastodon"> <a
href="https://fosstodon.org/@zenbrowser"
target="_blank"
class="font-normal"
aria-label="Visit Zen Browser on Mastodon"
>
<Fragment set:html={Mastodon.html} /> <Fragment set:html={Mastodon.html} />
</a> </a>
<a href="https://bsky.app/profile/zen-browser.app" target="_blank" class="font-normal" aria-label="Visit Zen Browser on Bluesky"> <a
href="https://bsky.app/profile/zen-browser.app"
target="_blank"
class="font-normal"
aria-label="Visit Zen Browser on Bluesky"
>
<Fragment set:html={Bluesky.html} /> <Fragment set:html={Bluesky.html} />
</a> </a>
<a href="https://www.reddit.com/r/zen_browser" target="_blank" class="font-normal" aria-label="Visit Zen Browser on Reddit"> <a
href="https://www.reddit.com/r/zen_browser"
target="_blank"
class="font-normal"
aria-label="Visit Zen Browser on Reddit"
>
<Fragment set:html={Reddit.html} /> <Fragment set:html={Reddit.html} />
</a> </a>
</div> </div>
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Description class="!font-semibold"> <Description class="!font-semibold"> About Us </Description>
About Us <div class="flex flex-col opacity-80">
</Description>
<div class="opacity-80 flex flex-col">
<a href="/about" class="font-normal">About Us</a> <a href="/about" class="font-normal">About Us</a>
<a href="/privacy-policy" class="font-normal">Privacy Policy</a> <a href="/privacy-policy" class="font-normal">Privacy Policy</a>
</div> </div>
</div> </div>
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Description class="!font-semibold"> <Description class="!font-semibold"> Get Started </Description>
Get Started <div class="flex flex-col opacity-80">
</Description>
<div class="opacity-80 flex flex-col">
<a href="/download" class="font-normal">Download</a> <a href="/download" class="font-normal">Download</a>
<a href="/mods" class="font-normal">Zen Mods</a> <a href="/mods" class="font-normal">Zen Mods</a>
<a href="/release-notes" class="font-normal">Release Notes</a> <a href="/release-notes" class="font-normal">Release Notes</a>
@ -81,25 +106,39 @@ const Reddit = icon({ prefix: "fab", iconName: "reddit" });
</div> </div>
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Description class="!font-semibold"> <Description class="!font-semibold"> Get Help </Description>
Get Help <div class="flex flex-col opacity-80">
</Description>
<div class="opacity-80 flex flex-col">
<a href="https://discord.gg/zen-browser" class="font-normal">Discord</a> <a href="https://discord.gg/zen-browser" class="font-normal">Discord</a>
<div class="flex items-center"> <div class="flex items-center">
<div class="absolute"> <div class="absolute">
<img src="https://uptime.zen-browser.app/embed-badges/pulse?" loading="lazy" alt="Phare badge" class="h-5 w-5 relative right-5 lg:right-6" /> <img
src="https://uptime.zen-browser.app/embed-badges/pulse?"
loading="lazy"
alt="Phare badge"
class="relative right-5 h-5 w-5 lg:right-6"
/>
</div> </div>
<a href="https://uptime.zen-browser.app/" class="font-normal">Uptime Status</a> <a href="https://uptime.zen-browser.app/" class="font-normal"
>Uptime Status</a
>
</div> </div>
<a href="https://github.com/zen-browser/desktop/issues/new/choose" class="font-normal">Report an Issue</a> <a
href="https://github.com/zen-browser/desktop/issues/new/choose"
class="font-normal">Report an Issue</a
>
</div> </div>
</div> </div>
</div> </div>
<div class="flex w-full h-32 items-center"> <div class="flex h-32 w-full items-center">
<div class="flex"> <div class="flex">
Made with ❤️ by the <a href="/about" class="font-bold inline-block ml-2">Zen Browser Team</a> Made with ❤️ by the <a href="/about" class="ml-2 inline-block font-bold"
>Zen Browser Team</a
>
</div> </div>
<Circles white multiplier={0.7} class="ml-auto mb-[-100px] mr-[-80px] hidden lg:block" /> <Circles
white
multiplier={0.7}
class="mb-[-100px] ml-auto mr-[-80px] hidden lg:block"
/>
</div> </div>
</footer> </footer>

View file

@ -4,45 +4,49 @@ import Description from '../components/Description.astro'
import Button from '../components/Button.astro' import Button from '../components/Button.astro'
import { Image } from 'astro:assets' import { Image } from 'astro:assets'
import myImage from '../assets/browsers.png' import myImage from '../assets/browsers.png'
import { ArrowRight } from 'lucide-astro'; import { ArrowRight } from 'lucide-astro'
import { motion } from 'motion/react'; import { motion } from 'motion/react'
import { getTitleAnimation } from '../animations' import { getTitleAnimation } from '../animations'
let titleAnimationCounter = 0; let titleAnimationCounter = 0
function getNewAnimationDelay() { function getNewAnimationDelay() {
titleAnimationCounter++; titleAnimationCounter++
return titleAnimationCounter * 0.15; return titleAnimationCounter * 0.15
} }
function getHeroTitleAnimation() { function getHeroTitleAnimation() {
return getTitleAnimation(getNewAnimationDelay()); return getTitleAnimation(getNewAnimationDelay())
} }
--- ---
<header <header
id="header" id="header"
class="flex py-32 pb-16 md:pb-32 lg:pb-16 w-full flex-col items-center text-center gap-[20%] lg:gap-[15%]" class="flex w-full flex-col items-center gap-[20%] py-32 pb-16 text-center md:pb-32 lg:gap-[15%] lg:pb-16"
> >
<div class="flex flex-col items-center h-full justify-center"> <div class="flex h-full flex-col items-center justify-center">
<motion.a href='/download' className="rounded-full py-0 px-2 py-1 md:px-4 bg-dark mb-10 items-center text-paper text-sm shadow-sm flex gap-2 hover:bg-paper hover:text-dark border-2 border-dark transition-bg hover:duration-200" client:load {...getTitleAnimation(1.8)}> <motion.a
<div> href="/download"
Beta is now available! className="transition-bg mb-10 flex items-center gap-2 rounded-full border-2 border-dark bg-dark px-2 py-0 py-1 text-sm text-paper shadow-sm hover:bg-paper hover:text-dark hover:duration-200 md:px-4"
</div> client:load
{...getTitleAnimation(1.8)}
>
<div>Beta is now available!</div>
<ArrowRight class="size-4" /> <ArrowRight class="size-4" />
</motion.a> </motion.a>
<Title class='relative text-left px-12 lg:px-0 md:text-center leading-[108px] md:!text-7xl lg:!text-9xl !font-normal'> <Title
class="relative px-12 text-left !font-normal leading-[108px] md:text-center md:!text-7xl lg:px-0 lg:!text-9xl"
>
<motion.span client:load {...getHeroTitleAnimation()}> <motion.span client:load {...getHeroTitleAnimation()}>
Welcome Welcome
</motion.span> </motion.span>
<motion.span client:load {...getHeroTitleAnimation()}> <motion.span client:load {...getHeroTitleAnimation()}> to </motion.span>
to
</motion.span>
<br class="hidden md:block" /> <br class="hidden md:block" />
<motion.span client:load {...getHeroTitleAnimation()}> <motion.span client:load {...getHeroTitleAnimation()}> a </motion.span>
a <motion.span
</motion.span> client:load
<motion.span client:load {...getHeroTitleAnimation()} className='italic text-coral'> {...getHeroTitleAnimation()}
className="italic text-coral"
>
calmer calmer
</motion.span> </motion.span>
<motion.span client:load {...getHeroTitleAnimation()}> <motion.span client:load {...getHeroTitleAnimation()}>
@ -50,12 +54,12 @@ function getHeroTitleAnimation() {
</motion.span> </motion.span>
</Title> </Title>
<motion.span client:load {...getHeroTitleAnimation()}> <motion.span client:load {...getHeroTitleAnimation()}>
<Description class='text-left px-12 lg:px-0 md:text-center' <Description class="px-12 text-left md:text-center lg:px-0"
>Beautifully designed, privacy-focused, and packed with features.<br >Beautifully designed, privacy-focused, and packed with features.<br
/>We care about your experience, not your data.</Description />We care about your experience, not your data.</Description
> >
</motion.span> </motion.span>
<div class="w-2/3 md:w-fit mt-6 gap-3 sm:gap-6 flex flex-col md:flex-row"> <div class="mt-6 flex w-2/3 flex-col gap-3 sm:gap-6 md:w-fit md:flex-row">
<motion.span client:load {...getHeroTitleAnimation()}> <motion.span client:load {...getHeroTitleAnimation()}>
<Button class="w-full" href="/download" isPrimary> <Button class="w-full" href="/download" isPrimary>
Download Download
@ -69,5 +73,10 @@ function getHeroTitleAnimation() {
</div> </div>
</header> </header>
<motion.span client:load {...getHeroTitleAnimation()}> <motion.span client:load {...getHeroTitleAnimation()}>
<Image src={myImage} alt="Zen browser" class="mx-auto mb-24" loading="eager" /> <Image
src={myImage}
alt="Zen browser"
class="mx-auto mb-24"
loading="eager"
/>
</motion.span> </motion.span>

View file

@ -1,35 +1,51 @@
--- ---
import Title from '../components/Title.astro' import Title from '../components/Title.astro'
import Description from '../components/Description.astro' import Description from '../components/Description.astro'
import { Image } from 'astro:assets'; import { Image } from 'astro:assets'
import browserCollapsed from '../assets/collapsed.png'; import browserCollapsed from '../assets/collapsed.png'
import browseMultiToolbar from '../assets/multiple-toolbar.png'; import browseMultiToolbar from '../assets/multiple-toolbar.png'
import browserSingleToolbar from '../assets/single-toolbar.png'; import browserSingleToolbar from '../assets/single-toolbar.png'
import Button from './Button.astro'; import Button from './Button.astro'
import { ArrowRight } from 'lucide-astro'; import { ArrowRight } from 'lucide-astro'
--- ---
<section <section
id="customization" id="customization"
class="flex w-full px-4 lg:px-12 xl:px-24 py-36 pt-24 gap-16 flex-col lg:flex-row relative overflow-y-hidden" class="relative flex w-full flex-col gap-16 overflow-y-hidden px-4 py-36 pt-24 lg:flex-row lg:px-12 xl:px-24"
> >
<div> <div>
<Title>Customizable<br class="md:hidden" /> to <br class="hidden md:block" />the last pixel</Title> <Title
<Description class="lg:px-0 lg:w-1/2"> >Customizable<br class="md:hidden" /> to <br class="hidden md:block" />the
Zen Browser is designed to be customizable. You can change every aspect of the browser to suit your needs. Literally, your imagination is the limit. last pixel</Title
>
<Description class="lg:w-1/2 lg:px-0">
Zen Browser is designed to be customizable. You can change every aspect of
the browser to suit your needs. Literally, your imagination is the limit.
</Description> </Description>
<div class="flex mt-4"> <div class="mt-4 flex">
<Button isPrimary href="/mods"> <Button isPrimary href="/mods">
Zen Mods Zen Mods
<ArrowRight className="size-4" /> <ArrowRight className="size-4" />
</Button> </Button>
</div> </div>
</div> </div>
<div class="relative ml-16 h-32 lg:mx-0 lg:w-1/2 flex" id="browser-images"> <div class="relative ml-16 flex h-32 lg:mx-0 lg:w-1/2" id="browser-images">
<Image src={browserCollapsed} alt="Zen browser" class="w-2/3 rounded-md shadow-md absolute top-[5%] left-[65%] -translate-x-1/2" /> <Image
<Image src={browseMultiToolbar} alt="Zen browser" class="w-2/3 rounded-md mx-auto absolute top-1/2 left-1/2 transform -translate-x-1/2" /> src={browserCollapsed}
<Image src={browserSingleToolbar} alt="Zen browser" class="w-2/3 rounded-md shadow-md absolute top-[15%] left-[10%] z-10 -translate-x-1/2" /> alt="Zen browser"
class="absolute left-[65%] top-[5%] w-2/3 -translate-x-1/2 rounded-md shadow-md"
/>
<Image
src={browseMultiToolbar}
alt="Zen browser"
class="absolute left-1/2 top-1/2 mx-auto w-2/3 -translate-x-1/2 transform rounded-md"
/>
<Image
src={browserSingleToolbar}
alt="Zen browser"
class="absolute left-[10%] top-[15%] z-10 w-2/3 -translate-x-1/2 rounded-md shadow-md"
/>
</div> </div>
</section> </section>
<style> <style>

View file

@ -1,5 +1,23 @@
<svg width="32" height="32" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg
<path fill-rule="evenodd" clip-rule="evenodd" d="M32 44.3077C38.7974 44.3077 44.3077 38.7974 44.3077 32C44.3077 25.2027 38.7974 19.6923 32 19.6923C25.2027 19.6923 19.6923 25.2027 19.6923 32C19.6923 38.7974 25.2027 44.3077 32 44.3077ZM41.8462 32C41.8462 37.4379 37.4379 41.8462 32 41.8462C26.5621 41.8462 22.1538 37.4379 22.1538 32C22.1538 26.5621 26.5621 22.1538 32 22.1538C37.4379 22.1538 41.8462 26.5621 41.8462 32Z" fill="currentColor"/> width="32"
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.3333 32C53.3333 43.7821 43.7821 53.3333 32 53.3333C20.2179 53.3333 10.6667 43.7821 10.6667 32C10.6667 20.2179 20.2179 10.6667 32 10.6667C43.7821 10.6667 53.3333 20.2179 53.3333 32ZM32 49.2308C41.5163 49.2308 49.2308 41.5163 49.2308 32C49.2308 22.4837 41.5163 14.7692 32 14.7692C22.4837 14.7692 14.7692 22.4837 14.7692 32C14.7692 41.5163 22.4837 49.2308 32 49.2308Z" fill="currentColor"/> height="32"
<path fill-rule="evenodd" clip-rule="evenodd" d="M64 32C64 49.6731 49.6731 64 32 64C14.3269 64 0 49.6731 0 32C0 14.3269 14.3269 0 32 0C49.6731 0 64 14.3269 64 32ZM32 58.2564C46.501 58.2564 58.2564 46.501 58.2564 32C58.2564 17.499 46.501 5.74359 32 5.74359C17.499 5.74359 5.74359 17.499 5.74359 32C5.74359 46.501 17.499 58.2564 32 58.2564Z" fill="currentColor"/> viewBox="0 0 64 64"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M32 44.3077C38.7974 44.3077 44.3077 38.7974 44.3077 32C44.3077 25.2027 38.7974 19.6923 32 19.6923C25.2027 19.6923 19.6923 25.2027 19.6923 32C19.6923 38.7974 25.2027 44.3077 32 44.3077ZM41.8462 32C41.8462 37.4379 37.4379 41.8462 32 41.8462C26.5621 41.8462 22.1538 37.4379 22.1538 32C22.1538 26.5621 26.5621 22.1538 32 22.1538C37.4379 22.1538 41.8462 26.5621 41.8462 32Z"
fill="currentColor"></path>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M53.3333 32C53.3333 43.7821 43.7821 53.3333 32 53.3333C20.2179 53.3333 10.6667 43.7821 10.6667 32C10.6667 20.2179 20.2179 10.6667 32 10.6667C43.7821 10.6667 53.3333 20.2179 53.3333 32ZM32 49.2308C41.5163 49.2308 49.2308 41.5163 49.2308 32C49.2308 22.4837 41.5163 14.7692 32 14.7692C22.4837 14.7692 14.7692 22.4837 14.7692 32C14.7692 41.5163 22.4837 49.2308 32 49.2308Z"
fill="currentColor"></path>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M64 32C64 49.6731 49.6731 64 32 64C14.3269 64 0 49.6731 0 32C0 14.3269 14.3269 0 32 0C49.6731 0 64 14.3269 64 32ZM32 58.2564C46.501 58.2564 58.2564 46.501 58.2564 32C58.2564 17.499 46.501 5.74359 32 5.74359C17.499 5.74359 5.74359 17.499 5.74359 32C5.74359 46.501 17.499 58.2564 32 58.2564Z"
fill="currentColor"></path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

View file

@ -1,35 +1,46 @@
--- ---
import Title from '../components/Title.astro' import Title from '../components/Title.astro'
import Description from '../components/Description.astro' import Description from '../components/Description.astro'
import Button from '../components/Button.astro' import Button from '../components/Button.astro'
import { Astronav, MenuItems, MenuIcon, Dropdown, DropdownItems, DropdownSubmenu } from "astro-navbar"; import {
Astronav,
MenuItems,
MenuIcon,
Dropdown,
DropdownItems,
DropdownSubmenu,
} from 'astro-navbar'
import { ArrowRight, ChevronDown, Download, DownloadCloud } from 'lucide-astro' import { ArrowRight, ChevronDown, Download, DownloadCloud } from 'lucide-astro'
import Logo from './Logo.astro'; import Logo from './Logo.astro'
import { ThemeSwitch } from 'free-astro-components' import { ThemeSwitch } from 'free-astro-components'
--- ---
<nav <nav
id="nav-bar" id="nav-bar"
class="flex justify-between w-full items-center lg:pt-3 lg:px-6 z-20 relative" class="relative z-20 flex w-full items-center justify-between lg:px-6 lg:pt-3"
> >
<Astronav> <Astronav>
<MenuItems class="w-full lg:w-fit p-2 bg-paper mx-auto flex gap-2 lg:gap-20 relative"> <MenuItems
<a class="font-bold text-lg items-center flex gap-2 mr-3" href="/"> class="relative mx-auto flex w-full gap-2 bg-paper p-2 lg:w-fit lg:gap-20"
<Logo class="text-dark" /> <span class="hidden lg:block">zen browser</span> >
<a class="mr-3 flex items-center gap-2 text-lg font-bold" href="/">
<Logo class="text-dark" />
<span class="hidden lg:block">zen browser</span>
</a> </a>
<div class="gap-6 flex items-center text-xs sm:text-sm lg:text-base"> <div class="flex items-center gap-6 text-xs sm:text-sm lg:text-base">
<Dropdown class="group"> <Dropdown class="group">
<button class="flex items-center"> <button class="flex items-center">
<span>Getting Started</span> <span>Getting Started</span>
<ChevronDown class="group-open:rotate-180 transform transition-transform duration-200 size-4 md:ml-2" /> <ChevronDown
class="size-4 transform transition-transform duration-200 group-open:rotate-180 md:ml-2"
/>
</button> </button>
<DropdownItems> <DropdownItems>
<div class="navbar-dropdown w-full lg:w-fit top-16 left-0 right-1/4"> <div
<a class="dropdown-item row-span-2 bg-dark/5" href="/mods"> class="navbar-dropdown left-0 right-1/4 top-16 w-full lg:w-fit"
<div class="dropdown-title"> >
Zen Mods <a class="dropdown-item bg-dark/5 row-span-2" href="/mods">
</div> <div class="dropdown-title">Zen Mods</div>
<div class="dropdown-description"> <div class="dropdown-description">
Customize your browsing experience with Zen Mods. Customize your browsing experience with Zen Mods.
</div> </div>
@ -39,17 +50,13 @@ import { ThemeSwitch } from 'free-astro-components'
</Button> </Button>
</a> </a>
<a class="dropdown-item" href="/release-notes"> <a class="dropdown-item" href="/release-notes">
<div class="dropdown-title"> <div class="dropdown-title">Release Notes</div>
Release Notes
</div>
<div class="dropdown-description"> <div class="dropdown-description">
Stay up to date with the latest features and improvements. Stay up to date with the latest features and improvements.
</div> </div>
</a> </a>
<a class="dropdown-item" href="https://discord.gg/zen-browser"> <a class="dropdown-item" href="https://discord.gg/zen-browser">
<div class="dropdown-title"> <div class="dropdown-title">Discord</div>
Discord
</div>
<div class="dropdown-description"> <div class="dropdown-description">
Join our community on Discord to chat with other Zen users! Join our community on Discord to chat with other Zen users!
</div> </div>
@ -60,38 +67,38 @@ import { ThemeSwitch } from 'free-astro-components'
<Dropdown class="group"> <Dropdown class="group">
<button class="flex items-center"> <button class="flex items-center">
<span>Useful Links</span> <span>Useful Links</span>
<ChevronDown class="group-open:rotate-180 transform transition-transform duration-200 size-4 md:ml-2" /> <ChevronDown
class="size-4 transform transition-transform duration-200 group-open:rotate-180 md:ml-2"
/>
</button> </button>
<DropdownItems> <DropdownItems>
<div class="navbar-dropdown w-full lg:w-fit left-0 !grid-cols-1 gap-1 top-16 lg:left-1/3 lg:right-1/4"> <div
class="navbar-dropdown left-0 top-16 w-full !grid-cols-1 gap-1 lg:left-1/3 lg:right-1/4 lg:w-fit"
>
<a class="dropdown-item" href="/donate"> <a class="dropdown-item" href="/donate">
<div class="dropdown-title"> <div class="dropdown-title">Donate ❤️</div>
Donate ❤️
</div>
<div class="dropdown-description"> <div class="dropdown-description">
Support the development of Zen Browser with a donation. Support the development of Zen Browser with a donation.
</div> </div>
</a> </a>
<a class="dropdown-item" href="/about"> <a class="dropdown-item" href="/about">
<div class="dropdown-title"> <div class="dropdown-title">About Us 🌟</div>
About Us 🌟
</div>
<div class="dropdown-description"> <div class="dropdown-description">
Learn more about the team behind Zen Browser. Learn more about the team behind Zen Browser.
</div> </div>
</a> </a>
<a class="dropdown-item" href="https://docs.zen-browser.app"> <a class="dropdown-item" href="https://docs.zen-browser.app">
<div class="dropdown-title"> <div class="dropdown-title">Documentation</div>
Documentation
</div>
<div class="dropdown-description"> <div class="dropdown-description">
Learn how to use Zen Browser with our documentation. Learn how to use Zen Browser with our documentation.
</div> </div>
</a> </a>
<a class="dropdown-item" href="https://github.com/zen-browser" target="_blank"> <a
<div class="dropdown-title"> class="dropdown-item"
GitHub href="https://github.com/zen-browser"
</div> target="_blank"
>
<div class="dropdown-title">GitHub</div>
<div class="dropdown-description"> <div class="dropdown-description">
Contribute to the development of Zen Browser on GitHub. Contribute to the development of Zen Browser on GitHub.
</div> </div>
@ -99,16 +106,16 @@ import { ThemeSwitch } from 'free-astro-components'
</div> </div>
</DropdownItems> </DropdownItems>
</Dropdown> </Dropdown>
<a class="items-center hidden lg:block" href="/mods"> <a class="hidden items-center lg:block" href="/mods">
<span>Mods</span> <span>Mods</span>
</a> </a>
</div> </div>
<div class="flex gap-2 ml-auto"> <div class="ml-auto flex gap-2">
<div id="theme-switcher" class="ml-auto md:mr-2"> <div id="theme-switcher" class="ml-auto md:mr-2">
<ThemeSwitch label="" class="py-2 px-1" /> <ThemeSwitch label="" class="px-1 py-2" />
</div> </div>
<Button href="/download" class="ml-auto" isPrimary> <Button href="/download" class="ml-auto" isPrimary>
<span class="hidden md:flex items-center gap-2"> <span class="hidden items-center gap-2 md:flex">
Download Download
<ArrowRight class="size-4" /> <ArrowRight class="size-4" />
</span> </span>
@ -122,10 +129,10 @@ import { ThemeSwitch } from 'free-astro-components'
</nav> </nav>
<style> <style>
.navbar-dropdown { .navbar-dropdown {
@apply absolute bg-paper shadow-sm rounded-lg border-2 border-dark p-3 grid gap-2 grid-cols-2; @apply absolute grid grid-cols-2 gap-2 rounded-lg border-2 border-dark bg-paper p-3 shadow-sm;
& .dropdown-item { & .dropdown-item {
@apply flex gap-2 p-4 flex-col rounded-lg select-none cursor-pointer transition-colors duration-200; @apply flex cursor-pointer select-none flex-col gap-2 rounded-lg p-4 transition-colors duration-200;
&:hover { &:hover {
@apply bg-muted; @apply bg-muted;

View file

@ -1,77 +1,105 @@
--- ---
import { Accordion, AccordionItem } from 'free-astro-components'; import { Accordion, AccordionItem } from 'free-astro-components'
import { Info } from 'lucide-astro'; import { Info } from 'lucide-astro'
import type { ReleaseNote, BreakingChange } from '../release-notes' import type { ReleaseNote, BreakingChange } from '../release-notes'
export type Props = ReleaseNote; export type Props = ReleaseNote
const {isTwilight, ...props} = Astro.props; const { isTwilight, ...props } = Astro.props
let date; let date
if (props.date) { if (props.date) {
const [day, month, year] = props.date.split('/'); const [day, month, year] = props.date.split('/')
date = new Date(Date.parse(`${year}-${month}-${day}`)); date = new Date(Date.parse(`${year}-${month}-${day}`))
} }
--- ---
<section <section
class="relative mt-24 flex flex-col border-t pt-24 lg:flex-row release-note-item" class="release-note-item relative mt-24 flex flex-col border-t pt-24 lg:flex-row"
id={props.version} id={props.version}
> >
<div class="px-5 md:px-10 md:pr-32"> <div class="px-5 md:px-10 md:pr-32">
{isTwilight ? ( {
<a class="rounded-full bg-coral w-fit py-1 px-3 text-xs !mb-2 block text-paper" href="/download?twilight"> isTwilight ? (
<a
class="!mb-2 block w-fit rounded-full bg-coral px-3 py-1 text-xs text-paper"
href="/download?twilight"
>
Twilight Twilight
</a> </a>
) : null} ) : null
}
<h1 class="text-3xl font-bold"> <h1 class="text-3xl font-bold">
{isTwilight ? ( {
<> isTwilight ? (
Twilight changes for {props.version} 🌙 <>Twilight changes for {props.version} 🌙</>
</>
) : ( ) : (
<> <>Release notes for {props.version} 🎉</>
Release notes for {props.version} 🎉 )
</> }
)}
</h1> </h1>
{date && date.toLocaleDateString('en-US', { dateStyle: 'long' })} {date && date.toLocaleDateString('en-US', { dateStyle: 'long' })}
<div class="mt-2"> <div class="mt-2">
<a rel="noopener noreferrer" class="whitespace-nowrap text-sm text-coral opacity-60" target="_blank" href={`https://github.com/zen-browser/desktop/releases/tag/${isTwilight ? 'twilight' : props.version}`}>Github Release</a> <a
{!isTwilight ? ( rel="noopener noreferrer"
class="whitespace-nowrap text-sm text-coral opacity-60"
target="_blank"
href={`https://github.com/zen-browser/desktop/releases/tag/${isTwilight ? 'twilight' : props.version}`}
>Github Release</a
>
{
!isTwilight ? (
<> <>
<span class="text-muted-foreground mx-auto">•</span> <span class="text-muted-foreground mx-auto">•</span>
<a rel="noopener noreferrer" class="whitespace-nowrap text-sm text-coral opacity-60" target="_blank" href={`https://github.com/zen-browser/desktop/actions/runs/${props.workflowId}`}>Workflow run</a> <a
rel="noopener noreferrer"
class="whitespace-nowrap text-sm text-coral opacity-60"
target="_blank"
href={`https://github.com/zen-browser/desktop/actions/runs/${props.workflowId}`}
>
Workflow run
</a>
</> </>
) : null} ) : null
}
</div> </div>
<div class="mt-6 text-sm opacity-70 text-muted-forground flex"> <div class="text-muted-forground mt-6 flex text-sm opacity-70">
{isTwilight ? ( {isTwilight ? <Info class="mx-4 my-0 size-6 text-yellow-500" /> : null}
<Info class="my-0 mx-4 size-6 text-yellow-500" />
) : null}
<p class="m-0"> <p class="m-0">
{isTwilight ? ( {
isTwilight ? (
<> <>
Please note that Twilight is a pre-release version of Zen Browser. It may contain bugs and unfinished features. Please note that Twilight is a pre-release version of Zen Browser.
It may contain bugs and unfinished features.
</> </>
) : null} ) : null
If you encounter any issues, please report them on <a rel="noopener noreferrer" target="_blank" href="https://github.com/zen-browser/desktop/issues/" class="text-underline text-coral">the issues page</a>. }
</div> If you encounter any issues, please report them on <a
rel="noopener noreferrer"
target="_blank"
href="https://github.com/zen-browser/desktop/issues/"
class="text-underline text-coral">the issues page</a
>.
</p> </p>
</div>
{props.extra ? ( {
<p class="text-md mt-2 text-muted-foreground extra"> props.extra ? (
<p class="text-md text-muted-foreground extra mt-2">
<Fragment set:html={props.extra.replace(/\n/g, '<br />')} /> <Fragment set:html={props.extra.replace(/\n/g, '<br />')} />
</p> </p>
) : null ) : null
} }
<div class="mt-8" data-orientation="vertical"></div> <div class="mt-8" data-orientation="vertical"></div>
<Accordion> <Accordion>
{props.fixes ? ( {
props.fixes ? (
<AccordionItem title="Fixes" name="fixes"> <AccordionItem title="Fixes" name="fixes">
<ul class="list-disc list-inside"> <ul class="list-inside list-disc">
{props.fixes.map((fix: any) => ( {props.fixes.map((fix: any) => (
<li class="text-md text-muted-foreground"> <li class="text-md text-muted-foreground">
{typeof fix === 'string' ? fix : ( {typeof fix === 'string' ? (
fix
) : (
<> <>
{fix.description} {fix.description}
{fix.issue ? ( {fix.issue ? (
@ -91,30 +119,39 @@ if (props.date) {
))} ))}
</ul> </ul>
</AccordionItem> </AccordionItem>
) : null} ) : null
{props.features ? ( }
{
props.features ? (
<AccordionItem title="Features" name="features"> <AccordionItem title="Features" name="features">
<ul class="list-disc list-inside"> <ul class="list-inside list-disc">
{props.features.map((feature: string) => ( {props.features.map((feature: string) => (
<li class="text-md text-muted-foreground">{feature}</li> <li class="text-md text-muted-foreground">{feature}</li>
))} ))}
</ul> </ul>
</AccordionItem> </AccordionItem>
) : null} ) : null
{props.themeChanges ? ( }
{
props.themeChanges ? (
<AccordionItem title="Theme Changes" name="themeChanges"> <AccordionItem title="Theme Changes" name="themeChanges">
<ul class="list-disc list-inside"> <ul class="list-inside list-disc">
{props.themeChanges.map((themeChange: string) => ( {props.themeChanges.map((themeChange: string) => (
<li class="text-md text-muted-foreground">{themeChange}</li> <li class="text-md text-muted-foreground">{themeChange}</li>
))} ))}
</ul> </ul>
</AccordionItem> </AccordionItem>
) : null} ) : null
{props.breakingChanges ? ( }
{
props.breakingChanges ? (
<AccordionItem title="Breaking Changes" name="breakingChanges"> <AccordionItem title="Breaking Changes" name="breakingChanges">
<ul class="list-disc list-inside"> <ul class="list-inside list-disc">
{props.breakingChanges.map((breakingChange: BreakingChange) => ( {props.breakingChanges.map((breakingChange: BreakingChange) => (
<li class="text-md text-muted-foreground">{typeof breakingChange === 'string' ? breakingChange : ( <li class="text-md text-muted-foreground">
{typeof breakingChange === 'string' ? (
breakingChange
) : (
<> <>
{breakingChange.description} {breakingChange.description}
<a <a
@ -127,13 +164,15 @@ if (props.date) {
Learn more Learn more
</a> </a>
</> </>
)}</li> )}
</li>
))} ))}
</ul> </ul>
</AccordionItem> </AccordionItem>
) : null} ) : null
}
</Accordion>
</div> </div>
</section>
<style is:global> <style is:global>
.ac-accordion-item-title { .ac-accordion-item-title {
@apply !text-dark; @apply !text-dark;
@ -152,13 +191,16 @@ if (props.date) {
transition: height 0.2s ease-in-out !important; transition: height 0.2s ease-in-out !important;
& li { & li {
opacity: .5; opacity: 0.5;
} }
} }
.ac-accordion { .ac-accordion {
&.ac-accordion--light { &.ac-accordion--light {
> * + * { > * + * {
border-color: light-dark(rgba(0,0,0,.1), rgba(255,255,255,.1)) !important; border-color: light-dark(
rgba(0, 0, 0, 0.1),
rgba(255, 255, 255, 0.1)
) !important;
width: 100%; width: 100%;
} }
} }
@ -171,6 +213,7 @@ if (props.date) {
} }
.release-note-item { .release-note-item {
border-color: light-dark(rgba(0,0,0,.2), rgba(255,255,255,.2)); border-color: light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2));
} }
</style> </style>
</section>

View file

@ -1,8 +1,8 @@
--- ---
const { class: className } = Astro.props; const { class: className } = Astro.props
--- ---
<h1 class:list={["title text-dark", className]}> <h1 class:list={['title text-dark', className]}>
<slot /> <slot />
</h1> </h1>
<style> <style>

View file

@ -1,4 +1,4 @@
import { format } from "date-fns" import { format } from 'date-fns'
export interface ZenTheme { export interface ZenTheme {
name: string name: string
@ -37,5 +37,5 @@ export function getAuthorLink(author: string): string {
} }
export function getLocalizedDate(date: Date): string { export function getLocalizedDate(date: Date): string {
return format(date, "PP"); return format(date, 'PP')
} }

View file

@ -6,45 +6,121 @@ import Layout from '../layouts/Layout.astro'
--- ---
<Layout title="About - Zen Browser"> <Layout title="About - Zen Browser">
<main class="min-h-screen relative flex flex-col py-24 justify-center items-center"> <main
<div class="text-center p-4 mb-24 lg:w-1/2"> class="relative flex min-h-screen flex-col items-center justify-center py-24"
>
<div class="mb-24 p-4 text-center lg:w-1/2">
<Title>About Us</Title> <Title>About Us</Title>
<Description> <Description>
We are simply a group of developers and designers who care about your experience on the web. We believe that the internet should be a place where you can explore, learn, and connect without worrying about your data being collected. We are simply a group of developers and designers who care about your
experience on the web. We believe that the internet should be a place
where you can explore, learn, and connect without worrying about your
data being collected.
</Description> </Description>
<Button href="/donate" class="w-fit mx-auto mt-4" isPrimary>A little help?</Button> <Button href="/donate" class="mx-auto mt-4 w-fit" isPrimary
>A little help?</Button
>
</div> </div>
<div class="w-full relative flex flex-col lg:flex-row justify-center items-center"> <div
<div class="p-8 lg:pr-24 flex flex-col lg:w-1/3"> class="relative flex w-full flex-col items-center justify-center lg:flex-row"
<div class="font-bold text-6xl">Main Team</div> >
<div class="flex flex-col p-8 lg:w-1/3 lg:pr-24">
<div class="text-6xl font-bold">Main Team</div>
<Description> <Description>
This list shows the main team members who are working hard to bring you the best browsing experience. This list shows the main team members who are working hard to bring
you the best browsing experience.
</Description> </Description>
<div class="mt-4"> <div class="mt-4">
<ul> <ul>
<li class="text-sm"><a href="https://github.com/mr-cheff"><strong class="font-bold">Mauro Baladés</strong></a><span class="opacity-60">: Creator, Main Developer</span></li> <li class="text-sm">
<li class="text-sm mt-1"><strong class="italic">Oscar Gonzalez</strong><span class="opacity-60"> Site Reliability Engineer (SRE) and code signing.</span></li> <a href="https://github.com/mr-cheff"
<li class="text-sm mt-1"><a href="https://janheres.eu/"><strong class="font-bold">Jan Heres</strong></a><span class="opacity-60">: Active contributor and helps with MacOS builds</span></li> ><strong class="font-bold">Mauro Baladés</strong></a
<li class="text-sm mt-1"><a href="https://github.com/BrhmDev"><strong class="font-bold">BrhmDev</strong></a><span class="opacity-60">: Active contributor with great contributions</span></li> ><span class="opacity-60">: Creator, Main Developer</span>
<li class="text-sm mt-1"><a href="https://thatcanoa.org/"><strong class="font-bold">Canoa</strong></a><span class="opacity-60">: Active contributor, and very active in issue handling and website management</span></li> </li>
<li class="text-sm mt-1"><a href="https://cybrneon.xyz/"><strong class="font-bold">Adam</strong></a><span class="opacity-60">: Branding and design</span></li> <li class="mt-1 text-sm">
<li class="text-sm mt-1"><a href="https://github.com/kristijanribaric"><strong class="font-bold">kristijanribaric</strong></a><span class="opacity-60">: Active contributor</span></li> <strong class="italic">Oscar Gonzalez</strong><span
<li class="text-sm mt-1"><a href="https://github.com/n7itro"><strong class="font-bold">n7itro</strong></a><span class="opacity-60">: Active contributor and release notes writer</span></li> class="opacity-60"
<li class="text-sm mt-1"><a href="https://josuegalre.netlify.app/"><strong class="font-bold">Bryan Galdámez</strong></a><span class="opacity-60">: Huge contributor on theme functionalities</span></li> >
<li class="text-sm mt-1"><a href="https://iamjafeth.com/"><strong class="font-bold">Jafeth Garro</strong></a><span class="opacity-60">: Documentation writer</span></li> Site Reliability Engineer (SRE) and code signing.</span>
<li class="text-sm mt-1"><a href="https://github.com/LarveyOfficial/"><strong class="font-bold">Larvey</strong></a><span class="opacity-60">: AUR maintainer</span></li> </li>
<li class="text-sm mt-1"><strong class="italic">Daniel García</strong><span class="opacity-60">: MacOS certificate and app notarization maintainer</span></li> <li class="mt-1 text-sm">
<a href="https://janheres.eu/"
><strong class="font-bold">Jan Heres</strong></a
><span class="opacity-60"
>: Active contributor and helps with MacOS builds</span>
</li>
<li class="mt-1 text-sm">
<a href="https://github.com/BrhmDev"
><strong class="font-bold">BrhmDev</strong></a
><span class="opacity-60"
>: Active contributor with great contributions</span>
</li>
<li class="mt-1 text-sm">
<a href="https://thatcanoa.org/"
><strong class="font-bold">Canoa</strong></a
><span class="opacity-60"
>: Active contributor, and very active in issue handling and
website management</span>
</li>
<li class="mt-1 text-sm">
<a href="https://cybrneon.xyz/"
><strong class="font-bold">Adam</strong></a
><span class="opacity-60">: Branding and design</span>
</li>
<li class="mt-1 text-sm">
<a href="https://github.com/kristijanribaric"
><strong class="font-bold">kristijanribaric</strong></a
><span class="opacity-60">: Active contributor</span>
</li>
<li class="mt-1 text-sm">
<a href="https://github.com/n7itro"
><strong class="font-bold">n7itro</strong></a
><span class="opacity-60"
>: Active contributor and release notes writer</span>
</li>
<li class="mt-1 text-sm">
<a href="https://josuegalre.netlify.app/"
><strong class="font-bold">Bryan Galdámez</strong></a
><span class="opacity-60"
>: Huge contributor on theme functionalities</span>
</li>
<li class="mt-1 text-sm">
<a href="https://iamjafeth.com/"
><strong class="font-bold">Jafeth Garro</strong></a
><span class="opacity-60">: Documentation writer</span>
</li>
<li class="mt-1 text-sm">
<a href="https://github.com/LarveyOfficial/"
><strong class="font-bold">Larvey</strong></a
><span class="opacity-60">: AUR maintainer</span>
</li>
<li class="mt-1 text-sm">
<strong class="italic">Daniel García</strong><span
class="opacity-60"
>: MacOS certificate and app notarization maintainer</span>
</li>
</ul> </ul>
</div> </div>
</div> </div>
<div class="hidden lg:block h-full w-[1px] bg-dark absolute opacity-15"></div> <div class="absolute hidden h-full w-[1px] bg-dark opacity-15 lg:block">
<div class="p-8 lg:pl-24 flex flex-col lg:w-1/3"> </div>
<div class="font-bold text-6xl">Contributors</div> <div class="flex flex-col p-8 lg:w-1/3 lg:pl-24">
<div class="text-6xl font-bold">Contributors</div>
<Description> <Description>
Here are all the wonderful people that decided to contribute to the project! The first set of images are from the desktop repository, and the second set is from the website repository. Here are all the wonderful people that decided to contribute to the
project! The first set of images are from the desktop repository, and
the second set is from the website repository.
</Description> </Description>
<img src="https://contributors-img.web.app/image?repo=zen-browser/desktop" alt="Contributors" class="mt-4" /> <img
<img src="https://contributors-img.web.app/image?repo=zen-browser/www" alt="Contributors (website)" class="mt-4" /> src="https://contributors-img.web.app/image?repo=zen-browser/desktop"
alt="Contributors"
class="mt-4"
/>
<img
src="https://contributors-img.web.app/image?repo=zen-browser/www"
alt="Contributors (website)"
class="mt-4"
/>
</div> </div>
</div> </div>
</main> </main>

View file

@ -7,7 +7,7 @@ import Layout from '../layouts/Layout.astro'
--- ---
<Layout title="Donate - Zen Browser"> <Layout title="Donate - Zen Browser">
<main class="flex flex-col items-center pt-36 pb-52"> <main class="flex flex-col items-center pb-52 pt-36">
<div class="mb-24 p-4 text-center lg:w-1/2"> <div class="mb-24 p-4 text-center lg:w-1/2">
<Title>Donate!</Title> <Title>Donate!</Title>
<Description> <Description>
@ -16,7 +16,9 @@ import Layout from '../layouts/Layout.astro'
us. us.
</Description> </Description>
</div> </div>
<div class="relative flex w-full flex-col justify-center items-center lg:flex-row"> <div
class="relative flex w-full flex-col items-center justify-center lg:flex-row"
>
<div class="flex flex-col p-8 lg:w-1/3 lg:pr-24"> <div class="flex flex-col p-8 lg:w-1/3 lg:pr-24">
<div class="text-6xl font-bold">Patreon</div> <div class="text-6xl font-bold">Patreon</div>
<Description> <Description>
@ -24,13 +26,17 @@ import Layout from '../layouts/Layout.astro'
choose the level of support that works best for you. choose the level of support that works best for you.
</Description> </Description>
<div class="mt-6"> <div class="mt-6">
<Button isPrimary href="https://www.patreon.com/zen_browser" class="w-fit"> <Button
isPrimary
href="https://www.patreon.com/zen_browser"
class="w-fit"
>
Go to Patreon Go to Patreon
<ArrowRight class="size-4" /> <ArrowRight class="size-4" />
</Button> </Button>
</div> </div>
</div> </div>
<div class="hidden h-72 w-[1px] bg-dark lg:block opacity-15"></div> <div class="hidden h-72 w-[1px] bg-dark opacity-15 lg:block"></div>
<div class="flex flex-col p-8 lg:w-1/3 lg:pl-24"> <div class="flex flex-col p-8 lg:w-1/3 lg:pl-24">
<div class="text-6xl font-bold">Ko-fi</div> <div class="text-6xl font-bold">Ko-fi</div>
<Description> <Description>

View file

@ -3,31 +3,38 @@ import Button from '../components/Button.astro'
import Description from '../components/Description.astro' import Description from '../components/Description.astro'
import Title from '../components/Title.astro' import Title from '../components/Title.astro'
import Layout from '../layouts/Layout.astro' import Layout from '../layouts/Layout.astro'
import { Image } from 'astro:assets'; import { Image } from 'astro:assets'
import myImage from '../assets/app-icon.png'; import myImage from '../assets/app-icon.png'
import { library, icon } from "@fortawesome/fontawesome-svg-core"; import { library, icon } from '@fortawesome/fontawesome-svg-core'
import { faWindows, faLinux, faApple } from "@fortawesome/free-brands-svg-icons"; import { faWindows, faLinux, faApple } from '@fortawesome/free-brands-svg-icons'
import { ArrowLeft, ArrowRight, HardDriveDownload, Info } from 'lucide-astro'; import { ArrowLeft, ArrowRight, HardDriveDownload, Info } from 'lucide-astro'
library.add(faWindows, faLinux, faApple); library.add(faWindows, faLinux, faApple)
const windowsIcon = icon({ prefix: "fab", iconName: "windows" }); const windowsIcon = icon({ prefix: 'fab', iconName: 'windows' })
const linuxIcon = icon({ prefix: "fab", iconName: "linux" }); const linuxIcon = icon({ prefix: 'fab', iconName: 'linux' })
const appleIcon = icon({ prefix: "fab", iconName: "apple" }); const appleIcon = icon({ prefix: 'fab', iconName: 'apple' })
--- ---
<Layout title="Welcome to Zen"> <Layout title="Welcome to Zen">
<main class="flex flex-col justify-center items-center min-h-screen"> <main class="flex min-h-screen flex-col items-center justify-center">
<div class="flex justify-center flex-col px-2 md:px-12 lg:px-auto lg:flex-row"> <div
<Image src={myImage} alt="Zen Browser" class="w-24 h-24" loading="eager" /> class="lg:px-auto flex flex-col justify-center px-2 md:px-12 lg:flex-row"
<div class="p-4 mb-24 lg:w-1/2 flex flex-col gap-6"> >
<Image
src={myImage}
alt="Zen Browser"
class="h-24 w-24"
loading="eager"
/>
<div class="mb-24 flex flex-col gap-6 p-4 lg:w-1/2">
<div> <div>
<Title>Try out <span id="zen-name">Zen</span>!</Title> <Title>Try out <span id="zen-name">Zen</span>!</Title>
<Description> <Description>
Start browsing the web with Zen Browser today. Beautifully designed, Start browsing the web with Zen Browser today. Beautifully designed,
privacy-focused, and packed with features! privacy-focused, and packed with features!
</Description> </Description>
<Description id="twilight-info" class="hidden mt-6"> <Description id="twilight-info" class="mt-6 hidden">
<strong>Twilight Mode:</strong> You're currently in Twilight mode, this <strong>Twilight Mode:</strong> You're currently in Twilight mode, this
means you're downloading the latest experimental features and updates. means you're downloading the latest experimental features and updates.
</Description> </Description>
@ -38,121 +45,197 @@ const appleIcon = icon({ prefix: "fab", iconName: "apple" });
Before downloading, we need to know your system info! Before downloading, we need to know your system info!
</div> </div>
<div class="form-description"> <div class="form-description">
Please select your operating system below, we already auto-detected Please select your operating system below, we already
it for you! auto-detected it for you!
</div> </div>
<form class="flex flex-col gap-2 form-select" id="os-select"> <form class="form-select flex flex-col gap-2" id="os-select">
<input type="radio" name="os" value="windows" id="os-select-windows" class="hidden" /> <input
<label class="flex border-2 rounded-md p-2 w-full border-dark gap-2 px-4 items-center cursor-pointer" for="os-select-windows"> type="radio"
name="os"
value="windows"
id="os-select-windows"
class="hidden"
/>
<label
class="flex w-full cursor-pointer items-center gap-2 rounded-md border-2 border-dark p-2 px-4"
for="os-select-windows"
>
<Fragment set:html={windowsIcon.html} /> <Fragment set:html={windowsIcon.html} />
<div>Windows</div> <div>Windows</div>
</label> </label>
<input type="radio" name="os" value="linux" id="os-select-linux" class="hidden" /> <input
<label class="flex border-2 rounded-md p-2 w-full border-dark gap-2 px-4 items-center cursor-pointer" for="os-select-linux"> type="radio"
name="os"
value="linux"
id="os-select-linux"
class="hidden"
/>
<label
class="flex w-full cursor-pointer items-center gap-2 rounded-md border-2 border-dark p-2 px-4"
for="os-select-linux"
>
<Fragment set:html={linuxIcon.html} /> <Fragment set:html={linuxIcon.html} />
<div>Linux</div> <div>Linux</div>
</label> </label>
<input type="radio" name="os" value="macos" id="os-select-macos" class="hidden" /> <input
<label class="flex border-2 rounded-md p-2 w-full border-dark gap-2 px-4 items-center cursor-pointer" for="os-select-macos"> type="radio"
name="os"
value="macos"
id="os-select-macos"
class="hidden"
/>
<label
class="flex w-full cursor-pointer items-center gap-2 rounded-md border-2 border-dark p-2 px-4"
for="os-select-macos"
>
<Fragment set:html={appleIcon.html} /> <Fragment set:html={appleIcon.html} />
<div>MacOS</div> <div>MacOS</div>
</label> </label>
</form> </form>
</div> </div>
<div id="form-macos-download" class="form-item hidden"> <div id="form-macos-download" class="form-item hidden">
<div class="form-title"> <div class="form-title">Download Zen for MacOS!</div>
Download Zen for MacOS!
</div>
<div class="form-description"> <div class="form-description">
You can choose between the Intel or ARM version of Zen Browser, select You can choose between the Intel or ARM version of Zen Browser,
the one that fits your system. select the one that fits your system.
</div> </div>
<div class="flex flex-col gap-4 form-select"> <div class="form-select flex flex-col gap-4">
<div class="border-2 p-2 px-4 border-dark text-dark rounded-md shadow-sm flex items-center cursor-pointer justify-between hover:bg-dark hover:text-paper transition-all duration-100" id="macos-arm-download"> <div
class="flex cursor-pointer items-center justify-between rounded-md border-2 border-dark p-2 px-4 text-dark shadow-sm transition-all duration-100 hover:bg-dark hover:text-paper"
id="macos-arm-download"
>
Download ARM Version Download ARM Version
<HardDriveDownload class="size-4" /> <HardDriveDownload class="size-4" />
</div> </div>
<div class="border-2 p-2 px-4 border-dark text-dark rounded-md shadow-sm flex items-center cursor-pointer justify-between hover:bg-dark hover:text-paper transition-all duration-100" id="macos-intel-download"> <div
class="flex cursor-pointer items-center justify-between rounded-md border-2 border-dark p-2 px-4 text-dark shadow-sm transition-all duration-100 hover:bg-dark hover:text-paper"
id="macos-intel-download"
>
Download Intel Version Download Intel Version
<HardDriveDownload class="size-4" /> <HardDriveDownload class="size-4" />
</div> </div>
</div> </div>
</div> </div>
<div id="linux-target-download" class="form-item hidden"> <div id="linux-target-download" class="form-item hidden">
<div class="form-title"> <div class="form-title">Choose your Linux target system</div>
Choose your Linux target system
</div>
<div class="form-description"> <div class="form-description">
We have a few options for Linux, please select the one that fits your We have a few options for Linux, please select the one that fits
system. your system.
</div> </div>
<form class="flex flex-col gap-2 form-select"> <form class="form-select flex flex-col gap-2">
<input type="radio" name="linux-target" value="x86_64" id="linux-target-x86_64" class="hidden" checked/> <input
<label class="flex border-2 rounded-md p-2 w-full border-dark gap-2 px-4 items-center cursor-pointer" for="linux-target-x86_64"> type="radio"
name="linux-target"
value="x86_64"
id="linux-target-x86_64"
class="hidden"
checked
/>
<label
class="flex w-full cursor-pointer items-center gap-2 rounded-md border-2 border-dark p-2 px-4"
for="linux-target-x86_64"
>
<div>x86_64</div> <div>x86_64</div>
</label> </label>
<input type="radio" name="linux-target" value="aarch64" id="linux-target-aarch64" class="hidden" /> <input
<label class="flex border-2 rounded-md p-2 w-full border-dark gap-2 px-4 items-center cursor-pointer" for="linux-target-aarch64"> type="radio"
name="linux-target"
value="aarch64"
id="linux-target-aarch64"
class="hidden"
/>
<label
class="flex w-full cursor-pointer items-center gap-2 rounded-md border-2 border-dark p-2 px-4"
for="linux-target-aarch64"
>
<div>aarch64</div> <div>aarch64</div>
</label> </label>
</form> </form>
</div> </div>
<div id="form-linux-download" class="form-item hidden"> <div id="form-linux-download" class="form-item hidden">
<div class="form-title"> <div class="form-title">Download Zen for Linux!</div>
Download Zen for Linux!
</div>
<div class="form-description"> <div class="form-description">
You can choose between different download formats for Linux, select You can choose between different download formats for Linux,
the one that fits you best. select the one that fits you best.
</div> </div>
<div class="flex flex-col gap-4 form-select"> <div class="form-select flex flex-col gap-4">
<div class="border-2 p-2 px-4 border-dark text-dark rounded-md shadow-sm flex items-center cursor-pointer justify-between hover:bg-dark hover:text-paper transition-all duration-100" id="linux-tar-download"> <div
class="flex cursor-pointer items-center justify-between rounded-md border-2 border-dark p-2 px-4 text-dark shadow-sm transition-all duration-100 hover:bg-dark hover:text-paper"
id="linux-tar-download"
>
Download Tarball Download Tarball
<HardDriveDownload class="size-4" /> <HardDriveDownload class="size-4" />
</div> </div>
<div class="border-2 p-2 px-4 border-dark text-dark rounded-md shadow-sm flex items-center cursor-pointer justify-between hover:bg-dark hover:text-paper transition-all duration-100" id="linux-appimage-download"> <div
class="flex cursor-pointer items-center justify-between rounded-md border-2 border-dark p-2 px-4 text-dark shadow-sm transition-all duration-100 hover:bg-dark hover:text-paper"
id="linux-appimage-download"
>
Download AppImage Download AppImage
<HardDriveDownload class="size-4" /> <HardDriveDownload class="size-4" />
</div> </div>
<div class="border-2 p-2 px-4 border-dark text-dark rounded-md shadow-sm flex items-center cursor-pointer justify-between hover:bg-dark hover:text-paper transition-all duration-100" id="linux-flathub-download"> <div
class="flex cursor-pointer items-center justify-between rounded-md border-2 border-dark p-2 px-4 text-dark shadow-sm transition-all duration-100 hover:bg-dark hover:text-paper"
id="linux-flathub-download"
>
Download from Flathub Download from Flathub
<HardDriveDownload class="size-4" /> <HardDriveDownload class="size-4" />
</div> </div>
</div> </div>
</div> </div>
<div id="windows-target-download" class="form-item hidden"> <div id="windows-target-download" class="form-item hidden">
<div class="form-title"> <div class="form-title">Choose your Windows target system</div>
Choose your Windows target system
</div>
<div class="form-description"> <div class="form-description">
We have a few options for Windows, please select the one that fits your We have a few options for Windows, please select the one that fits
system. your system.
</div> </div>
<form class="flex flex-col gap-2 form-select"> <form class="form-select flex flex-col gap-2">
<input type="radio" name="windows-target" value="x86_64" id="windows-target-x86_64" class="hidden" checked/> <input
<label class="flex border-2 rounded-md p-2 w-full border-dark gap-2 px-4 items-center cursor-pointer" for="windows-target-x86_64"> type="radio"
name="windows-target"
value="x86_64"
id="windows-target-x86_64"
class="hidden"
checked
/>
<label
class="flex w-full cursor-pointer items-center gap-2 rounded-md border-2 border-dark p-2 px-4"
for="windows-target-x86_64"
>
<div>x86_64</div> <div>x86_64</div>
</label> </label>
<input type="radio" name="windows-target" value="arm64" id="windows-target-arm64" class="hidden" /> <input
<label class="flex border-2 rounded-md p-2 w-full border-dark gap-2 px-4 items-center cursor-pointer" for="windows-target-arm64"> type="radio"
name="windows-target"
value="arm64"
id="windows-target-arm64"
class="hidden"
/>
<label
class="flex w-full cursor-pointer items-center gap-2 rounded-md border-2 border-dark p-2 px-4"
for="windows-target-arm64"
>
<div>ARM64</div> <div>ARM64</div>
</label> </label>
</form> </form>
</div> </div>
<div id="windows-download" class="form-item hidden"> <div id="windows-download" class="form-item hidden">
<div class="form-title"> <div class="form-title">Download Zen for Windows!</div>
Download Zen for Windows!
</div>
<div class="form-description"> <div class="form-description">
You can choose between different download formats for Windows, select You can choose between different download formats for Windows,
the one that fits you best. select the one that fits you best.
</div> </div>
<div class="flex flex-col gap-4 form-select"> <div class="form-select flex flex-col gap-4">
<div class="border-2 p-2 px-4 border-dark text-dark rounded-md shadow-sm flex items-center cursor-pointer justify-between hover:bg-dark hover:text-paper transition-all duration-100" id="windows-installer-download"> <div
class="flex cursor-pointer items-center justify-between rounded-md border-2 border-dark p-2 px-4 text-dark shadow-sm transition-all duration-100 hover:bg-dark hover:text-paper"
id="windows-installer-download"
>
Download Installer Download Installer
<HardDriveDownload class="size-4" /> <HardDriveDownload class="size-4" />
</div> </div>
<div class="opacity-50 cursor-not-allowed border-2 p-2 px-4 border-dark text-dark rounded-md shadow-sm flex items-center cursor-pointer justify-between hover:bg-dark hover:text-paper transition-all duration-100" id="windows-zip-download"> <div
class="flex cursor-not-allowed cursor-pointer items-center justify-between rounded-md border-2 border-dark p-2 px-4 text-dark opacity-50 shadow-sm transition-all duration-100 hover:bg-dark hover:text-paper"
id="windows-zip-download"
>
Download Zip Download Zip
<HardDriveDownload class="size-4" /> <HardDriveDownload class="size-4" />
</div> </div>
@ -180,213 +263,260 @@ const appleIcon = icon({ prefix: "fab", iconName: "apple" });
<script> <script>
const releases = { const releases = {
macos: { macos: {
intel: "zen.macos-x86_64.dmg", intel: 'zen.macos-x86_64.dmg',
arm: "zen.macos-aarch64.dmg", arm: 'zen.macos-aarch64.dmg',
}, },
linux: { linux: {
x86_64: { x86_64: {
tar: "zen.linux-x86_64.tar.bz2", tar: 'zen.linux-x86_64.tar.bz2',
appImage: "zen-x86_64.AppImage", appImage: 'zen-x86_64.AppImage',
}, },
aarch64: { aarch64: {
tar: "zen.linux-aarch64.tar.bz2", tar: 'zen.linux-aarch64.tar.bz2',
appImage: "zen-aarch64.AppImage", appImage: 'zen-aarch64.AppImage',
}, },
}, },
windows: { windows: {
x86_64: { x86_64: {
installer: "zen.installer.exe", installer: 'zen.installer.exe',
zip: "zen.win-x86_64.zip", zip: 'zen.win-x86_64.zip',
}, },
arm64: { arm64: {
installer: "zen.installer-arm64.exe", installer: 'zen.installer-arm64.exe',
zip: "zen.win-arm64.zip", zip: 'zen.win-arm64.zip',
}, },
}, },
} as any; } as any
const BASE_URL = const BASE_URL =
"https://github.com/zen-browser/desktop/releases/latest/download"; 'https://github.com/zen-browser/desktop/releases/latest/download'
const TWILIGHT_BASE_URL = const TWILIGHT_BASE_URL =
"https://github.com/zen-browser/desktop/releases/download/twilight"; 'https://github.com/zen-browser/desktop/releases/download/twilight'
var selectedOS: string | null = null; var selectedOS: string | null = null
var isTwilight: boolean = false; var isTwilight: boolean = false
var selectedArch: string | null = null; var selectedArch: string | null = null
function showButton(buttonId: string, show: boolean) { function showButton(buttonId: string, show: boolean) {
const button = document.getElementById(buttonId) as HTMLButtonElement; const button = document.getElementById(buttonId) as HTMLButtonElement
if (show) { if (show) {
button?.classList.remove("hidden"); button?.classList.remove('hidden')
} else { } else {
button?.classList.add("hidden"); button?.classList.add('hidden')
} }
} }
function showThanksPage() { function showThanksPage() {
showButton("next-button", false); showButton('next-button', false)
showButton("back-button", false); showButton('back-button', false)
} }
function downloadRelease(os: string, arch: string, format: string = "") { function downloadRelease(os: string, arch: string, format: string = '') {
console.log("Downloading release", os, arch, format); console.log('Downloading release', os, arch, format)
let releaseName = releases[os][arch]; let releaseName = releases[os][arch]
if (os !== "macos") { if (os !== 'macos') {
releaseName = releases[os][arch][format]; releaseName = releases[os][arch][format]
} }
const url = `${isTwilight ? TWILIGHT_BASE_URL : BASE_URL}/${releaseName}`; const url = `${isTwilight ? TWILIGHT_BASE_URL : BASE_URL}/${releaseName}`
console.log("Downloading from", url); console.log('Downloading from', url)
window.open(url, "_blank"); window.open(url, '_blank')
showThanksPage(); showThanksPage()
} }
function goNextForm() { function goNextForm() {
if (selectedOS === null) { if (selectedOS === null) {
const osSelect = document.getElementById("os-select") as HTMLFormElement; const osSelect = document.getElementById('os-select') as HTMLFormElement
const selectedRadio = osSelect.querySelector("input[type='radio']:checked") as HTMLInputElement; const selectedRadio = osSelect.querySelector(
selectedOS = selectedRadio.value; "input[type='radio']:checked",
document.getElementById("form-os-select")?.classList.add("hidden"); ) as HTMLInputElement
selectedOS = selectedRadio.value
document.getElementById('form-os-select')?.classList.add('hidden')
if (selectedOS === "macos") { if (selectedOS === 'macos') {
const macosDownload = document.getElementById("form-macos-download") as HTMLDivElement; const macosDownload = document.getElementById(
macosDownload.classList.remove("hidden"); 'form-macos-download',
showButton("next-button", false); ) as HTMLDivElement
} else if (selectedOS === "linux") { macosDownload.classList.remove('hidden')
const linuxDownload = document.getElementById("linux-target-download") as HTMLDivElement; showButton('next-button', false)
linuxDownload.classList.remove("hidden"); } else if (selectedOS === 'linux') {
} else if (selectedOS === "windows") { const linuxDownload = document.getElementById(
const windowsDownload = document.getElementById("windows-target-download") as HTMLDivElement; 'linux-target-download',
windowsDownload.classList.remove("hidden"); ) as HTMLDivElement
linuxDownload.classList.remove('hidden')
} else if (selectedOS === 'windows') {
const windowsDownload = document.getElementById(
'windows-target-download',
) as HTMLDivElement
windowsDownload.classList.remove('hidden')
} }
showButton("back-button", true); // Show Back button after first step showButton('back-button', true) // Show Back button after first step
} else if (selectedOS === "linux" && selectedArch === null) { } else if (selectedOS === 'linux' && selectedArch === null) {
const linuxTargetSelect = document.getElementById("linux-target-download") as HTMLFormElement; const linuxTargetSelect = document.getElementById(
const selectedRadio = linuxTargetSelect.querySelector("input[type='radio']:checked") as HTMLInputElement; 'linux-target-download',
selectedArch = selectedRadio.value; ) as HTMLFormElement
document.getElementById("linux-target-download")?.classList.add("hidden"); const selectedRadio = linuxTargetSelect.querySelector(
"input[type='radio']:checked",
) as HTMLInputElement
selectedArch = selectedRadio.value
document.getElementById('linux-target-download')?.classList.add('hidden')
const linuxDownload = document.getElementById("form-linux-download") as HTMLDivElement; const linuxDownload = document.getElementById(
linuxDownload.classList.remove("hidden"); 'form-linux-download',
) as HTMLDivElement
linuxDownload.classList.remove('hidden')
showButton("next-button", false); showButton('next-button', false)
} else if (selectedOS === "windows" && selectedArch === null) { } else if (selectedOS === 'windows' && selectedArch === null) {
const windowsTargetSelect = document.getElementById("windows-target-download") as HTMLFormElement; const windowsTargetSelect = document.getElementById(
const selectedRadio = windowsTargetSelect.querySelector("input[type='radio']:checked") as HTMLInputElement; 'windows-target-download',
selectedArch = selectedRadio.value; ) as HTMLFormElement
document.getElementById("windows-target-download")?.classList.add("hidden"); const selectedRadio = windowsTargetSelect.querySelector(
"input[type='radio']:checked",
) as HTMLInputElement
selectedArch = selectedRadio.value
document
.getElementById('windows-target-download')
?.classList.add('hidden')
const windowsDownload = document.getElementById("windows-download") as HTMLDivElement; const windowsDownload = document.getElementById(
windowsDownload.classList.remove("hidden"); 'windows-download',
) as HTMLDivElement
windowsDownload.classList.remove('hidden')
showButton("next-button", false); showButton('next-button', false)
} else { } else {
throw new Error("Unknown state"); throw new Error('Unknown state')
} }
} }
function goPreviousForm() { function goPreviousForm() {
if (selectedArch) { if (selectedArch) {
// Go back to architecture selection // Go back to architecture selection
if (selectedOS === "linux") { if (selectedOS === 'linux') {
document.getElementById("form-linux-download")?.classList.add("hidden"); document.getElementById('form-linux-download')?.classList.add('hidden')
document.getElementById("linux-target-download")?.classList.remove("hidden"); document
} else if (selectedOS === "windows") { .getElementById('linux-target-download')
document.getElementById("windows-download")?.classList.add("hidden"); ?.classList.remove('hidden')
document.getElementById("windows-target-download")?.classList.remove("hidden"); } else if (selectedOS === 'windows') {
document.getElementById('windows-download')?.classList.add('hidden')
document
.getElementById('windows-target-download')
?.classList.remove('hidden')
} }
selectedArch = null; selectedArch = null
showButton("back-button", true); showButton('back-button', true)
showButton("next-button", true); showButton('next-button', true)
} else if (selectedOS) { } else if (selectedOS) {
// Go back to OS selection // Go back to OS selection
if (selectedOS === "macos") { if (selectedOS === 'macos') {
document.getElementById("form-macos-download")?.classList.add("hidden"); document.getElementById('form-macos-download')?.classList.add('hidden')
} else if (selectedOS === "linux") { } else if (selectedOS === 'linux') {
document.getElementById("linux-target-download")?.classList.add("hidden"); document
} else if (selectedOS === "windows") { .getElementById('linux-target-download')
document.getElementById("windows-target-download")?.classList.add("hidden"); ?.classList.add('hidden')
} else if (selectedOS === 'windows') {
document
.getElementById('windows-target-download')
?.classList.add('hidden')
} }
document.getElementById("form-os-select")?.classList.remove("hidden"); document.getElementById('form-os-select')?.classList.remove('hidden')
selectedOS = null; selectedOS = null
showButton("back-button", false); showButton('back-button', false)
showButton("next-button", true); showButton('next-button', true)
} }
} }
showButton("back-button", false); // Hide Back button on page load showButton('back-button', false) // Hide Back button on page load
showButton("next-button", true); // Ensure Next button is visible on load showButton('next-button', true) // Ensure Next button is visible on load
function filloutDefaultOS() { function filloutDefaultOS() {
const osSelect = document.getElementById("os-select") as HTMLFormElement; const osSelect = document.getElementById('os-select') as HTMLFormElement
const osSelectWindows = document.getElementById("os-select-windows") as HTMLInputElement; const osSelectWindows = document.getElementById(
const osSelectLinux = document.getElementById("os-select-linux") as HTMLInputElement; 'os-select-windows',
const osSelectMacOS = document.getElementById("os-select-macos") as HTMLInputElement; ) as HTMLInputElement
const osSelectLinux = document.getElementById(
'os-select-linux',
) as HTMLInputElement
const osSelectMacOS = document.getElementById(
'os-select-macos',
) as HTMLInputElement
if (navigator.platform.includes("Win")) { if (navigator.platform.includes('Win')) {
osSelectWindows.checked = true; osSelectWindows.checked = true
} else if (navigator.platform.includes("Linux")) { } else if (navigator.platform.includes('Linux')) {
osSelectLinux.checked = true; osSelectLinux.checked = true
} else if (navigator.platform.includes("Mac")) { } else if (navigator.platform.includes('Mac')) {
osSelectMacOS.checked = true; osSelectMacOS.checked = true
} }
} }
function getIfTwilight() { function getIfTwilight() {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search)
isTwilight = urlParams.has("twilight"); isTwilight = urlParams.has('twilight')
if (isTwilight) { if (isTwilight) {
console.log("Twilight mode enabled"); console.log('Twilight mode enabled')
const name = document.getElementById("zen-name"); const name = document.getElementById('zen-name')
const twilightInfo = document.getElementById("twilight-info"); const twilightInfo = document.getElementById('twilight-info')
if (name) if (name) name.innerHTML = 'Twilight'
name.innerHTML = "Twilight"; if (twilightInfo) twilightInfo.classList.remove('hidden')
if (twilightInfo)
twilightInfo.classList.remove("hidden");
} }
} }
getIfTwilight(); getIfTwilight()
document.getElementById("next-button")?.addEventListener("click", () => { document.getElementById('next-button')?.addEventListener('click', () => {
goNextForm(); goNextForm()
}); })
document.getElementById("back-button")?.addEventListener("click", () => { document.getElementById('back-button')?.addEventListener('click', () => {
goPreviousForm(); goPreviousForm()
}); })
document.getElementById("macos-arm-download")?.addEventListener("click", () => { document
downloadRelease("macos", "arm"); .getElementById('macos-arm-download')
}); ?.addEventListener('click', () => {
downloadRelease('macos', 'arm')
})
document.getElementById("macos-intel-download")?.addEventListener("click", () => { document
downloadRelease("macos", "intel"); .getElementById('macos-intel-download')
}); ?.addEventListener('click', () => {
downloadRelease('macos', 'intel')
})
document.getElementById("linux-tar-download")?.addEventListener("click", () => { document
downloadRelease("linux", selectedArch as string, "tar"); .getElementById('linux-tar-download')
}); ?.addEventListener('click', () => {
downloadRelease('linux', selectedArch as string, 'tar')
})
document.getElementById("linux-appimage-download")?.addEventListener("click", () => { document
downloadRelease("linux", selectedArch as string, "appImage"); .getElementById('linux-appimage-download')
}); ?.addEventListener('click', () => {
downloadRelease('linux', selectedArch as string, 'appImage')
})
document
.getElementById('linux-flathub-download')
?.addEventListener('click', () => {
window.open('https://flathub.org/apps/io.github.zen_browser.zen')
})
document.getElementById("linux-flathub-download")?.addEventListener("click", () => { document
window.open("https://flathub.org/apps/io.github.zen_browser.zen"); .getElementById('windows-installer-download')
}); ?.addEventListener('click', () => {
downloadRelease('windows', selectedArch as string, 'installer')
})
document.getElementById("windows-installer-download")?.addEventListener("click", () => { document
downloadRelease("windows", selectedArch as string, "installer"); .getElementById('windows-zip-download')
}); ?.addEventListener('click', () => {
document.getElementById("windows-zip-download")?.addEventListener("click", () => {
//downloadRelease("windows", selectedArch as string, "zip"); //downloadRelease("windows", selectedArch as string, "zip");
}); })
filloutDefaultOS(); filloutDefaultOS()
</script> </script>
<style is:global> <style is:global>
@keyframes fadeIn { @keyframes fadeIn {
@ -404,7 +534,6 @@ const appleIcon = icon({ prefix: "fab", iconName: "apple" });
} }
#download-form { #download-form {
} }
.form-item { .form-item {
@ -420,31 +549,31 @@ const appleIcon = icon({ prefix: "fab", iconName: "apple" });
} }
.form-item > *:not(.hidden, .form-select), .form-item > *:not(.hidden, .form-select),
.form-select > *:not(.hidden, input[type="radio"]) { .form-select > *:not(.hidden, input[type='radio']) {
@apply select-none; @apply select-none;
opacity: 0; opacity: 0;
animation: fadeIn 0.2s forwards; animation: fadeIn 0.2s forwards;
&:nth-of-type(1) { &:nth-of-type(1) {
animation-delay: calc(var(--animation-delay) + .1s) !important; animation-delay: calc(var(--animation-delay) + 0.1s) !important;
} }
&:nth-of-type(2) { &:nth-of-type(2) {
animation-delay: calc(var(--animation-delay) + .2s) !important; animation-delay: calc(var(--animation-delay) + 0.2s) !important;
} }
&:nth-of-type(3) { &:nth-of-type(3) {
animation-delay: calc(var(--animation-delay) + .3s) !important; animation-delay: calc(var(--animation-delay) + 0.3s) !important;
} }
} }
.form-title { .form-title {
@apply font-bold mb-1; @apply mb-1 font-bold;
} }
.form-description { .form-description {
@apply opacity-50 text-sm mb-4; @apply mb-4 text-sm opacity-50;
} }
form { form {
@ -455,7 +584,7 @@ const appleIcon = icon({ prefix: "fab", iconName: "apple" });
@apply bg-muted; @apply bg-muted;
} }
input[type="radio"]:checked + & { input[type='radio']:checked + & {
transform: translateX(5px); transform: translateX(5px);
@apply bg-dark text-paper; @apply bg-dark text-paper;
} }

View file

@ -1,9 +1,9 @@
import rss, { type RSSOptions } from '@astrojs/rss'; import rss, { type RSSOptions } from '@astrojs/rss'
import { releaseNotes } from '../release-notes'; import { releaseNotes } from '../release-notes'
import type { ReleaseNote } from '../release-notes'; import type { ReleaseNote } from '../release-notes'
/** The default number of entries to include in the RSS feed. */ /** The default number of entries to include in the RSS feed. */
const RSS_ENTRY_LIMIT = 20; const RSS_ENTRY_LIMIT = 20
/** /**
* Handles the GET request for the `feed.xml` endpoint. * Handles the GET request for the `feed.xml` endpoint.
@ -11,13 +11,14 @@ const RSS_ENTRY_LIMIT = 20;
*/ */
export function GET(context: any) { export function GET(context: any) {
// Just in case the release notes array is empty for whatever reason. // Just in case the release notes array is empty for whatever reason.
const latestDate = releaseNotes.length > 0 const latestDate =
releaseNotes.length > 0
? formatRssDate(releaseNotes[0].date as string) ? formatRssDate(releaseNotes[0].date as string)
: new Date(); : new Date()
const rssData: RSSOptions = { const rssData: RSSOptions = {
title: "Zen Browser Release Notes", title: 'Zen Browser Release Notes',
description: "Release Notes for the Zen Browser", description: 'Release Notes for the Zen Browser',
site: context.url, site: context.url,
items: [], items: [],
customData: ` customData: `
@ -30,8 +31,8 @@ export function GET(context: any) {
<title>Zen Browser</title> <title>Zen Browser</title>
<link>https://www.zen-browser.app</link> <link>https://www.zen-browser.app</link>
</image> </image>
` `,
}; }
for (const releaseNote of releaseNotes.slice(0, RSS_ENTRY_LIMIT)) { for (const releaseNote of releaseNotes.slice(0, RSS_ENTRY_LIMIT)) {
rssData.items.push({ rssData.items.push({
@ -40,10 +41,10 @@ export function GET(context: any) {
pubDate: formatRssDate(releaseNote.date as string), pubDate: formatRssDate(releaseNote.date as string),
description: releaseNote.extra, description: releaseNote.extra,
content: formatReleaseNote(releaseNote), content: formatReleaseNote(releaseNote),
}); })
} }
return rss(rssData); return rss(rssData)
} }
/** /**
@ -54,15 +55,15 @@ export function GET(context: any) {
* @returns The passed in date string as a Date object. * @returns The passed in date string as a Date object.
*/ */
function formatRssDate(dateStr: string) { function formatRssDate(dateStr: string) {
const splitDate = dateStr.split("/"); const splitDate = dateStr.split('/')
if (splitDate.length !== 3) { if (splitDate.length !== 3) {
throw new Error("Invalid date format"); throw new Error('Invalid date format')
} }
const day = Number(splitDate[0]); const day = Number(splitDate[0])
const month = Number(splitDate[1]) - 1; const month = Number(splitDate[1]) - 1
const year = Number(splitDate[2]); const year = Number(splitDate[2])
return new Date(year, month, day); return new Date(year, month, day)
} }
/** /**
@ -74,85 +75,99 @@ function formatReleaseNote(releaseNote: ReleaseNote) {
let content = `<p> let content = `<p>
If you encounter any issues, please report them on <a href="https://github.com/zen-browser/desktop/issues/">the issues page</a>. If you encounter any issues, please report them on <a href="https://github.com/zen-browser/desktop/issues/">the issues page</a>.
Thanks everyone for your feedback! Thanks everyone for your feedback!
</p>`; </p>`
if (releaseNote.image) { if (releaseNote.image) {
content += `<img src="https://cdn.jsdelivr.net/gh/zen-browser/www/public/releases/${releaseNote.version}.png" content += `<img src="https://cdn.jsdelivr.net/gh/zen-browser/www/public/releases/${releaseNote.version}.png"
alt="Release Image for version ${releaseNote.version}" alt="Release Image for version ${releaseNote.version}"
style="max-width: 30em; width: 100%; border-radius: 0.5rem;" style="max-width: 30em; width: 100%; border-radius: 0.5rem;"
/>`; />`
} }
if (releaseNote.extra) { if (releaseNote.extra) {
content += `<p>${releaseNote.extra.replace(/(\n)/g, "<br />")}</p>`; content += `<p>${releaseNote.extra.replace(/(\n)/g, '<br />')}</p>`
} }
content += addReleaseNoteSection("⚠️ Breaking changes", releaseNote.breakingChanges?.map(breakingChangeToReleaseNote)); content += addReleaseNoteSection(
content += addReleaseNoteSection("✓ Fixes", releaseNote.fixes?.map(fixToReleaseNote)); '⚠️ Breaking changes',
content += addReleaseNoteSection("🖌 Theme Changes", releaseNote.themeChanges) releaseNote.breakingChanges?.map(breakingChangeToReleaseNote),
content += addReleaseNoteSection("⭐ Features", releaseNote.features); )
content += addReleaseNoteSection(
'✓ Fixes',
releaseNote.fixes?.map(fixToReleaseNote),
)
content += addReleaseNoteSection('🖌 Theme Changes', releaseNote.themeChanges)
content += addReleaseNoteSection('⭐ Features', releaseNote.features)
return content; return content
} }
function addReleaseNoteSection(title: string, items?: string[]): string { function addReleaseNoteSection(title: string, items?: string[]): string {
if (!items) { if (!items) {
return ""; return ''
} }
let content = `<h2>${title}</h2>`; let content = `<h2>${title}</h2>`
content += `<ul>`; content += `<ul>`
for (const item of items) { for (const item of items) {
if (item && item.length > 0) { if (item && item.length > 0) {
content += `<li>${item}</li>`; content += `<li>${item}</li>`
} }
} }
content += `</ul>`; content += `</ul>`
return content; return content
} }
function fixToReleaseNote(fix?: Exclude<ReleaseNote['fixes'], undefined>[number]) { function fixToReleaseNote(
fix?: Exclude<ReleaseNote['fixes'], undefined>[number],
) {
if (typeof fix === 'string') { if (typeof fix === 'string') {
return fix; return fix
} }
if (!fix || !fix.description || fix.description.length === 0) { if (!fix || !fix.description || fix.description.length === 0) {
return ""; return ''
} }
let note = fix.description; let note = fix.description
if (fix.issue) { if (fix.issue) {
note += ` (<a href="https://github.com/zen-browser/desktop/issues/${fix.issue}" target="_blank">#${fix.issue}</a>)`; note += ` (<a href="https://github.com/zen-browser/desktop/issues/${fix.issue}" target="_blank">#${fix.issue}</a>)`
} }
return note; return note
} }
function breakingChangeToReleaseNote(breakingChange?: Exclude<ReleaseNote['breakingChanges'], undefined>[number]) { function breakingChangeToReleaseNote(
breakingChange?: Exclude<ReleaseNote['breakingChanges'], undefined>[number],
) {
if (typeof breakingChange === 'string') { if (typeof breakingChange === 'string') {
return breakingChange; return breakingChange
} }
if (!breakingChange || !breakingChange.description || breakingChange.description.length === 0) { if (
return ""; !breakingChange ||
!breakingChange.description ||
breakingChange.description.length === 0
) {
return ''
} }
return `${breakingChange.description} (<a href="${breakingChange.link}" target="_blank">Learn more</a>)`; return `${breakingChange.description} (<a href="${breakingChange.link}" target="_blank">Learn more</a>)`
} }
function pubDate(date?: Date) { function pubDate(date?: Date) {
date ??= new Date(); date ??= new Date()
const pieces = date.toString().split(' '); const pieces = date.toString().split(' ')
const offsetTime = pieces[5].match(/[-+]\d{4}/); const offsetTime = pieces[5].match(/[-+]\d{4}/)
const offset = (offsetTime) ? offsetTime : pieces[5]; const offset = offsetTime ? offsetTime : pieces[5]
const parts = [ const parts = [
pieces[0] + ',', pieces[0] + ',',
pieces[2], pieces[2],
pieces[1], pieces[1],
pieces[3], pieces[3],
pieces[4], pieces[4],
offset offset,
]; ]
return parts.join(' '); return parts.join(' ')
} }

View file

@ -28,5 +28,4 @@ import HomeExtras from '../components/HomeExtras.astro'
animation: headerSlideIn 0.5s ease-in-out; animation: headerSlideIn 0.5s ease-in-out;
} }
</style> </style>
<script> <script></script>
</script>

View file

@ -1,37 +1,37 @@
--- ---
import { getAbsoluteLocaleUrl } from 'astro:i18n'; import { getAbsoluteLocaleUrl } from 'astro:i18n'
import { getAllMods, getAuthorLink, getLocalizedDate } from '../../mods'; import { getAllMods, getAuthorLink, getLocalizedDate } from '../../mods'
import Layout from '../../layouts/Layout.astro'; import Layout from '../../layouts/Layout.astro'
import Title from '../../components/Title.astro'; import Title from '../../components/Title.astro'
import Description from '../../components/Description.astro'; import Description from '../../components/Description.astro'
import Button from '../../components/Button.astro'; import Button from '../../components/Button.astro'
import BackButton from '../../components/BackButton.astro'; import BackButton from '../../components/BackButton.astro'
import { ArrowRight, Info } from 'lucide-astro'; import { ArrowRight, Info } from 'lucide-astro'
export async function getStaticPaths() { export async function getStaticPaths() {
const mods = await getAllMods(); const mods = await getAllMods()
return mods.map((mod) => ({ return mods.map((mod) => ({
params: { slug: mod.id }, params: { slug: mod.id },
props: { ...mod }, props: { ...mod },
})); }))
} }
// https://github.com/TeaClientMC/Website/blob/7faacc9f8b2c79c74f711d413b155c84faafc00d/src/pages/news/%5B...slug%5D.astro // https://github.com/TeaClientMC/Website/blob/7faacc9f8b2c79c74f711d413b155c84faafc00d/src/pages/news/%5B...slug%5D.astro
const mod = Astro.props; const mod = Astro.props
const dates = { const dates = {
createdAt: getLocalizedDate(mod.createdAt), createdAt: getLocalizedDate(mod.createdAt),
updatedAt: getLocalizedDate(mod.updatedAt) updatedAt: getLocalizedDate(mod.updatedAt),
} }
--- ---
<Layout title={`${mod.name} - Zen Mods`}> <Layout title={`${mod.name} - Zen Mods`}>
<main class="mt-6 2xl:mt-0"> <main class="mt-6 2xl:mt-0">
<div class="px-8 mx-auto flex flex-col lg:w-1/2 gap-6 mt-12 lg:mt-32 mb-24"> <div class="mx-auto mb-24 mt-12 flex flex-col gap-6 px-8 lg:mt-32 lg:w-1/2">
<div <div
id="install-theme-error" id="install-theme-error"
class="flex flex-col md:flex-row items-center justify-center md:justify-between gap-2 rounded-xl bg-red-200 dark:bg-red-700 p-2 px-4" class="flex flex-col items-center justify-center gap-2 rounded-xl bg-red-200 p-2 px-4 md:flex-row md:justify-between dark:bg-red-700"
> >
<div class="flex items-center gap-2 text-center md:text-left"> <div class="flex items-center gap-2 text-center md:text-left">
<Info /> <Info />
@ -39,7 +39,11 @@ const dates = {
You need to have Zen Browser installed to install this theme.{' '} You need to have Zen Browser installed to install this theme.{' '}
</p> </p>
</div> </div>
<Button href="/download" class="inline-flex bg-red-300 !rounded-lg dark:bg-red-800 flex-shrink-0 whitespace-nowrap" isAlert> <Button
href="/download"
class="inline-flex flex-shrink-0 whitespace-nowrap !rounded-lg bg-red-300 dark:bg-red-800"
isAlert
>
Download now! Download now!
<ArrowRight class="size-4" /> <ArrowRight class="size-4" />
</Button> </Button>
@ -52,19 +56,29 @@ const dates = {
<img <img
src={mod.image} src={mod.image}
alt={mod.name} alt={mod.name}
class="h-full w-full object-cover rounded-xl border-2 shadow border-dark" class="h-full w-full rounded-xl border-2 border-dark object-cover shadow"
/> />
<div class="flex justify-between flex-col sm:flex-row gap-2"> <div class="flex flex-col justify-between gap-2 sm:flex-row">
<div class="font-normal flex-shrink-0"> <div class="flex-shrink-0 font-normal">
<p>Created by <a href={getAuthorLink(mod.author)} class="font-bold">@{mod.author}</a> • <span class="font-bold">v{mod.version}</span></p> <p>
Created by <a href={getAuthorLink(mod.author)} class="font-bold"
>@{mod.author}</a
> • <span class="font-bold">v{mod.version}</span>
</p>
<p>Creation date • <b>{dates.createdAt}</b></p> <p>Creation date • <b>{dates.createdAt}</b></p>
{(dates.createdAt !== dates.updatedAt) && <p>Latest update • <b>{dates.updatedAt}</b></p>} {
dates.createdAt !== dates.updatedAt && (
<p>
Latest update • <b>{dates.updatedAt}</b>
</p>
)
}
</div> </div>
<div class="flex flex-col sm:items-end"> <div class="flex flex-col sm:items-end">
<Button <Button
class="hidden" class="hidden"
id="install-theme" id="install-theme"
extra={{"zen-theme-id":mod.id}} extra={{ 'zen-theme-id': mod.id }}
isPrimary isPrimary
> >
Install Theme 🎉 Install Theme 🎉
@ -72,7 +86,7 @@ const dates = {
<Button <Button
class="hidden" class="hidden"
id="install-theme-uninstall" id="install-theme-uninstall"
extra={{"zen-theme-id":mod.id}} extra={{ 'zen-theme-id': mod.id }}
isPrimary isPrimary
> >
Uninstall Theme Uninstall Theme
@ -83,6 +97,6 @@ const dates = {
<div class="h-[1px] opacity-80 bg-dark w-full"></div> <div class="h-[1px] opacity-80 bg-dark w-full"></div>
--> -->
</div> </div>
<div> <div></div>
</main> </main></Layout
</Layout> >

View file

@ -1,91 +1,241 @@
--- ---
import Title from "../components/Title.astro"; import Title from '../components/Title.astro'
import Layout from "../layouts/Layout.astro"; import Layout from '../layouts/Layout.astro'
--- ---
<Layout title="Privacy Policy - Zen"> <Layout title="Privacy Policy - Zen">
<main class="w-1/2 mx-auto mt-52 pb-24"> <main class="mx-auto mt-52 w-1/2 pb-24">
<Title id="privacy-policy">Privacy Policy</Title> <Title id="privacy-policy">Privacy Policy</Title>
<div class="ml-4 font-bold"> <div class="ml-4 font-bold">Last updated: 2024-08-12</div>
Last updated: 2024-08-12 <Title class="mt-16 !text-4xl font-bold" id="introduction"
</div> >Introduction</Title
<Title class="!text-4xl font-bold mt-16" id="introduction">Introduction</Title> >
<p>Welcome to Zen Browser! Your privacy is our priority. This Privacy Policy outlines the types of personal information we collect, how we use it, and the steps we take to protect your data when you use Zen Browser.</p> <p>
<div class="my-12 mx-12 flex gap-4 font-bold"> Welcome to Zen Browser! Your privacy is our priority. This Privacy Policy
outlines the types of personal information we collect, how we use it, and
the steps we take to protect your data when you use Zen Browser.
</p>
<div class="mx-12 my-12 flex gap-4 font-bold">
We don't sell data - We don't collect data - We don't track you We don't sell data - We don't collect data - We don't track you
</div> </div>
<Title class="!text-4xl font-bold mt-16" id="1-information-we-do-not-collect">1. Information We Do Not Collect</Title> <Title
<p>Zen Browser is designed with privacy in mind. We do not collect, store, or share any of your personal data. Heres what that means:</p> class="mt-16 !text-4xl font-bold"
id="1-information-we-do-not-collect"
>1. Information We Do Not Collect</Title
>
<p>
Zen Browser is designed with privacy in mind. We do not collect, store, or
share any of your personal data. Heres what that means:
</p>
<ul> <ul>
<li>Crash reports can be sent to Mozilla Firefox. But, we do not collect any crash reports. Crash reports are sent securely to Mozilla Firefox to help improve the stability of the browser. They do not contain any personal information.</li> <li>
Crash reports can be sent to Mozilla Firefox. But, we do not collect any
crash reports. Crash reports are sent securely to Mozilla Firefox to
help improve the stability of the browser. They do not contain any
personal information.
</li>
</ul> </ul>
<h3 class="!font-bold text-xl mt-4" id="-1-1-no-telemetry-"><strong class="font-bold">1.1. No Telemetry</strong></h3> <h3 class="mt-4 text-xl !font-bold" id="-1-1-no-telemetry-">
<strong class="font-bold">1.1. No Telemetry</strong>
</h3>
<p>We do not collect any telemetry data.</p> <p>We do not collect any telemetry data.</p>
<p>However, you can opt-in to share telemetry data to Mozilla for the improvement of FireFox (the base upon which the Zen Browser is built). It will be treated in accordance with their Privacy Policy which you can read about <a href="https://www.mozilla.org/en-US/privacy/">here</a>.</p> <p>
<h3 class="!font-bold text-xl mt-4" id="-1-2-no-personal-data-collection-"><strong class="font-bold">1.2. No Personal Data Collection</strong></h3> However, you can opt-in to share telemetry data to Mozilla for the
<p>Zen Browser does not collect any personal information such as your IP address, browsing history, search queries, or form data.</p> improvement of FireFox (the base upon which the Zen Browser is built). It
<h3 class="!font-bold text-xl mt-4" id="-1-3-no-third-party-tracking-"><strong class="font-bold">1.3. No Third-Party Tracking</strong></h3> will be treated in accordance with their Privacy Policy which you can read
<p>We do not allow third-party trackers or analytics tools to operate within Zen Browser. Your browsing activity remains entirely private and is not shared with any third party. Mozilla is not considered a third party as it is the base of Zen Browser.</p> about <a href="https://www.mozilla.org/en-US/privacy/">here</a>.
<Title class="!text-4xl font-bold mt-16" id="2-information-stored-locally-on-your-device">2. Information Stored Locally on Your Device</Title> </p>
<h3 class="!font-bold text-xl mt-4" id="-2-1-browsing-data-"><strong class="font-bold">2.1. Browsing Data</strong></h3> <h3 class="mt-4 text-xl !font-bold" id="-1-2-no-personal-data-collection-">
<p>Zen Browser stores certain data locally on your device to enhance your browsing experience. This includes:</p> <strong class="font-bold">1.2. No Personal Data Collection</strong>
</h3>
<p>
Zen Browser does not collect any personal information such as your IP
address, browsing history, search queries, or form data.
</p>
<h3 class="mt-4 text-xl !font-bold" id="-1-3-no-third-party-tracking-">
<strong class="font-bold">1.3. No Third-Party Tracking</strong>
</h3>
<p>
We do not allow third-party trackers or analytics tools to operate within
Zen Browser. Your browsing activity remains entirely private and is not
shared with any third party. Mozilla is not considered a third party as it
is the base of Zen Browser.
</p>
<Title
class="mt-16 !text-4xl font-bold"
id="2-information-stored-locally-on-your-device"
>2. Information Stored Locally on Your Device</Title
>
<h3 class="mt-4 text-xl !font-bold" id="-2-1-browsing-data-">
<strong class="font-bold">2.1. Browsing Data</strong>
</h3>
<p>
Zen Browser stores certain data locally on your device to enhance your
browsing experience. This includes:
</p>
<ul> <ul>
<li><strong class="font-bold">Cookies</strong>: Cookies are stored locally on your device and are not shared with Zen Browser or any third party. You have full control over the management of cookies through the browsers settings.</li> <li>
<li><strong class="font-bold">Cache and Temporary Files</strong>: Zen Browser may store cache files and other temporary data locally to improve performance. These files can be cleared at any time through the browsers settings.</li> <strong class="font-bold">Cookies</strong>: Cookies are stored locally
on your device and are not shared with Zen Browser or any third party.
You have full control over the management of cookies through the
browsers settings.
</li>
<li>
<strong class="font-bold">Cache and Temporary Files</strong>: Zen
Browser may store cache files and other temporary data locally to
improve performance. These files can be cleared at any time through the
browsers settings.
</li>
</ul> </ul>
<h3 class="!font-bold text-xl mt-4" id="-2-2-settings-and-preferences-"><strong class="font-bold">2.2. Settings and Preferences</strong></h3> <h3 class="mt-4 text-xl !font-bold" id="-2-2-settings-and-preferences-">
<p>Any customizations, settings, and preferences you make within Zen Browser are stored locally on your device. We do not have access to or control over this data.</p> <strong class="font-bold">2.2. Settings and Preferences</strong>
<Title class="!text-4xl font-bold mt-16" id="3-sync-feature">3. Sync Feature</Title> </h3>
<p>Zen Browser offers a &quot;Sync&quot; feature, which is implemented using Mozilla Firefox&#39;s Sync feature. This feature allows you to synchronize your bookmarks, history, passwords, and other data across multiple devices. For this feature to work, your data is encrypted and stored on Mozillas servers and is treated in accordance with their Privacy Policy. We, at Zen, cannot view any of this data.</p> <p>
Any customizations, settings, and preferences you make within Zen Browser
are stored locally on your device. We do not have access to or control
over this data.
</p>
<Title class="mt-16 !text-4xl font-bold" id="3-sync-feature"
>3. Sync Feature</Title
>
<p>
Zen Browser offers a &quot;Sync&quot; feature, which is implemented using
Mozilla Firefox&#39;s Sync feature. This feature allows you to synchronize
your bookmarks, history, passwords, and other data across multiple
devices. For this feature to work, your data is encrypted and stored on
Mozillas servers and is treated in accordance with their Privacy Policy.
We, at Zen, cannot view any of this data.
</p>
<ul> <ul>
<li><a href="https://www.mozilla.org/en-US/privacy/mozilla-accounts/">Mozilla Firefox Sync</a></li> <li>
<li><a href="https://support.mozilla.org/en-US/kb/how-firefox-securely-saves-passwords#:~:text=Firefox%20Desktop%20encrypts%20your%20passwords,cryptography%20to%20obscure%20your%20passwords.">This is how we store your passwords</a></li> <a href="https://www.mozilla.org/en-US/privacy/mozilla-accounts/"
>Mozilla Firefox Sync</a>
</li>
<li>
<a
href="https://support.mozilla.org/en-US/kb/how-firefox-securely-saves-passwords#:~:text=Firefox%20Desktop%20encrypts%20your%20passwords,cryptography%20to%20obscure%20your%20passwords."
>This is how we store your passwords</a>
</li>
</ul> </ul>
<Title class="!text-4xl font-bold mt-16" id="4-data-security">4. Data Security</Title> <Title class="mt-16 !text-4xl font-bold" id="4-data-security"
<p>Although Zen Browser does not collect your data, we are committed to protecting the information that is stored locally on your device and, if you use the Sync feature, the encrypted data stored on Mozilla&#39;s servers. We recommend that you use secure passwords, enable device encryption, and regularly update your software to ensure your data remains safe.</p> >4. Data Security</Title
>
<p>
Although Zen Browser does not collect your data, we are committed to
protecting the information that is stored locally on your device and, if
you use the Sync feature, the encrypted data stored on Mozilla&#39;s
servers. We recommend that you use secure passwords, enable device
encryption, and regularly update your software to ensure your data remains
safe.
</p>
<ul> <ul>
<li>Note that most of the security measures are taken care by Mozilla Firefox.</li> <li>
Note that most of the security measures are taken care by Mozilla
Firefox.
</li>
</ul> </ul>
<Title class="!text-4xl font-bold mt-16" id="5-your-control">5. Your Control</Title> <Title class="mt-16 !text-4xl font-bold" id="5-your-control"
<h3 class="!font-bold text-xl mt-4" id="-5-1-data-deletion-"><strong class="font-bold">5.1. Data Deletion</strong></h3> >5. Your Control</Title
<p>You have full control over all data stored locally on your device by Zen Browser. You can clear your browsing data, cookies, and cache at any time using the browsers settings.</p> >
<h3 class="!font-bold text-xl mt-4" id="-5-2-do-not-track-"><strong class="font-bold">5.2. Do Not Track</strong></h3> <h3 class="mt-4 text-xl !font-bold" id="-5-1-data-deletion-">
<p>Zen Browser automatically honors &quot;Do Not Track&quot; requests by default. We ensure that no tracking of your activity occurs, in compliance with this setting.</p> <strong class="font-bold">5.1. Data Deletion</strong>
<Title class="!text-4xl font-bold mt-16" id="6-our-website-and-services">6. Our Website and Services</Title> </h3>
<p>When you click on the &quot;Download&quot; button on our website, a number in the database is incremented to track the number of downloads. This is done to understand the popularity of the browser. No personal data is collected during the process.</p> <p>
<h3 class="!font-bold text-xl mt-4" id="-6-1-external-links-"><strong class="font-bold">6.1. External links</strong></h3> You have full control over all data stored locally on your device by Zen
<p>Zen Browser may contain links to external websites or services that are not owned or operated by us. We are not responsible for the content or privacy practices of these sites. We recommend that you review the privacy policies of these sites before providing them with any personal information.</p> Browser. You can clear your browsing data, cookies, and cache at any time
<Title class="!text-4xl font-bold mt-16" id="7-changes-to-this-privacy-policy">7. Changes to This Privacy Policy</Title> using the browsers settings.
<p>We may update this Privacy Policy from time to time to reflect changes in our practices or legal requirements. We will notify you of any significant changes by updating the effective date at the top of this policy. Continued use of Zen Browser after such changes constitutes your acceptance of the new terms.</p> </p>
<Title class="!text-4xl font-bold mt-16" id="8-other-telemetry-done-by-mozilla-firefox">8. Other telemetry done by Mozilla Firefox</Title> <h3 class="mt-4 text-xl !font-bold" id="-5-2-do-not-track-">
<p>We try to disable all telemetry data collection in Zen Browser. But, we may have missed some. Check the below links for more information.</p> <strong class="font-bold">5.2. Do Not Track</strong>
<p>You can also optionally enable telemetry data collection and other Mozilla Research Studies in Zen Browser. This is disabled by default. You can enable it by going to the settings page.</p> </h3>
<p>
Zen Browser automatically honors &quot;Do Not Track&quot; requests by
default. We ensure that no tracking of your activity occurs, in compliance
with this setting.
</p>
<Title class="mt-16 !text-4xl font-bold" id="6-our-website-and-services"
>6. Our Website and Services</Title
>
<p>
When you click on the &quot;Download&quot; button on our website, a number
in the database is incremented to track the number of downloads. This is
done to understand the popularity of the browser. No personal data is
collected during the process.
</p>
<h3 class="mt-4 text-xl !font-bold" id="-6-1-external-links-">
<strong class="font-bold">6.1. External links</strong>
</h3>
<p>
Zen Browser may contain links to external websites or services that are
not owned or operated by us. We are not responsible for the content or
privacy practices of these sites. We recommend that you review the privacy
policies of these sites before providing them with any personal
information.
</p>
<Title
class="mt-16 !text-4xl font-bold"
id="7-changes-to-this-privacy-policy"
>7. Changes to This Privacy Policy</Title
>
<p>
We may update this Privacy Policy from time to time to reflect changes in
our practices or legal requirements. We will notify you of any significant
changes by updating the effective date at the top of this policy.
Continued use of Zen Browser after such changes constitutes your
acceptance of the new terms.
</p>
<Title
class="mt-16 !text-4xl font-bold"
id="8-other-telemetry-done-by-mozilla-firefox"
>8. Other telemetry done by Mozilla Firefox</Title
>
<p>
We try to disable all telemetry data collection in Zen Browser. But, we
may have missed some. Check the below links for more information.
</p>
<p>
You can also optionally enable telemetry data collection and other Mozilla
Research Studies in Zen Browser. This is disabled by default. You can
enable it by going to the settings page.
</p>
<ul> <ul>
<li>Please check <a href="https://www.mozilla.org/en-US/privacy/">Firefox Privacy Notice</a> for more information.</li> <li>
Please check <a href="https://www.mozilla.org/en-US/privacy/"
>Firefox Privacy Notice</a
> for more information.
</li>
</ul> </ul>
<Title class="!text-4xl font-bold mt-16" id="9-contact-us">9. Contact Us</Title> <Title class="mt-16 !text-4xl font-bold" id="9-contact-us"
<p>If you have any questions or concerns about this Privacy Policy or Zen Browser, please contact us at:</p> >9. Contact Us</Title
>
<p>
If you have any questions or concerns about this Privacy Policy or Zen
Browser, please contact us at:
</p>
<ul> <ul>
<li>Discord: <a href="https://discord.gg/zen-browser">Zen Browser&#39;s Discord</a></li> <li>
Discord: <a href="https://discord.gg/zen-browser"
>Zen Browser&#39;s Discord</a>
</li>
<li>GitHub: <a href="https://github.com/zen-browser">Organization</a></li> <li>GitHub: <a href="https://github.com/zen-browser">Organization</a></li>
</ul> </ul>
</main> </main>
</Layout> </Layout>
<style> <style>
p, li { p,
@apply opacity-55 li {
@apply opacity-55;
} }
ul { ul {
@apply mt-4 @apply mt-4;
} }
li { li {
@apply list-disc ml-4 @apply ml-4 list-disc;
} }
a { a {
@apply text-coral @apply text-coral;
} }
</style> </style>

View file

@ -1,17 +1,20 @@
--- ---
import { releaseNotes } from '../../release-notes' import { releaseNotes } from '../../release-notes'
import Layout from '../../layouts/Layout.astro'; import Layout from '../../layouts/Layout.astro'
export async function getStaticPaths() { export async function getStaticPaths() {
return [...releaseNotes.map((release: any) => ({ return [
...releaseNotes.map((release: any) => ({
params: { slug: release.version }, params: { slug: release.version },
props: { ...release }, props: { ...release },
})), { })),
{
params: { slug: 'latest' }, params: { slug: 'latest' },
props: { ...releaseNotes[0] }, props: { ...releaseNotes[0] },
}]; },
]
} }
const release = Astro.props; const release = Astro.props
return Astro.redirect(`/release-notes#${release.version}`); return Astro.redirect(`/release-notes#${release.version}`)
--- ---

View file

@ -13,7 +13,7 @@ import Title from '../../components/Title.astro'
class="py-42 flex min-h-screen flex-col justify-center px-10 lg:px-10 xl:px-10 2xl:w-3/5" class="py-42 flex min-h-screen flex-col justify-center px-10 lg:px-10 xl:px-10 2xl:w-3/5"
> >
<Title class="mt-48 text-4xl font-bold">Release Notes</Title> <Title class="mt-48 text-4xl font-bold">Release Notes</Title>
<p class="opacity-55 text-base"> <p class="text-base opacity-55">
Stay up to date with the latest changes to Zen Browser! Since the <a Stay up to date with the latest changes to Zen Browser! Since the <a
class="text-coral" class="text-coral"
href="#1.0.0-a.1">first release</a href="#1.0.0-a.1">first release</a
@ -24,7 +24,11 @@ import Title from '../../components/Title.astro'
>, we've been working hard to make Zen Browser the best it can be. >, we've been working hard to make Zen Browser the best it can be.
Thanks everyone for your feedback! ❤️ Thanks everyone for your feedback! ❤️
</p> </p>
{
(releaseNotesTwilight.features || releaseNotesTwilight.fixes) && (
<ReleaseNoteItem {...releaseNotesTwilight} isTwilight /> <ReleaseNoteItem {...releaseNotesTwilight} isTwilight />
)
}
{releaseNotes.map((notes: any) => <ReleaseNoteItem {...notes} />)} {releaseNotes.map((notes: any) => <ReleaseNoteItem {...notes} />)}
</div> </div>
</main> </main>

View file

@ -1,27 +1,27 @@
import releaseNotesStable from './release-notes/stable.json'; import releaseNotesStable from './release-notes/stable.json'
interface FixWithIssue { interface FixWithIssue {
description: string; description: string
issue?: number; issue?: number
} }
type Fix = string | FixWithIssue; type Fix = string | FixWithIssue
export type BreakingChange = string | { description: string; link: string }; export type BreakingChange = string | { description: string; link: string }
export interface ReleaseNote { export interface ReleaseNote {
version: string; version: string
date?: string; // optional for twilight date?: string // optional for twilight
extra?: string; extra?: string
image?: boolean; image?: boolean
fixes?: Fix[]; fixes?: Fix[]
features?: string[]; features?: string[]
breakingChanges?: BreakingChange[]; breakingChanges?: BreakingChange[]
themeChanges?: string[]; themeChanges?: string[]
inProgress?: boolean; inProgress?: boolean
workflowId?: number; workflowId?: number
isTwilight?: boolean; isTwilight?: boolean
} }
export const releaseNotes: ReleaseNote[] = releaseNotesStable.reverse(); export const releaseNotes: ReleaseNote[] = releaseNotesStable.reverse()
export {default as releaseNotesTwilight} from './release-notes/twilight.json'; export { default as releaseNotesTwilight } from './release-notes/twilight.json'

View file

@ -363,9 +363,7 @@
"version": "1.0.0-a.13", "version": "1.0.0-a.13",
"date": "05/08/2024", "date": "05/08/2024",
"extra": "This is a smaller release to fix some bugs and improve some small details.\n\nIm going to try doing more frequent releases from now on, see how it goes.", "extra": "This is a smaller release to fix some bugs and improve some small details.\n\nIm going to try doing more frequent releases from now on, see how it goes.",
"features": [ "features": ["Allow to remember sidebar width even after collapsing it."],
"Allow to remember sidebar width even after collapsing it."
],
"fixes": [ "fixes": [
{ {
"description": "Task Manager Icon Missing in Flatpak Version", "description": "Task Manager Icon Missing in Flatpak Version",
@ -733,11 +731,11 @@
"issue": 972 "issue": 972
}, },
{ {
"description": "Zen Browser.app is damaged and cant be opened on macOS", "description": "Zen Browser.app is damaged and can\u2019t be opened on macOS",
"issue": 1245 "issue": 1245
}, },
{ {
"description": "Fixed issue with keyboard shortcuts on macOS", "description": "Fixed issue with \u2318 keyboard shortcuts on macOS",
"issue": 376 "issue": 376
} }
] ]
@ -921,9 +919,7 @@
"image": true, "image": true,
"workflowId": 11000317603, "workflowId": 11000317603,
"extra": "This update addresses some significant issues with the previous release.\n\nWe appreciate your patience and support!", "extra": "This update addresses some significant issues with the previous release.\n\nWe appreciate your patience and support!",
"features": [ "features": ["Added a new system for handling keyboard shortcuts"],
"Added a new system for handling keyboard shortcuts"
],
"fixes": [ "fixes": [
{ {
"description": "The New Tab button is not visible", "description": "The New Tab button is not visible",
@ -980,9 +976,7 @@
"Enabled container tabs by default", "Enabled container tabs by default",
"Improved Expand Tabs on Hover layout" "Improved Expand Tabs on Hover layout"
], ],
"themeChanges": [ "themeChanges": ["Toggle inputs will not use the themed tertiary color"],
"Toggle inputs will not use the themed tertiary color"
],
"breakingChanges": [ "breakingChanges": [
"The keyboard shortcuts will be overriden by the defaults ones in this update" "The keyboard shortcuts will be overriden by the defaults ones in this update"
], ],
@ -1279,9 +1273,7 @@
"issue": 2156 "issue": 2156
} }
], ],
"breakingChanges": [ "breakingChanges": ["Removed Show Expand Button option from settings"],
"Removed Show Expand Button option from settings"
],
"themeChanges": [ "themeChanges": [
"The variable '--zen-main-browser-background' will now contain the generated gradient", "The variable '--zen-main-browser-background' will now contain the generated gradient",
"Added the 'unread' attribute for background tabs that haven't been accessed yet" "Added the 'unread' attribute for background tabs that haven't been accessed yet"
@ -1384,9 +1376,7 @@
"description": "Fixed wrong aligment on glance action buttons" "description": "Fixed wrong aligment on glance action buttons"
} }
], ],
"features": [ "features": ["No new features, sorry"]
"No new features, sorry"
]
}, },
{ {
"version": "1.0.1-a.17", "version": "1.0.1-a.17",
@ -1883,5 +1873,134 @@
"breakingChanges": [ "breakingChanges": [
"Disabled firefox's login manager by default, because safer alternatives are available. Can be enabled again in the preferences" "Disabled firefox's login manager by default, because safer alternatives are available. Can be enabled again in the preferences"
] ]
},
{
"version": "1.6t",
"extra": "This new release includes more polishing changes and bug fixes. We are most excited about our new release schedule, we are now using Firefox's Release Candidates for twilight, meaning we can test out new Firefox versions before they are released to the public. This will allow us to fix any issues before they are released to the public.\n\nIt may be a bit late, but happy new year! We hope you have a great year ahead of you!",
"fixes": [
"Fixed compact mode sidebar not hiding fully on some occasions",
"Fixed context menu checkbox icon aligments for Windows users",
"Fixed having multiple windows displaying the wrong essentials at startup",
{
"description": "Fixed toolbar jittering when in compact mode",
"issue": 3916
},
{
"description": "Fixed grain texture not being clipped at window corners",
"issue": 2942
},
{
"description": "Fixed arrow keys not working after opening an extension popup",
"issue": 2743
},
"Fixed wrong window buttons when opening customize toolbar in compact mode",
{
"description": "Fixed animation for opening popups cliping their content",
"issue": 4050
},
{
"description": "Fixed weird colouring for toggle inputs",
"issue": 4047
},
"Update application identifier for Zen Browser to align with new naming conventions (MacOS)",
"Fixed translations trying to be overriden on Windows builds",
{
"description": "Fixed web panels closing and inmediately opening again when clicking on the toolbar button",
"issue": 4049
},
"Fixed issues when building PGO, leading to bad optimization techniques",
"Fixed empty separations not appearing at the top toolbar",
"Fixed the spacing between the titlebar window buttons and toolbar buttons in MacOS",
"Fixed support for scrolling vertical tabs when opening a background tab",
{
"description": "Fixed mod default preferences not displayed correctly until reopening the settings page or browser restart",
"issue": 3887
},
{
"description": "Fixed changing mod string preferences sometimes results in missing characters",
"issue": 3558
},
{
"description": "Fixed on-boarding search engine icons having large sizes",
"issue": 3575
},
{
"description": "Fixed preferences sidebar hidding on smaller screens",
"issue": 4051
},
"Fixed coloring issue on tree elements such as search engine selection on the preferences page",
"Fixed pinning and unpinning extensions messing up the toolbar",
{
"description": "Fixed duplicating a tab showing the default workspace container indicator",
"issue": 4132
},
{
"description": "Fixed common issues on the on-boarding pages such as button backgrounds and spacing",
"issue": 4035
},
"Fixed visibility issues when selecting multiple essentials",
"Fixed syncing workspaces on some specific scenarios",
"Fixed glance buttons not being able to be clicked",
"Fixed horizontal spacing issues when using split views",
{
"description": "Fixed naming issues between sidebar and web panels",
"issue": 4043
},
"Fixed moving pinned tabs into the essential tabs container breaking the UI",
"Improved security for Zen Mods, only allowing installs from the Zen Mods website",
"Fixed the completly broken save bookmark dialog",
"Fixed and improved the grain texture for workspace backgrounds (Thanks to @different55)",
{
"description": "Fixed touch security not working on macos",
"issue": 437
},
"Fixed zen's app bundle ID giving an incorrect identifier",
"Fixed issue related to changing workspace with the 4 / 5 mouse buttons and we now take into consideration natural scrolling",
"Fixed some website dialogs overflowing the window",
"Fixed moving around pins not saving their position after a restart",
{
"description": "Fixed extension Pin to Toolbar overlayed with gtk theme toggle",
"issue": 1934
},
{
"description": "Fixed opening home page instead of newtab when closing the last workspace tab",
"issue": 3201
},
"Fixed essential icon not appearing on the context menu",
"Fixed accessibility issues on some native links",
"Fixed compact mode not closing when opening an extension dialog pinned to the sidebar",
"Fixed windows 11 titlebar looking weird when having a custom accent color",
"Fixed workspaces icons appearing on private mode once opening another window",
{
"description": "Fixed having invalid shortcuts hard-locking the user inside the loading screen",
"issue": 4071
},
"Fixed weird UI when dragging essentials around, making them look like they are clipping out of the view",
"Fixed compact mode sidebar flashing for some seconds when opening a glance window"
],
"features": [
"Updated to firefox 134.0",
"Added transitioning when changing between workspaces backgrounds",
"Browser background now goes grayscale when unfocused",
"Added support for Windows 11 setting 'Show accent color on window title bars and borders'",
"Enabled experimental features for twilight builds",
"Improved design of native zen dialogs",
"Re-enabled the default password manager",
"Improved on-boarding experience for new users",
"Added support for searching for emojis on the workspace icon picker",
"Added support for MacOS smart cards",
"Installed mods will now be correctly ordered in the settings page",
"Builds will now contain full LTO, improving performance",
"Color picker dialog will now convert to a valid color if it's hex-like but invalid",
"Made workspace switching tab's animation smoother",
"Added developer tools shortcuts support!"
],
"breakingChanges": [
"Changed versioning scheme to match Firefox's Release Candidates",
"Removed option to disable workspaces. Workspaces are now a core feature of Zen, and it can be really easy to ignore them (they are hidden if you have just one workspace) if you don't want to use them. Most of Zen's functionality is built around workspaces, so we decided to make them a core feature. They were an option before because they where experimental."
],
"workflowId": 12660919692,
"image": false,
"date": "07/01/2025"
} }
] ]

View file

@ -1,125 +1,7 @@
{ {
"version": "1.6t", "version": "xxx",
"extra": "This new release includes more polishing changes and bug fixes. We are most excited about our new release schedule, we are now using Firefox's Release Candidates for twilight, meaning we can test out new Firefox versions before they are released to the public. This will allow us to fix any issues before they are released to the public.\n\nIt may be a bit late, but happy new year! We hope you have a great year ahead of you!", "image": false,
"fixes": [ "extra": "",
"Fixed compact mode sidebar not hiding fully on some occasions", "fixes": [],
"Fixed context menu checkbox icon aligments for Windows users", "features": []
"Fixed having multiple windows displaying the wrong essentials at startup",
{
"description": "Fixed toolbar jittering when in compact mode",
"issue": 3916
},
{
"description": "Fixed grain texture not being clipped at window corners",
"issue": 2942
},
{
"description": "Fixed arrow keys not working after opening an extension popup",
"issue": 2743
},
"Fixed wrong window buttons when opening customize toolbar in compact mode",
{
"description": "Fixed animation for opening popups cliping their content",
"issue": 4050
},
{
"description": "Fixed weird colouring for toggle inputs",
"issue": 4047
},
"Update application identifier for Zen Browser to align with new naming conventions (MacOS)",
"Fixed translations trying to be overriden on Windows builds",
{
"description": "Fixed web panels closing and inmediately opening again when clicking on the toolbar button",
"issue": 4049
},
"Fixed issues when building PGO, leading to bad optimization techniques",
"Fixed empty separations not appearing at the top toolbar",
"Fixed the spacing between the titlebar window buttons and toolbar buttons in MacOS",
"Fixed support for scrolling vertical tabs when opening a background tab",
{
"description": "Fixed mod default preferences not displayed correctly until reopening the settings page or browser restart",
"issue": 3887
},
{
"description": "Fixed changing mod string preferences sometimes results in missing characters",
"issue": 3558
},
{
"description": "Fixed on-boarding search engine icons having large sizes",
"issue": 3575
},
{
"description": "Fixed preferences sidebar hidding on smaller screens",
"issue": 4051
},
"Fixed coloring issue on tree elements such as search engine selection on the preferences page",
"Fixed pinning and unpinning extensions messing up the toolbar",
{
"description": "Fixed duplicating a tab showing the default workspace container indicator",
"issue": 4132
},
{
"description": "Fixed common issues on the on-boarding pages such as button backgrounds and spacing",
"issue": 4035
},
"Fixed visibility issues when selecting multiple essentials",
"Fixed syncing workspaces on some specific scenarios",
"Fixed glance buttons not being able to be clicked",
"Fixed horizontal spacing issues when using split views",
{
"description": "Fixed naming issues between sidebar and web panels",
"issue": 4043
},
"Fixed moving pinned tabs into the essential tabs container breaking the UI",
"Improved security for Zen Mods, only allowing installs from the Zen Mods website",
"Fixed the completly broken save bookmark dialog",
"Fixed and improved the grain texture for workspace backgrounds (Thanks to @different55)",
{
"description": "Fixed touch security not working on macos",
"issue": 437
},
"Fixed zen's app bundle ID giving an incorrect identifier",
"Fixed issue related to changing workspace with the 4 / 5 mouse buttons and we now take into consideration natural scrolling",
"Fixed some website dialogs overflowing the window",
"Fixed moving around pins not saving their position after a restart",
{
"description": "Fixed extension Pin to Toolbar overlayed with gtk theme toggle",
"issue": 1934
},
{
"description": "Fixed opening home page instead of newtab when closing the last workspace tab",
"issue": 3201
},
"Fixed essential icon not appearing on the context menu",
"Fixed accessibility issues on some native links",
"Fixed compact mode not closing when opening an extension dialog pinned to the sidebar",
"Fixed windows 11 titlebar looking weird when having a custom accent color",
"Fixed workspaces icons appearing on private mode once opening another window",
{
"description": "Fixed having invalid shortcuts hard-locking the user inside the loading screen",
"issue": 4071
},
"Fixed weird UI when dragging essentials around, making them look like they are clipping out of the view",
"Fixed compact mode sidebar flashing for some seconds when opening a glance window"
],
"features": [
"Updated to firefox 134.0",
"Added transitioning when changing between workspaces backgrounds",
"Browser background now goes grayscale when unfocused",
"Added support for Windows 11 setting 'Show accent color on window title bars and borders'",
"Enabled experimental features for twilight builds",
"Improved design of native zen dialogs",
"Re-enabled the default password manager",
"Improved on-boarding experience for new users",
"Added support for searching for emojis on the workspace icon picker",
"Added support for MacOS smart cards",
"Builds will now contain full LTO, improving performance",
"Color picker dialog will now convert to a valid color if it's hex-like but invalid",
"Made workspace switching tab's animation smoother",
"Added developer tools shortcuts support!"
],
"breakingChanges": [
"Changed versioning scheme to match Firefox's Release Candidates",
"Removed option to disable workspaces. Workspaces are now a core feature of Zen, and it can be really easy to ignore them (they are hidden if you have just one workspace) if you don't want to use them. Most of Zen's functionality is built around workspaces, so we decided to make them a core feature. They were an option before because they where experimental."
]
} }

View file

@ -9,12 +9,12 @@ export default {
'-lg': '@media (min-width: 1024px)', '-lg': '@media (min-width: 1024px)',
}, },
colors: { colors: {
"paper": "var(--zen-paper)", paper: 'var(--zen-paper)',
"coral": "#F76F53", coral: '#F76F53',
"dark": "var(--zen-dark)", dark: 'var(--zen-dark)',
"muted": "var(--zen-muted)", muted: 'var(--zen-muted)',
"zen-blue": "#6287f5", 'zen-blue': '#6287f5',
"zen-green": "#63f78b", 'zen-green': '#63f78b',
}, },
}, },
}, },