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

View file

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

View file

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

View file

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

18934
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,46 +1,46 @@
{
"name": "www",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev --port 3000",
"start": "astro preview --port 3000",
"build": "astro check && astro build",
"preview": "astro preview --port 3000",
"wrangler": "wrangler",
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/cloudflare": "^12.1.0",
"@astrojs/react": "^4.1.2",
"@astrojs/rss": "^4.0.10",
"@astrojs/sitemap": "^3.2.1",
"@astrojs/tailwind": "^5.1.4",
"@fontsource/bricolage-grotesque": "^5.1.0",
"@fortawesome/fontawesome-svg-core": "^6.7.1",
"@fortawesome/free-brands-svg-icons": "^6.7.1",
"@fortawesome/free-solid-svg-icons": "^6.7.1",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.2",
"astro": "^5.1.1",
"astro-navbar": "^2.3.7",
"autoprefixer": "10.4.14",
"date-fns": "^4.1.0",
"free-astro-components": "^1.1.1",
"lucide-astro": "^0.460.0",
"motion": "^11.13.5",
"postcss": "8.4.21",
"prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.6.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sharp": "^0.33.5",
"tailwindcss": "^3.4.15",
"typescript": "^5.6.3"
},
"devDependencies": {
"wrangler": "^3.94.0"
}
"name": "www",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev --port 3000",
"start": "astro preview --port 3000",
"build": "astro check && astro build",
"preview": "astro preview --port 3000",
"wrangler": "wrangler",
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/cloudflare": "^12.1.0",
"@astrojs/react": "^4.1.2",
"@astrojs/rss": "^4.0.10",
"@astrojs/sitemap": "^3.2.1",
"@astrojs/tailwind": "^5.1.4",
"@fontsource/bricolage-grotesque": "^5.1.0",
"@fortawesome/fontawesome-svg-core": "^6.7.1",
"@fortawesome/free-brands-svg-icons": "^6.7.1",
"@fortawesome/free-solid-svg-icons": "^6.7.1",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.2",
"astro": "^5.1.1",
"astro-navbar": "^2.3.7",
"autoprefixer": "10.4.14",
"date-fns": "^4.1.0",
"free-astro-components": "^1.1.1",
"lucide-astro": "^0.460.0",
"motion": "^11.13.5",
"postcss": "8.4.21",
"prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.6.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sharp": "^0.33.5",
"tailwindcss": "^3.4.15",
"typescript": "^5.6.3"
},
"devDependencies": {
"wrangler": "^3.94.0"
}
}

4051
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1 +1 @@
{"status":"OK"}
{ "status": "OK" }

View file

@ -1,15 +1,19 @@
export function getTitleAnimation(delay = 0) {
return {
initial: { opacity: 0, translateY: 20, filter: 'blur(4px)' },
whileInView: { opacity: 1, translateY: 0, filter: 'blur(0px)', transition: { duration: 0.3, delay } },
viewport: { once: true }
};
whileInView: {
opacity: 1,
translateY: 0,
filter: 'blur(0px)',
transition: { duration: 0.3, delay },
},
viewport: { once: true },
}
}
export function getZoomInAnimation(delay = 0) {
return {
initial: { scale: 0.8, opacity: 0 },
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" />
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']} >
<slot />
</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']} >
<slot />
</button>
)}
{
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 />
</a>
) : (
<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 />
</button>
)
}
<style>
button, a {
font-size: .9rem;
button,
a {
font-size: 0.9rem;
&[disabled] {
cursor: not-allowed;
opacity: .5;
opacity: 0.5;
}
}
</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 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">
{[...Array(4)].map((_, i) => (
<div class:list={["absolute rounded-full -translate-x-1/2 -translate-y-1/2", white ? "border-paper" : "border-coral"]}
style={{
width: `${multiplier*sizes[i]}px`,
height: `${multiplier*sizes[i]}px`,
borderWidth: `${multiplier*borderWidths[i]}px`,
{
[...Array(4)].map((_, i) => (
<div
class:list={[
'absolute -translate-x-1/2 -translate-y-1/2 rounded-full',
white ? 'border-paper' : 'border-coral',
]}
style={{
width: `${multiplier * sizes[i]}px`,
height: `${multiplier * sizes[i]}px`,
borderWidth: `${multiplier * borderWidths[i]}px`,
}}
/>
))}
/>
))
}
</div>
</div>

View file

@ -2,42 +2,52 @@
import Title from '../components/Title.astro'
import Description from '../components/Description.astro'
import Button from '../components/Button.astro'
import { motion } from 'motion/react';
import {
Github,
Check,
} from 'lucide-astro'
import { motion } from 'motion/react'
import { Github, Check } from 'lucide-astro'
import { getTitleAnimation } from '../animations'
---
<section
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>
<motion.span client:load {...getTitleAnimation(0.2)}>
Community
</motion.span>
<motion.span client:load {...getTitleAnimation(0.4)}>
Driven
</motion.span>
<motion.span client:load {...getTitleAnimation(0.4)}> Driven </motion.span>
</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
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>
<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)}>
<Button class:list={['px-4']} href="https://github.com/zen-browser">
<Github class="size-4" />
<span>View on Github</span>
</Button>
</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" />
<span>Fully Customizable</span>
</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" />
<span>Privacy Focused</span>
</motion.div>

View file

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

View file

@ -1,113 +1,168 @@
---
import Title from '../components/Title.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 browserCompactMode from '../assets/browser-compactmode.png';
import browserSplitViews from '../assets/browser-splitview.png';
import browserSidebar from '../assets/browser-sidebar.png';
import browserWorkspaces from '../assets/browser-workspaces.png'
import browserCompactMode from '../assets/browser-compactmode.png'
import browserSplitViews from '../assets/browser-splitview.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'
---
<section
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 id="feature-list" class="lg:w-1/3 flex flex-col gap-4">
<div class="flex w-full flex-col items-start gap-12 lg:flex-row">
<div id="feature-list" class="flex flex-col gap-4 lg:w-1/3">
<motion.span client:load {...getTitleAnimation(0.2)}>
<Title>Features</Title>
</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">
<Description class="feature-title font-bold text-2xl">
<Description class="feature-title text-2xl font-bold">
Workspaces
</Description>
<p class="desc text-base font-normal mt-2">
With Zen, you can create multiple workspaces to keep your tabs organized.
<p class="desc mt-2 text-base font-normal">
With Zen, you can create multiple workspaces to keep your tabs
organized.
</p>
</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">
<Description class="feature-title font-bold text-2xl">
<Description class="feature-title text-2xl font-bold">
Compact Mode
</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.
</p>
</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">
<Description class="feature-title font-bold text-2xl">
<Description class="feature-title text-2xl font-bold">
Split Views
</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.
</p>
</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">
<Description class="feature-title font-bold text-2xl">
<Description class="feature-title text-2xl font-bold">
Sidebar
</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.
</p>
</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">
<Description class="feature-title font-bold text-2xl">
<Description class="feature-title text-2xl font-bold">
Zen Glance
</Description>
<p class="desc text-base font-normal mt-2">
Preview your tabs with Zen Glance to quickly find what you're looking for, without switching tabs.
<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.
</p>
</div>
</motion.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">
<Image src={browserWorkspaces} alt="Browser Workspaces" class="rounded-xl shadow border-4 border-white" />
<Image src={browserCompactMode} alt="Browser Compact Mode" class="rounded-xl shadow border-4 border-white" />
<Image src={browserSplitViews} alt="Browser Split Views" class="rounded-xl shadow border-4 border-white" />
<Image src={browserSidebar} alt="Browser Sidebar" class="rounded-xl shadow border-4 border-white" />
<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={browserWorkspaces}
alt="Browser Workspaces"
class="rounded-xl border-4 border-white shadow"
/>
<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>
</section>
<script>
function changeFeature(index: number) {
document.querySelectorAll('#feature-list .feature').forEach((feature: any, i) => {
if (i === index) {
feature.setAttribute('active', '');
} else {
feature.removeAttribute('active');
}
});
document.querySelectorAll('#feature-list-image > *').forEach((img: any, i) => {
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) => {
if (i === index) {
feature.setAttribute('active', '')
} else {
feature.removeAttribute('active')
}
})
document
.querySelectorAll('#feature-list-image > *')
.forEach((img: any, i) => {
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) => {
feature.addEventListener('click', () => {
changeFeature(i);
});
});
changeFeature(0);
document
.querySelectorAll('#feature-list .feature')
.forEach((feature: any, i) => {
feature.addEventListener('click', () => {
changeFeature(i)
})
})
changeFeature(0)
</script>
<style>
.feature {
@ -116,7 +171,9 @@ import { getTitleAnimation, getZoomInAnimation } from '../animations'
}
.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 {

View file

@ -5,75 +5,100 @@ import Button from '../components/Button.astro'
import Circles from '../components/Circles.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";
import { faMastodon, faBluesky, faGithub, faTwitter, faReddit } from "@fortawesome/free-brands-svg-icons";
library.add(faMastodon, faBluesky, faGithub, faTwitter, faReddit);
const Mastodon = icon({ prefix: "fab", iconName: "mastodon" });
const Bluesky = icon({ prefix: "fab", iconName: "bluesky" });
const Github = icon({ prefix: "fab", iconName: "github" });
const Twitter = icon({ prefix: "fab", iconName: "twitter" });
const Reddit = icon({ prefix: "fab", iconName: "reddit" });
library.add(faMastodon, faBluesky, faGithub, faTwitter, faReddit)
const Mastodon = icon({ prefix: 'fab', iconName: 'mastodon' })
const Bluesky = icon({ prefix: 'fab', iconName: 'bluesky' })
const Github = icon({ prefix: 'fab', iconName: 'github' })
const Twitter = icon({ prefix: 'fab', iconName: 'twitter' })
const Reddit = icon({ prefix: 'fab', iconName: 'reddit' })
---
<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>
<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
about your experience, not your data.
</Description>
</div>
</div>
<div class="flex flex-col justify-between w-full lg:flex-row gap-6 lg:pr-52 lg:my-12">
<Button href="/download" isPrimary class="h-fit bg-paper w-fit !text-dark">
<div
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
<ArrowRight class="size-4" />
</Button>
<div class="gap-10 flex flex-col">
<div class="flex flex-col gap-10">
<div class="flex flex-col gap-2">
<Description class="!font-semibold">
Follow Us
</Description>
<div class="opacity-80 flex gap-4 items-center">
<a href="https://github.com/zen-browser" target="_blank" class="font-bold" aria-label="Visit Zen Browser on GitHub">
<Description class="!font-semibold"> Follow Us </Description>
<div class="flex items-center gap-4 opacity-80">
<a
href="https://github.com/zen-browser"
target="_blank"
class="font-bold"
aria-label="Visit Zen Browser on GitHub"
>
<Fragment set:html={Github.html} />
</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} />
</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} />
</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} />
</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} />
</a>
</div>
</div>
<div class="flex flex-col gap-2">
<Description class="!font-semibold">
About Us
</Description>
<div class="opacity-80 flex flex-col">
<Description class="!font-semibold"> About Us </Description>
<div class="flex flex-col opacity-80">
<a href="/about" class="font-normal">About Us</a>
<a href="/privacy-policy" class="font-normal">Privacy Policy</a>
</div>
</div>
</div>
<div class="flex flex-col gap-2">
<Description class="!font-semibold">
Get Started
</Description>
<div class="opacity-80 flex flex-col">
<Description class="!font-semibold"> Get Started </Description>
<div class="flex flex-col opacity-80">
<a href="/download" class="font-normal">Download</a>
<a href="/mods" class="font-normal">Zen Mods</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 class="flex flex-col gap-2">
<Description class="!font-semibold">
Get Help
</Description>
<div class="opacity-80 flex flex-col">
<Description class="!font-semibold"> Get Help </Description>
<div class="flex flex-col opacity-80">
<a href="https://discord.gg/zen-browser" class="font-normal">Discord</a>
<div class="flex items-center">
<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>
<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>
<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 class="flex w-full h-32 items-center">
<div class="flex h-32 w-full items-center">
<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>
<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>
</footer>

View file

@ -4,45 +4,49 @@ import Description from '../components/Description.astro'
import Button from '../components/Button.astro'
import { Image } from 'astro:assets'
import myImage from '../assets/browsers.png'
import { ArrowRight } from 'lucide-astro';
import { motion } from 'motion/react';
import { ArrowRight } from 'lucide-astro'
import { motion } from 'motion/react'
import { getTitleAnimation } from '../animations'
let titleAnimationCounter = 0;
let titleAnimationCounter = 0
function getNewAnimationDelay() {
titleAnimationCounter++;
return titleAnimationCounter * 0.15;
titleAnimationCounter++
return titleAnimationCounter * 0.15
}
function getHeroTitleAnimation() {
return getTitleAnimation(getNewAnimationDelay());
return getTitleAnimation(getNewAnimationDelay())
}
---
<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">
<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)}>
<div>
Beta is now available!
</div>
<div class="flex h-full flex-col items-center justify-center">
<motion.a
href="/download"
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"
client:load
{...getTitleAnimation(1.8)}
>
<div>Beta is now available!</div>
<ArrowRight class="size-4" />
</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()}>
Welcome
</motion.span>
<motion.span client:load {...getHeroTitleAnimation()}>
to
</motion.span>
<motion.span client:load {...getHeroTitleAnimation()}> to </motion.span>
<br class="hidden md:block" />
<motion.span client:load {...getHeroTitleAnimation()}>
a
</motion.span>
<motion.span client:load {...getHeroTitleAnimation()} className='italic text-coral'>
<motion.span client:load {...getHeroTitleAnimation()}> a </motion.span>
<motion.span
client:load
{...getHeroTitleAnimation()}
className="italic text-coral"
>
calmer
</motion.span>
<motion.span client:load {...getHeroTitleAnimation()}>
@ -50,12 +54,12 @@ function getHeroTitleAnimation() {
</motion.span>
</Title>
<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
/>We care about your experience, not your data.</Description
>
</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()}>
<Button class="w-full" href="/download" isPrimary>
Download
@ -69,5 +73,10 @@ function getHeroTitleAnimation() {
</div>
</header>
<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>

View file

@ -1,35 +1,51 @@
---
import Title from '../components/Title.astro'
import Description from '../components/Description.astro'
import { Image } from 'astro:assets';
import { Image } from 'astro:assets'
import browserCollapsed from '../assets/collapsed.png';
import browseMultiToolbar from '../assets/multiple-toolbar.png';
import browserSingleToolbar from '../assets/single-toolbar.png';
import Button from './Button.astro';
import { ArrowRight } from 'lucide-astro';
import browserCollapsed from '../assets/collapsed.png'
import browseMultiToolbar from '../assets/multiple-toolbar.png'
import browserSingleToolbar from '../assets/single-toolbar.png'
import Button from './Button.astro'
import { ArrowRight } from 'lucide-astro'
---
<section
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>
<Title>Customizable<br class="md:hidden" /> to <br class="hidden md:block" />the last pixel</Title>
<Description class="lg:px-0 lg:w-1/2">
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.
<Title
>Customizable<br class="md:hidden" /> to <br class="hidden md:block" />the
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>
<div class="flex mt-4">
<div class="mt-4 flex">
<Button isPrimary href="/mods">
Zen Mods
<ArrowRight className="size-4" />
</Button>
Zen Mods
<ArrowRight className="size-4" />
</Button>
</div>
</div>
<div class="relative ml-16 h-32 lg:mx-0 lg:w-1/2 flex" 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 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" />
<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" />
<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="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>
</section>
<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">
<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 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 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"/>
<svg
width="32"
height="32"
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>

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 Description from '../components/Description.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 Logo from './Logo.astro';
import Logo from './Logo.astro'
import { ThemeSwitch } from 'free-astro-components'
---
<nav
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>
<MenuItems class="w-full lg:w-fit p-2 bg-paper mx-auto flex gap-2 lg:gap-20 relative">
<a class="font-bold text-lg items-center flex gap-2 mr-3" href="/">
<Logo class="text-dark" /> <span class="hidden lg:block">zen browser</span>
<MenuItems
class="relative mx-auto flex w-full gap-2 bg-paper p-2 lg:w-fit lg:gap-20"
>
<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>
<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">
<button class="flex items-center">
<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>
<DropdownItems>
<div class="navbar-dropdown w-full lg:w-fit top-16 left-0 right-1/4">
<a class="dropdown-item row-span-2 bg-dark/5" href="/mods">
<div class="dropdown-title">
Zen Mods
</div>
<div
class="navbar-dropdown left-0 right-1/4 top-16 w-full lg:w-fit"
>
<a class="dropdown-item bg-dark/5 row-span-2" href="/mods">
<div class="dropdown-title">Zen Mods</div>
<div class="dropdown-description">
Customize your browsing experience with Zen Mods.
</div>
@ -38,18 +49,14 @@ import { ThemeSwitch } from 'free-astro-components'
<ArrowRight class="size-4" />
</Button>
</a>
<a class="dropdown-item" href="/release-notes">
<div class="dropdown-title">
Release Notes
</div>
<a class="dropdown-item" href="/release-notes">
<div class="dropdown-title">Release Notes</div>
<div class="dropdown-description">
Stay up to date with the latest features and improvements.
</div>
</a>
<a class="dropdown-item" href="https://discord.gg/zen-browser">
<div class="dropdown-title">
Discord
</div>
<div class="dropdown-title">Discord</div>
<div class="dropdown-description">
Join our community on Discord to chat with other Zen users!
</div>
@ -60,38 +67,38 @@ import { ThemeSwitch } from 'free-astro-components'
<Dropdown class="group">
<button class="flex items-center">
<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>
<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">
<a class="dropdown-item" href="/donate">
<div class="dropdown-title">
Donate ❤️
</div>
<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">
<div class="dropdown-title">Donate ❤️</div>
<div class="dropdown-description">
Support the development of Zen Browser with a donation.
</div>
</a>
<a class="dropdown-item" href="/about">
<div class="dropdown-title">
About Us 🌟
</div>
<div class="dropdown-title">About Us 🌟</div>
<div class="dropdown-description">
Learn more about the team behind Zen Browser.
</div>
</a>
<a class="dropdown-item" href="https://docs.zen-browser.app">
<div class="dropdown-title">
Documentation
</div>
<div class="dropdown-title">Documentation</div>
<div class="dropdown-description">
Learn how to use Zen Browser with our documentation.
</div>
</a>
<a class="dropdown-item" href="https://github.com/zen-browser" target="_blank">
<div class="dropdown-title">
GitHub
</div>
<a
class="dropdown-item"
href="https://github.com/zen-browser"
target="_blank"
>
<div class="dropdown-title">GitHub</div>
<div class="dropdown-description">
Contribute to the development of Zen Browser on GitHub.
</div>
@ -99,16 +106,16 @@ import { ThemeSwitch } from 'free-astro-components'
</div>
</DropdownItems>
</Dropdown>
<a class="items-center hidden lg:block" href="/mods">
<a class="hidden items-center lg:block" href="/mods">
<span>Mods</span>
</a>
</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">
<ThemeSwitch label="" class="py-2 px-1" />
<ThemeSwitch label="" class="px-1 py-2" />
</div>
<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
<ArrowRight class="size-4" />
</span>
@ -122,10 +129,10 @@ import { ThemeSwitch } from 'free-astro-components'
</nav>
<style>
.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 {
@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 {
@apply bg-muted;

View file

@ -1,176 +1,219 @@
---
import { Accordion, AccordionItem } from 'free-astro-components';
import { Info } from 'lucide-astro';
import { Accordion, AccordionItem } from 'free-astro-components'
import { Info } from 'lucide-astro'
import type { ReleaseNote, BreakingChange } from '../release-notes'
export type Props = ReleaseNote;
const {isTwilight, ...props} = Astro.props;
export type Props = ReleaseNote
const { isTwilight, ...props } = Astro.props
let date;
let date
if (props.date) {
const [day, month, year] = props.date.split('/');
date = new Date(Date.parse(`${year}-${month}-${day}`));
const [day, month, year] = props.date.split('/')
date = new Date(Date.parse(`${year}-${month}-${day}`))
}
---
<section
class="relative mt-24 flex flex-col border-t pt-24 lg:flex-row release-note-item"
id={props.version}
>
<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">
Twilight
</a>
) : null}
<h1 class="text-3xl font-bold">
{isTwilight ? (
<>
Twilight changes for {props.version} 🌙
</>
) : (
<>
Release notes for {props.version} 🎉
</>
)}
</h1>
{date && date.toLocaleDateString('en-US', { dateStyle: 'long'})}
<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>
{!isTwilight ? (
<>
<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>
</>
) : null}
</div>
<div class="mt-6 text-sm opacity-70 text-muted-forground flex">
{isTwilight ? (
<Info class="my-0 mx-4 size-6 text-yellow-500" />
) : null}
<p class="m-0">
{isTwilight ? (
<>
Please note that Twilight is a pre-release version of Zen Browser. It may contain bugs and unfinished features.
</>
) : 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>
</p>
class="release-note-item relative mt-24 flex flex-col border-t pt-24 lg:flex-row"
id={props.version}
>
<div class="px-5 md:px-10 md:pr-32">
{
isTwilight ? (
<a
class="!mb-2 block w-fit rounded-full bg-coral px-3 py-1 text-xs text-paper"
href="/download?twilight"
>
Twilight
</a>
) : null
}
<h1 class="text-3xl font-bold">
{
isTwilight ? (
<>Twilight changes for {props.version} 🌙</>
) : (
<>Release notes for {props.version} 🎉</>
)
}
</h1>
{date && date.toLocaleDateString('en-US', { dateStyle: 'long' })}
<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
>
{
!isTwilight ? (
<>
<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>
</>
) : null
}
</div>
<div class="text-muted-forground mt-6 flex text-sm opacity-70">
{isTwilight ? <Info class="mx-4 my-0 size-6 text-yellow-500" /> : null}
<p class="m-0">
{
isTwilight ? (
<>
Please note that Twilight is a pre-release version of Zen Browser.
It may contain bugs and unfinished features.
</>
) : 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
>.
</p>
</div>
{props.extra ? (
<p class="text-md mt-2 text-muted-foreground extra">
<Fragment set:html={props.extra.replace(/\n/g, '<br />')} />
</p>
) : null
}
<div class="mt-8" data-orientation="vertical"></div>
<Accordion>
{props.fixes ? (
<AccordionItem title="Fixes" name="fixes">
<ul class="list-disc list-inside">
{props.fixes.map((fix: any) => (
<li class="text-md text-muted-foreground">
{typeof fix === 'string' ? fix : (
<>
{fix.description}
{fix.issue ? (
<a
class="text-coral"
href={`https://github.com/zen-browser/desktop/issues/${fix.issue}`}
rel="noopener noreferrer"
target="_blank"
aria-label={`View issue number ${fix.issue} on GitHub`}
>
#{fix.issue}
</a>
) : null}
</>
)}
</li>
))}
</ul>
</AccordionItem>
) : null}
{props.features ? (
<AccordionItem title="Features" name="features">
<ul class="list-disc list-inside">
{props.features.map((feature: string) => (
<li class="text-md text-muted-foreground">{feature}</li>
))}
</ul>
</AccordionItem>
) : null}
{props.themeChanges ? (
<AccordionItem title="Theme Changes" name="themeChanges">
<ul class="list-disc list-inside">
{props.themeChanges.map((themeChange: string) => (
<li class="text-md text-muted-foreground">{themeChange}</li>
))}
</ul>
</AccordionItem>
) : null}
{props.breakingChanges ? (
<AccordionItem title="Breaking Changes" name="breakingChanges">
<ul class="list-disc list-inside">
{props.breakingChanges.map((breakingChange: BreakingChange) => (
<li class="text-md text-muted-foreground">{typeof breakingChange === 'string' ? breakingChange : (
<>
{breakingChange.description}
<a
class="text-coral"
href={breakingChange.link}
rel="noopener noreferrer"
target="_blank"
aria-label={`View breaking change on GitHub`}
>
Learn more
</a>
</>
)}</li>
))}
</ul>
</AccordionItem>
) : null}
</div>
{
props.extra ? (
<p class="text-md text-muted-foreground extra mt-2">
<Fragment set:html={props.extra.replace(/\n/g, '<br />')} />
</p>
) : null
}
<div class="mt-8" data-orientation="vertical"></div>
<Accordion>
{
props.fixes ? (
<AccordionItem title="Fixes" name="fixes">
<ul class="list-inside list-disc">
{props.fixes.map((fix: any) => (
<li class="text-md text-muted-foreground">
{typeof fix === 'string' ? (
fix
) : (
<>
{fix.description}
{fix.issue ? (
<a
class="text-coral"
href={`https://github.com/zen-browser/desktop/issues/${fix.issue}`}
rel="noopener noreferrer"
target="_blank"
aria-label={`View issue number ${fix.issue} on GitHub`}
>
#{fix.issue}
</a>
) : null}
</>
)}
</li>
))}
</ul>
</AccordionItem>
) : null
}
{
props.features ? (
<AccordionItem title="Features" name="features">
<ul class="list-inside list-disc">
{props.features.map((feature: string) => (
<li class="text-md text-muted-foreground">{feature}</li>
))}
</ul>
</AccordionItem>
) : null
}
{
props.themeChanges ? (
<AccordionItem title="Theme Changes" name="themeChanges">
<ul class="list-inside list-disc">
{props.themeChanges.map((themeChange: string) => (
<li class="text-md text-muted-foreground">{themeChange}</li>
))}
</ul>
</AccordionItem>
) : null
}
{
props.breakingChanges ? (
<AccordionItem title="Breaking Changes" name="breakingChanges">
<ul class="list-inside list-disc">
{props.breakingChanges.map((breakingChange: BreakingChange) => (
<li class="text-md text-muted-foreground">
{typeof breakingChange === 'string' ? (
breakingChange
) : (
<>
{breakingChange.description}
<a
class="text-coral"
href={breakingChange.link}
rel="noopener noreferrer"
target="_blank"
aria-label={`View breaking change on GitHub`}
>
Learn more
</a>
</>
)}
</li>
))}
</ul>
</AccordionItem>
) : null
}
</Accordion>
</div>
<style is:global>
.ac-accordion-item-title {
@apply !text-dark;
flex-direction: row-reverse !important;
&:hover {
opacity: 0.8 !important;
}
& > svg {
color: var(--zen-dark) !important;
}
}
.ac-accordion-item {
transition: height 0.2s ease-in-out !important;
& li {
opacity: 0.5;
}
}
.ac-accordion {
&.ac-accordion--light {
> * + * {
border-color: light-dark(
rgba(0, 0, 0, 0.1),
rgba(255, 255, 255, 0.1)
) !important;
width: 100%;
}
}
}
.extra {
& a {
@apply !text-coral;
}
}
.release-note-item {
border-color: light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2));
}
</style>
</section>
<style is:global>
.ac-accordion-item-title {
@apply !text-dark;
flex-direction: row-reverse!important;
&:hover {
opacity: 0.8 !important;
}
& > svg {
color: var(--zen-dark)!important;
}
}
.ac-accordion-item {
transition: height 0.2s ease-in-out !important;
& li {
opacity: .5;
}
}
.ac-accordion {
&.ac-accordion--light {
> * + * {
border-color: light-dark(rgba(0,0,0,.1), rgba(255,255,255,.1)) !important;
width: 100%;
}
}
}
.extra {
& a {
@apply !text-coral;
}
}
.release-note-item {
border-color: light-dark(rgba(0,0,0,.2), rgba(255,255,255,.2));
}
</style>

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 />
</h1>
<style>

View file

@ -30,7 +30,7 @@ import Footer from '../components/Footer.astro'
/>
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="sitemap" href="/sitemap-0.xml"/>
<link rel="sitemap" href="/sitemap-0.xml" />
<!-- ICO Favicon as a fallback for browsers that don't support SVG FavIcons (Safari) -->
<link rel="icon" type="image/x-icon" href="/favicon.ico" />

View file

@ -1,4 +1,4 @@
import { format } from "date-fns"
import { format } from 'date-fns'
export interface ZenTheme {
name: string
@ -37,5 +37,5 @@ export function getAuthorLink(author: string): 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">
<main class="min-h-screen relative flex flex-col py-24 justify-center items-center">
<div class="text-center p-4 mb-24 lg:w-1/2">
<main
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>
<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>
<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 class="w-full relative flex flex-col lg:flex-row justify-center items-center">
<div class="p-8 lg:pr-24 flex flex-col lg:w-1/3">
<div class="font-bold text-6xl">Main Team</div>
<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="text-6xl font-bold">Main Team</div>
<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>
<div class="mt-4">
<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 mt-1"><strong class="italic">Oscar Gonzalez</strong><span class="opacity-60"> Site Reliability Engineer (SRE) and code signing.</span></li>
<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>
<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>
<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 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="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>
<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>
<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>
<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 class="text-sm mt-1"><strong class="italic">Daniel García</strong><span class="opacity-60">: MacOS certificate and app notarization maintainer</span></li>
</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="mt-1 text-sm">
<strong class="italic">Oscar Gonzalez</strong><span
class="opacity-60"
>
Site Reliability Engineer (SRE) and code signing.</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>
</div>
</div>
<div class="hidden lg:block h-full w-[1px] bg-dark absolute opacity-15"></div>
<div class="p-8 lg:pl-24 flex flex-col lg:w-1/3">
<div class="font-bold text-6xl">Contributors</div>
<div class="absolute hidden h-full 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="text-6xl font-bold">Contributors</div>
<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>
<img 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" />
<img
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>
</main>

View file

@ -7,7 +7,7 @@ import Layout from '../layouts/Layout.astro'
---
<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">
<Title>Donate!</Title>
<Description>
@ -16,7 +16,9 @@ import Layout from '../layouts/Layout.astro'
us.
</Description>
</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="text-6xl font-bold">Patreon</div>
<Description>
@ -24,13 +26,17 @@ import Layout from '../layouts/Layout.astro'
choose the level of support that works best for you.
</Description>
<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
<ArrowRight class="size-4" />
</Button>
</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="text-6xl font-bold">Ko-fi</div>
<Description>

View file

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

View file

@ -1,26 +1,27 @@
import rss, { type RSSOptions } from '@astrojs/rss';
import { releaseNotes } from '../release-notes';
import type { ReleaseNote } from '../release-notes';
import rss, { type RSSOptions } from '@astrojs/rss'
import { releaseNotes } from '../release-notes'
import type { ReleaseNote } from '../release-notes'
/** 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.
* @returns The RSS feed for the Zen Browser release notes.
*/
export function GET(context: any) {
// Just in case the release notes array is empty for whatever reason.
const latestDate = releaseNotes.length > 0
? formatRssDate(releaseNotes[0].date as string)
: new Date();
// Just in case the release notes array is empty for whatever reason.
const latestDate =
releaseNotes.length > 0
? formatRssDate(releaseNotes[0].date as string)
: new Date()
const rssData: RSSOptions = {
title: "Zen Browser Release Notes",
description: "Release Notes for the Zen Browser",
site: context.url,
items: [],
customData: `
const rssData: RSSOptions = {
title: 'Zen Browser Release Notes',
description: 'Release Notes for the Zen Browser',
site: context.url,
items: [],
customData: `
<language>en</language>
<link>https://www.zen-browser.app/release-notes</link>
<copyright>Zen Browser © ${new Date().getFullYear()} - Made with by the Zen team.</copyright>
@ -30,20 +31,20 @@ export function GET(context: any) {
<title>Zen Browser</title>
<link>https://www.zen-browser.app</link>
</image>
`
};
`,
}
for (const releaseNote of releaseNotes.slice(0, RSS_ENTRY_LIMIT)) {
rssData.items.push({
title: `Release notes for version ${releaseNote.version}`,
link: `https://www.zen-browser.app/release-notes/${releaseNote.version}`,
pubDate: formatRssDate(releaseNote.date as string),
description: releaseNote.extra,
content: formatReleaseNote(releaseNote),
});
}
for (const releaseNote of releaseNotes.slice(0, RSS_ENTRY_LIMIT)) {
rssData.items.push({
title: `Release notes for version ${releaseNote.version}`,
link: `https://www.zen-browser.app/release-notes/${releaseNote.version}`,
pubDate: formatRssDate(releaseNote.date as string),
description: releaseNote.extra,
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.
*/
function formatRssDate(dateStr: string) {
const splitDate = dateStr.split("/");
if (splitDate.length !== 3) {
throw new Error("Invalid date format");
}
const splitDate = dateStr.split('/')
if (splitDate.length !== 3) {
throw new Error('Invalid date format')
}
const day = Number(splitDate[0]);
const month = Number(splitDate[1]) - 1;
const year = Number(splitDate[2]);
return new Date(year, month, day);
const day = Number(splitDate[0])
const month = Number(splitDate[1]) - 1
const year = Number(splitDate[2])
return new Date(year, month, day)
}
/**
@ -71,88 +72,102 @@ function formatRssDate(dateStr: string) {
* @returns The formatted release note as a HTML string.
*/
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>.
Thanks everyone for your feedback!
</p>`;
</p>`
if (releaseNote.image) {
content += `<img src="https://cdn.jsdelivr.net/gh/zen-browser/www/public/releases/${releaseNote.version}.png"
if (releaseNote.image) {
content += `<img src="https://cdn.jsdelivr.net/gh/zen-browser/www/public/releases/${releaseNote.version}.png"
alt="Release Image for version ${releaseNote.version}"
style="max-width: 30em; width: 100%; border-radius: 0.5rem;"
/>`;
}
/>`
}
if (releaseNote.extra) {
content += `<p>${releaseNote.extra.replace(/(\n)/g, "<br />")}</p>`;
}
if (releaseNote.extra) {
content += `<p>${releaseNote.extra.replace(/(\n)/g, '<br />')}</p>`
}
content += addReleaseNoteSection("⚠️ Breaking changes", releaseNote.breakingChanges?.map(breakingChangeToReleaseNote));
content += addReleaseNoteSection("✓ Fixes", releaseNote.fixes?.map(fixToReleaseNote));
content += addReleaseNoteSection("🖌 Theme Changes", releaseNote.themeChanges)
content += addReleaseNoteSection("⭐ Features", releaseNote.features);
content += addReleaseNoteSection(
'⚠️ Breaking changes',
releaseNote.breakingChanges?.map(breakingChangeToReleaseNote),
)
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 {
if (!items) {
return "";
}
if (!items) {
return ''
}
let content = `<h2>${title}</h2>`;
content += `<ul>`;
for (const item of items) {
if (item && item.length > 0) {
content += `<li>${item}</li>`;
}
let content = `<h2>${title}</h2>`
content += `<ul>`
for (const item of items) {
if (item && item.length > 0) {
content += `<li>${item}</li>`
}
content += `</ul>`;
return content;
}
content += `</ul>`
return content
}
function fixToReleaseNote(fix?: Exclude<ReleaseNote['fixes'], undefined>[number]) {
if (typeof fix === 'string') {
return fix;
}
function fixToReleaseNote(
fix?: Exclude<ReleaseNote['fixes'], undefined>[number],
) {
if (typeof fix === 'string') {
return fix
}
if (!fix || !fix.description || fix.description.length === 0) {
return "";
}
if (!fix || !fix.description || fix.description.length === 0) {
return ''
}
let note = fix.description;
if (fix.issue) {
note += ` (<a href="https://github.com/zen-browser/desktop/issues/${fix.issue}" target="_blank">#${fix.issue}</a>)`;
}
return note;
let note = fix.description
if (fix.issue) {
note += ` (<a href="https://github.com/zen-browser/desktop/issues/${fix.issue}" target="_blank">#${fix.issue}</a>)`
}
return note
}
function breakingChangeToReleaseNote(breakingChange?: Exclude<ReleaseNote['breakingChanges'], undefined>[number]) {
if (typeof breakingChange === 'string') {
return breakingChange;
}
function breakingChangeToReleaseNote(
breakingChange?: Exclude<ReleaseNote['breakingChanges'], undefined>[number],
) {
if (typeof breakingChange === 'string') {
return breakingChange
}
if (!breakingChange || !breakingChange.description || breakingChange.description.length === 0) {
return "";
}
if (
!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) {
date ??= new Date();
date ??= new Date()
const pieces = date.toString().split(' ');
const offsetTime = pieces[5].match(/[-+]\d{4}/);
const offset = (offsetTime) ? offsetTime : pieces[5];
const parts = [
pieces[0] + ',',
pieces[2],
pieces[1],
pieces[3],
pieces[4],
offset
];
const pieces = date.toString().split(' ')
const offsetTime = pieces[5].match(/[-+]\d{4}/)
const offset = offsetTime ? offsetTime : pieces[5]
const parts = [
pieces[0] + ',',
pieces[2],
pieces[1],
pieces[3],
pieces[4],
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;
}
</style>
<script>
</script>
<script></script>

View file

@ -1,37 +1,37 @@
---
import { getAbsoluteLocaleUrl } from 'astro:i18n';
import { getAllMods, getAuthorLink, getLocalizedDate } from '../../mods';
import Layout from '../../layouts/Layout.astro';
import Title from '../../components/Title.astro';
import Description from '../../components/Description.astro';
import Button from '../../components/Button.astro';
import BackButton from '../../components/BackButton.astro';
import { ArrowRight, Info } from 'lucide-astro';
import { getAbsoluteLocaleUrl } from 'astro:i18n'
import { getAllMods, getAuthorLink, getLocalizedDate } from '../../mods'
import Layout from '../../layouts/Layout.astro'
import Title from '../../components/Title.astro'
import Description from '../../components/Description.astro'
import Button from '../../components/Button.astro'
import BackButton from '../../components/BackButton.astro'
import { ArrowRight, Info } from 'lucide-astro'
export async function getStaticPaths() {
const mods = await getAllMods();
const mods = await getAllMods()
return mods.map((mod) => ({
params: { slug: mod.id },
props: { ...mod },
}));
}))
}
// https://github.com/TeaClientMC/Website/blob/7faacc9f8b2c79c74f711d413b155c84faafc00d/src/pages/news/%5B...slug%5D.astro
const mod = Astro.props;
const mod = Astro.props
const dates = {
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">
<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
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">
<Info />
@ -39,7 +39,11 @@ const dates = {
You need to have Zen Browser installed to install this theme.{' '}
</p>
</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!
<ArrowRight class="size-4" />
</Button>
@ -52,19 +56,29 @@ const dates = {
<img
src={mod.image}
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="font-normal flex-shrink-0">
<p>Created by <a href={getAuthorLink(mod.author)} class="font-bold">@{mod.author}</a> • <span class="font-bold">v{mod.version}</span></p>
<div class="flex flex-col justify-between gap-2 sm:flex-row">
<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>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 class="flex flex-col sm:items-end">
<Button
class="hidden"
id="install-theme"
extra={{"zen-theme-id":mod.id}}
extra={{ 'zen-theme-id': mod.id }}
isPrimary
>
Install Theme 🎉
@ -72,7 +86,7 @@ const dates = {
<Button
class="hidden"
id="install-theme-uninstall"
extra={{"zen-theme-id":mod.id}}
extra={{ 'zen-theme-id': mod.id }}
isPrimary
>
Uninstall Theme
@ -83,6 +97,6 @@ const dates = {
<div class="h-[1px] opacity-80 bg-dark w-full"></div>
-->
</div>
<div>
</main>
</Layout>
<div></div>
</main></Layout
>

View file

@ -1,91 +1,241 @@
---
import Title from "../components/Title.astro";
import Layout from "../layouts/Layout.astro";
import Title from '../components/Title.astro'
import Layout from '../layouts/Layout.astro'
---
<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>
<div class="ml-4 font-bold">
Last updated: 2024-08-12
</div>
<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>
<div class="my-12 mx-12 flex gap-4 font-bold">
<div class="ml-4 font-bold">Last updated: 2024-08-12</div>
<Title class="mt-16 !text-4xl font-bold" 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>
<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
</div>
<Title class="!text-4xl font-bold mt-16" 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>
<Title
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>
<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>
<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>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>
<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>
<p>Zen Browser does not collect any personal information such as your IP address, browsing history, search queries, or form data.</p>
<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>
<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="!text-4xl font-bold mt-16" id="2-information-stored-locally-on-your-device">2. Information Stored Locally on Your Device</Title>
<h3 class="!font-bold text-xl mt-4" 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>
<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>
<h3 class="mt-4 text-xl !font-bold" id="-1-2-no-personal-data-collection-">
<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>
<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>
<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>
<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>
<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="!text-4xl font-bold mt-16" 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>
<h3 class="mt-4 text-xl !font-bold" id="-2-2-settings-and-preferences-">
<strong class="font-bold">2.2. Settings and Preferences</strong>
</h3>
<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>
<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>
<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>
<Title class="!text-4xl font-bold mt-16" id="4-data-security">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>
<Title class="mt-16 !text-4xl font-bold" id="4-data-security"
>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>
<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>
<Title class="!text-4xl font-bold mt-16" id="5-your-control">5. Your Control</Title>
<h3 class="!font-bold text-xl mt-4" id="-5-1-data-deletion-"><strong class="font-bold">5.1. Data Deletion</strong></h3>
<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>
<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="!text-4xl font-bold mt-16" 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="!font-bold text-xl mt-4" 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="!text-4xl font-bold mt-16" 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="!text-4xl font-bold mt-16" 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>
<Title class="mt-16 !text-4xl font-bold" id="5-your-control"
>5. Your Control</Title
>
<h3 class="mt-4 text-xl !font-bold" id="-5-1-data-deletion-">
<strong class="font-bold">5.1. Data Deletion</strong>
</h3>
<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="mt-4 text-xl !font-bold" id="-5-2-do-not-track-">
<strong class="font-bold">5.2. Do Not Track</strong>
</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>
<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>
<Title class="!text-4xl font-bold mt-16" id="9-contact-us">9. Contact Us</Title>
<p>If you have any questions or concerns about this Privacy Policy or Zen Browser, please contact us at:</p>
<Title class="mt-16 !text-4xl font-bold" id="9-contact-us"
>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>
<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>
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>
</ul>
</main>
</Layout>
<style>
p, li {
@apply opacity-55
p,
li {
@apply opacity-55;
}
ul {
@apply mt-4
@apply mt-4;
}
li {
@apply list-disc ml-4
@apply ml-4 list-disc;
}
a {
@apply text-coral
@apply text-coral;
}
</style>

View file

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

View file

@ -6,26 +6,30 @@ import Title from '../../components/Title.astro'
---
<Layout title="Release Notes - Zen Browser">
<main
class="flex h-full min-h-[1000px] flex-1 flex-col items-center justify-center py-4"
>
<div
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>
<p class="opacity-55 text-base">
Stay up to date with the latest changes to Zen Browser! Since the <a
class="text-coral"
href="#1.0.0-a.1">first release</a
> till <a
class="text-coral"
href={`/release-notes#${releaseNotes[0].version}`}
>{releaseNotes[0].version}</a
>, we've been working hard to make Zen Browser the best it can be.
Thanks everyone for your feedback! ❤️
</p>
<ReleaseNoteItem {...releaseNotesTwilight} isTwilight />
{releaseNotes.map((notes: any) => <ReleaseNoteItem {...notes} />)}
</div>
</main>
<main
class="flex h-full min-h-[1000px] flex-1 flex-col items-center justify-center py-4"
>
<div
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>
<p class="text-base opacity-55">
Stay up to date with the latest changes to Zen Browser! Since the <a
class="text-coral"
href="#1.0.0-a.1">first release</a
> till <a
class="text-coral"
href={`/release-notes#${releaseNotes[0].version}`}
>{releaseNotes[0].version}</a
>, we've been working hard to make Zen Browser the best it can be.
Thanks everyone for your feedback! ❤️
</p>
{
(releaseNotesTwilight.features || releaseNotesTwilight.fixes) && (
<ReleaseNoteItem {...releaseNotesTwilight} isTwilight />
)
}
{releaseNotes.map((notes: any) => <ReleaseNoteItem {...notes} />)}
</div>
</main>
</Layout>

View file

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

View file

@ -363,9 +363,7 @@
"version": "1.0.0-a.13",
"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.",
"features": [
"Allow to remember sidebar width even after collapsing it."
],
"features": ["Allow to remember sidebar width even after collapsing it."],
"fixes": [
{
"description": "Task Manager Icon Missing in Flatpak Version",
@ -733,11 +731,11 @@
"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
},
{
"description": "Fixed issue with keyboard shortcuts on macOS",
"description": "Fixed issue with \u2318 keyboard shortcuts on macOS",
"issue": 376
}
]
@ -921,9 +919,7 @@
"image": true,
"workflowId": 11000317603,
"extra": "This update addresses some significant issues with the previous release.\n\nWe appreciate your patience and support!",
"features": [
"Added a new system for handling keyboard shortcuts"
],
"features": ["Added a new system for handling keyboard shortcuts"],
"fixes": [
{
"description": "The New Tab button is not visible",
@ -980,9 +976,7 @@
"Enabled container tabs by default",
"Improved Expand Tabs on Hover layout"
],
"themeChanges": [
"Toggle inputs will not use the themed tertiary color"
],
"themeChanges": ["Toggle inputs will not use the themed tertiary color"],
"breakingChanges": [
"The keyboard shortcuts will be overriden by the defaults ones in this update"
],
@ -1279,9 +1273,7 @@
"issue": 2156
}
],
"breakingChanges": [
"Removed Show Expand Button option from settings"
],
"breakingChanges": ["Removed Show Expand Button option from settings"],
"themeChanges": [
"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"
@ -1384,9 +1376,7 @@
"description": "Fixed wrong aligment on glance action buttons"
}
],
"features": [
"No new features, sorry"
]
"features": ["No new features, sorry"]
},
{
"version": "1.0.1-a.17",
@ -1883,5 +1873,134 @@
"breakingChanges": [
"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",
"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",
"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."
]
"version": "xxx",
"image": false,
"extra": "",
"fixes": [],
"features": []
}

View file

@ -1,22 +1,22 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
darkMode: [ 'selector' , '[data-theme="dark"]'],
theme: {
extend: {
screens: {
'-md': '@media (min-width: 768px)',
'-lg': '@media (min-width: 1024px)',
},
colors: {
"paper": "var(--zen-paper)",
"coral": "#F76F53",
"dark": "var(--zen-dark)",
"muted": "var(--zen-muted)",
"zen-blue": "#6287f5",
"zen-green": "#63f78b",
},
},
},
plugins: [],
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
darkMode: ['selector', '[data-theme="dark"]'],
theme: {
extend: {
screens: {
'-md': '@media (min-width: 768px)',
'-lg': '@media (min-width: 1024px)',
},
colors: {
paper: 'var(--zen-paper)',
coral: '#F76F53',
dark: 'var(--zen-dark)',
muted: 'var(--zen-muted)',
'zen-blue': '#6287f5',
'zen-green': '#63f78b',
},
},
},
plugins: [],
}