Merge main into new-branding to sync latest changes

Resolves conflicts:
- Footer.astro: Keep new branding design, add preRelease logic from main
- au4.astro, beta.astro: Accept deletions from main
- download.astro: Keep new branding styles (text-12, text-text-primary), add pre-release builds section from main
This commit is contained in:
Mudskipper
2025-10-20 12:55:27 +11:00
17 changed files with 881 additions and 450 deletions

View File

@@ -21,6 +21,7 @@
"astro-compressor": "^0.4.1",
"astro-icon": "^0.8.1",
"astro-lazy-youtube-embed": "^0.5.4",
"classnames": "^2.5.1",
"platform": "^1.3.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@@ -19,7 +19,7 @@
/gitbook-access https://app.gitbook.com/invite/-MhmG2mhIIHTtQPuHV_k/acNI2LAF6LtdJW06t4Hc
/devserver https://discord.gg/N3XKxzTrq3
/dev https://audacity.gitbook.io/dev/
/nightly /beta
/nightly /next
/au4win https://nightly.link/audacity/audacity/workflows/au4_build_windows/master
/au4mac https://nightly.link/audacity/audacity/workflows/au4_build_macos/master
/au4lin https://nightly.link/audacity/audacity/workflows/au4_build_linux/master
@@ -29,6 +29,11 @@
/) /
/vpat /VPAT.pdf
# prerelease
/au4 /next 301
/alpha /next 301
/beta /next 301
# campaign links
/survey https://docs.google.com/forms/d/e/1FAIpQLSd42XPhC69bKUA9kbYiTXboRLaj9Q11_BUEXTK0v0TzWJQmDQ/viewform?usp=dialog
/audacitypromo https://www.youtube.com/watch?v=Lb7jx4wdXXE&mtm_campaign=audacityapp&mtm_content=welcomescreen_video

View File

@@ -1,16 +1,16 @@
export type ReleaseInfo = {
name: string;
browser_download_url: string;
checksum: string;
checksum?: string;
type: string;
};
type ReleaseDirectory = {
export type ReleaseDirectory = {
version: string;
win: ReleaseInfo[];
mac: ReleaseInfo[];
lin: ReleaseInfo[];
src: ReleaseInfo[];
src?: ReleaseInfo[];
};
export const audacityReleases: ReleaseDirectory = {
@@ -127,4 +127,161 @@ export const audacityReleases: ReleaseDirectory = {
type: ".tar.gz",
},
],
};
};
export const hasDownloadAssets = (downloads?: ReleaseDirectory): boolean => {
if (!downloads) {
return false;
}
const { win, mac, lin, src } = downloads;
return Boolean(
(win && win.length)
|| (mac && mac.length)
|| (lin && lin.length)
|| (src && src.length),
);
};
export type PreReleaseEntry = {
id: string;
label: string;
isActive: boolean;
summary: string;
pageHref: string;
downloads: ReleaseDirectory;
};
export const alphaPreRelease: PreReleaseEntry = {
id: "alpha",
label: "Alpha",
isActive: true,
summary:
"Get an early look at the next major release. Expect unfinished features and potential bugs.",
pageHref: "/next",
downloads: {
version: "Audacity 4 Alpha 1",
win: [
{
name: "64 bit",
browser_download_url:
"https://github.com/audacity/audacity/actions/runs/18406361889/artifacts/4237171895",
type: ".zip",
},
],
mac: [
{
name: "ARM 64 zip (Apple Silicon)",
browser_download_url:
"https://github.com/audacity/audacity/actions/runs/18406354692/artifacts/4237536953",
type: ".zip",
},
],
lin: [
{
name: "AppImage",
browser_download_url:
"https://github.com/audacity/audacity/actions/runs/18406368664/artifacts/4237050905",
type: ".zip",
},
],
src: [],
},
};
export const betaPreRelease: PreReleaseEntry = {
id: "beta",
label: "Beta",
isActive: false,
summary: "Help us test upcoming features before they ship.",
pageHref: "/next",
downloads: {
version: "3.5.0 beta",
win: [
{
name: "64 bit installer (recommended)",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-win-3.5.0-beta-3-64bit.exe",
checksum:
"ed7de964ed11cbc8f74e815dbcb2cb8487ba136818cf1a148f16cadd4c10f3d0",
type: ".exe",
},
{
name: "64 bit zip file",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-win-3.5.0-beta-3-64bit.zip",
checksum:
"52da5c3e2507408d72c4ab425c2e465e3c8ad452b2ac89ddfb3f5bc141d68a03",
type: ".zip",
},
{
name: "32 bit installer",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-win-3.5.0-beta-3-32bit.exe",
checksum:
"6bb6c0d3513be7d98c400f43d84cd39992065f4c6460d80b6cb1667733ca95c7",
type: ".exe",
},
{
name: "32 bit zip file",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-win-3.5.0-beta-3-32bit.zip",
checksum:
"c313ca3c475b487bf88a42537cbc9454090391250017fe210226b3ca78797d9a",
type: ".zip",
},
],
mac: [
{
name: "Universal dmg (recommended)",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-macOS-3.5.0-beta-3-universal.dmg",
checksum:
"9500ede91b837fc12e5106fa33d6603829288b90fb1e28d2d70bfee9db33406e",
type: ".dmg",
},
{
name: "ARM 64 dmg (Apple Silicon)",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-macOS-3.5.0-beta-3-arm64.dmg",
checksum:
"0f3e9b9ee8e77d8b8613db8d66927e982bbec870e801811060d3d8fbc25c7698",
type: ".dmg",
},
{
name: "x86_64 dmg (Intel)",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-macOS-3.5.0-beta-3-x86_64.dmg",
checksum:
"415342de27b572bd3801f51bd77e850a21701b39e2392c2c347ea8db4da4f122",
type: ".dmg",
},
],
lin: [
{
name: "AppImage",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-linux-3.5.0-beta-3-x64.AppImage",
checksum:
"fc5df63e3819f4f59b4366c436579bd1a73b045a4dae28316edf6c23948a06ce",
type: ".AppImage",
},
],
src: [
{
name: "Source code",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-sources-3.5.0-beta-3.tar.gz",
checksum:
"53c33ed875f4eb1501707d7989a6de253370a368c1c50a877e5cfa96c02bebdc",
type: ".tar.gz",
},
],
},
};
export const preReleaseList: PreReleaseEntry[] = [
alphaPreRelease,
betaPreRelease,
];

View File

@@ -1,98 +0,0 @@
export type ReleaseInfo = {
name: string;
browser_download_url: string;
checksum: string;
type: string;
};
type ReleaseDirectory = {
version: string;
win: ReleaseInfo[];
mac: ReleaseInfo[];
lin: ReleaseInfo[];
src: ReleaseInfo[];
};
export const betaReleases: ReleaseDirectory = {
version: "3.5.0 beta",
win: [
{
name: "64 bit installer (recommended)",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-win-3.5.0-beta-3-64bit.exe",
checksum:
"ed7de964ed11cbc8f74e815dbcb2cb8487ba136818cf1a148f16cadd4c10f3d0",
type: ".exe",
},
{
name: "64 bit zip file",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-win-3.5.0-beta-3-64bit.zip",
checksum:
"52da5c3e2507408d72c4ab425c2e465e3c8ad452b2ac89ddfb3f5bc141d68a03",
type: ".zip",
},
{
name: "32 bit installer",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-win-3.5.0-beta-3-32bit.exe",
checksum:
"6bb6c0d3513be7d98c400f43d84cd39992065f4c6460d80b6cb1667733ca95c7",
type: ".exe",
},
{
name: "32 bit zip file",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-win-3.5.0-beta-3-32bit.zip",
checksum:
"c313ca3c475b487bf88a42537cbc9454090391250017fe210226b3ca78797d9a",
type: ".zip",
},
],
mac: [
{
name: "Universal dmg (recommended)",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-macOS-3.5.0-beta-3-universal.dmg",
checksum:
"9500ede91b837fc12e5106fa33d6603829288b90fb1e28d2d70bfee9db33406e",
type: ".dmg",
},
{
name: "ARM 64 dmg (Apple Silicon)",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-macOS-3.5.0-beta-3-arm64.dmg",
checksum:
"0f3e9b9ee8e77d8b8613db8d66927e982bbec870e801811060d3d8fbc25c7698",
type: ".dmg",
},
{
name: "x86_64 dmg (Intel)",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-macOS-3.5.0-beta-3-x86_64.dmg",
checksum:
"415342de27b572bd3801f51bd77e850a21701b39e2392c2c347ea8db4da4f122",
type: ".dmg",
},
],
lin: [
{
name: "AppImage",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-linux-3.5.0-beta-3-x64.AppImage",
checksum:
"fc5df63e3819f4f59b4366c436579bd1a73b045a4dae28316edf6c23948a06ce",
type: ".AppImage",
},
],
src: [
{
name: "Source code",
browser_download_url:
"https://github.com/audacity/audacity/releases/download/Audacity-3.5.0-beta-3/audacity-sources-3.5.0-beta-3.tar.gz",
checksum:
"53c33ed875f4eb1501707d7989a6de253370a368c1c50a877e5cfa96c02bebdc",
type: ".tar.gz",
},
],
};

View File

@@ -0,0 +1,106 @@
export type PromoData = {
isActive?: boolean;
priority?: number;
osTargets?: string[];
suppressOnPaths?: string[];
message: string;
styles?: {
container?: string;
message?: string;
button?: string;
};
tracking?: {
category: string;
action: string;
name: string;
};
cta?: {
text: string;
link: string;
};
};
const promoData: Record<string, PromoData> = {
audacity4Alpha: {
isActive: true,
priority: 50,
suppressOnPaths: ["/next", "/download"],
message: "Want a peek at our next big release?",
cta: {
text: "Try the Audacity 4 Alpha",
link: "/next",
},
tracking: {
category: "Promo CTA",
action: "Promo CTA button",
name: "Audacity 4 Alpha",
},
styles: {
container: "bg-[#0f004d]",
message: "text-gray-100",
button: "bg-[#ff3254] hover:bg-[#ff1a3c] text-white",
},
},
voiceByAuribus: {
isActive: true,
priority: 50,
osTargets: ["Windows", "OS X"],
message:
"AI powered professional vocals. Transform any track with Voice by Auribus!",
styles: {
container: "bg-yellow-300",
message: "text-gray-900",
button: "bg-gray-100 hover:bg-white",
},
tracking: {
category: "Promo CTA",
action: "Promo CTA button",
name: "Voice by Auribus Muse Hub",
},
cta: {
text: "Get it on MuseHub",
link: "https://www.musehub.com/plugin/auribus?utm_source=au-web&utm_medium=au-banner&utm_campaign=au-web-mh-web-auribus",
},
},
ampknob: {
isActive: false,
osTargets: ["Windows", "OS X"],
message: "Heavy guitar tone in seconds. One knob, no distractions.",
styles: {
container: "bg-yellow-300",
message: "text-gray-900 font-bold",
button:
"font-bold border-2 border-gray-900 bg-gray-900 text-white hover:bg-yellow-300 hover:text-gray-900 hover:border-gray-900",
},
tracking: {
category: "Promo CTA",
action: "Promo CTA button",
name: "Ampknob Revc Muse Hub",
},
cta: {
text: "Try for free",
link: "https://www.musehub.com/plugin/ampknob-revc?utm_source=audacity&utm_medium=web&utm_campaign=auampknob-revc",
},
},
survey: {
isActive: false,
message: "3 minute survey:\nHelp us understand what features you want next",
styles: {
container: "bg-yellow-300",
message: "text-lg text-gray-900",
button:
"h-10 bg-gray-100 hover:bg-white border border-gray-900 text-gray-900",
},
tracking: {
category: "Promo CTA",
action: "Survey CTA button",
name: "Go to Survey",
},
cta: {
text: "Take the survey",
link: "https://docs.google.com/forms/d/e/1FAIpQLScxH_f64JPCWt5nwqa8MTPXfmi453mqYwy1xZFPF_mx9mYkNw/viewform",
},
},
};
export default promoData;

View File

@@ -1,43 +0,0 @@
import React from "react";
import "../../styles/icons.css";
import { trackEvent } from "../../utils/matomo";
function BetaBanner(url) {
//no beta at the moment
return null;
function handleButtonClick() {
trackEvent("Beta CTA", "Beta CTA button", "Go to Beta site");
}
if (url.url.endsWith("/beta/") || url.url.endsWith("/beta")) {
return null;
} else
return (
<div
id="beta-banner"
className="flex items-center justify-center min-h-24 bg-orange-400 gap-4 flex-wrap"
>
<div className="flex gap-2 flex-wrap my-4 mx-2">
<p className="text-xl font-bold text-gray-900">
Get early access to new features!
</p>
<p className="text-xl text-gray-900">
Help us test our Save to cloud feature
</p>
</div>
<a
href="/beta"
id="join-button"
onClick={() => {
handleButtonClick();
}}
className="flex text-xl h-12 my-4 justify-center items-center px-4 border-2 border-gray-900 rounded-md hover:bg-gray-900 hover:text-white"
>
Join the beta
</a>
</div>
);
}
export default BetaBanner;

View File

@@ -1,55 +1,220 @@
import { museHubReleases } from "../../assets/data/museHubReleases";
import "../../styles/icons.css";
import cx from "classnames";
import promoData from "../../assets/data/promotions";
import type { PromoData } from "../../assets/data/promotions";
import useBrowserOS from "../../hooks/useDetectOS";
import "../../styles/icons.css";
import { trackEvent } from "../../utils/matomo";
import { useEffect, useMemo, useRef, useState } from "react";
function PromoBanner() {
// no promo atm
//return false;
const DEFAULT_PROMO_STYLES: NonNullable<PromoData["styles"]> = {
container: "bg-yellow-300",
message: "text-gray-900",
button: "bg-gray-100 hover:bg-white",
};
const browserOS = useBrowserOS();
const BASE_CONTAINER_CLASSNAME =
"flex flex-col lg:flex-row justify-center items-center align-start py-4 gap-3 lg:gap-6 transition-colors duration-200";
const BASE_MESSAGE_CLASSNAME = "text-lg font-semibold";
const BASE_BUTTON_CLASSNAME =
"flex h-8 justify-center items-center px-4 rounded-md font-semibold";
// Only show the banner for supported OSes
const showBanner = browserOS === "OS X" || browserOS === "Windows";
const PLACEHOLDER_CONTAINER_CLASSNAME =
"flex flex-col lg:flex-row justify-center items-center align-start py-4 gap-3 lg:gap-6 transition-colors duration-200 opacity-0 pointer-events-none";
const getHref = () => {
if (showBanner) {
return "https://www.musehub.com/plugin/ampknob-revc?utm_source=audacity&utm_medium=web&utm_campaign=auampknob-revc";
} else {
return "#"; // Default if OS is not supported
type PromoBannerProps = {
requestPath?: string;
};
const STATIC_PROMOS: PromoData[] = Object.values(promoData);
const isPromoActive = (promo: PromoData | null | undefined) =>
promo?.isActive ?? true;
const isSuppressedOnPath = (promo: PromoData, path: string | null) => {
if (!path) {
return false;
}
const suppressedPaths = promo.suppressOnPaths ?? [];
return suppressedPaths.includes(path);
};
const getHighestPriorityPromo = (promos: PromoData[]) =>
promos.reduce<PromoData | null>((selected, current) => {
if (!current) {
return selected;
}
};
if (!selected) {
return current;
}
const currentPriority = current.priority ?? 0;
const selectedPriority = selected.priority ?? 0;
return currentPriority > selectedPriority ? current : selected;
}, null);
const getEligiblePromos = (promos: PromoData[], os: string | null) =>
promos.filter((promo) => {
if (!isPromoActive(promo)) {
return false;
}
if (!promo.osTargets || promo.osTargets.length === 0) {
return true;
}
if (!os) {
return false;
}
return promo.osTargets.includes(os);
});
const selectWeightedPromo = (promos: PromoData[]) => {
if (promos.length === 0) {
return null;
}
const weights = promos.map((promo) => Math.max(promo.priority ?? 1, 0));
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
if (totalWeight <= 0) {
return getHighestPriorityPromo(promos);
}
let threshold = Math.random() * totalWeight;
for (let index = 0; index < promos.length; index += 1) {
threshold -= weights[index];
if (threshold <= 0) {
return promos[index];
}
}
return promos[promos.length - 1] ?? null;
};
const buildPromoList = (path: string | null): PromoData[] =>
STATIC_PROMOS.filter((promo) => !isSuppressedOnPath(promo, path));
const PromoBanner: React.FC<PromoBannerProps> = ({ requestPath }) => {
const browserOS = useBrowserOS();
const [selectedPromo, setSelectedPromo] = useState<PromoData | null>(null);
const [isReady, setIsReady] = useState(false);
const hasSelected = useRef(false);
const [shouldReserveSpace, setShouldReserveSpace] = useState<boolean>(() => {
const pathForEval =
typeof window !== "undefined"
? window.location.pathname
: requestPath ?? null;
const promos = buildPromoList(pathForEval);
return promos.some((promo) => isPromoActive(promo) && Boolean(promo.cta));
});
const initialPath = useMemo(() => {
if (typeof window !== "undefined") {
return window.location.pathname;
}
return requestPath ?? null;
}, [requestPath]);
useEffect(() => {
if (hasSelected.current) {
return;
}
const pathName =
typeof window !== "undefined" ? window.location.pathname : initialPath;
if (typeof window !== "undefined" && browserOS === null) {
return;
}
const promos = buildPromoList(pathName);
if (promos.length === 0) {
hasSelected.current = true;
setSelectedPromo(null);
setIsReady(true);
setShouldReserveSpace(false);
return;
}
const eligiblePromos = getEligiblePromos(promos, browserOS);
const fallbackPromos = promos.filter((promo) => isPromoActive(promo));
const selectionPool =
eligiblePromos.length > 0 ? eligiblePromos : fallbackPromos;
if (selectionPool.length === 0) {
hasSelected.current = true;
setSelectedPromo(null);
setIsReady(true);
setShouldReserveSpace(false);
return;
}
const promo = selectWeightedPromo(selectionPool);
hasSelected.current = true;
setSelectedPromo(promo && promo.cta ? promo : null);
setIsReady(true);
setShouldReserveSpace(true);
}, [browserOS, initialPath]);
if (!isReady && shouldReserveSpace) {
return (
<div className={PLACEHOLDER_CONTAINER_CLASSNAME} aria-hidden="true">
<div className="lg:flex text-center gap-4 flex-wrap justify-center">
<p className={BASE_MESSAGE_CLASSNAME}>&nbsp;</p>
</div>
<span className={BASE_BUTTON_CLASSNAME}>&nbsp;</span>
</div>
);
}
if (!isReady) {
return null;
}
if (!selectedPromo || !selectedPromo.cta) {
return null;
}
const { tracking, cta, message, styles } = selectedPromo;
const containerClassName = cx(
BASE_CONTAINER_CLASSNAME,
styles?.container ?? DEFAULT_PROMO_STYLES.container
);
const messageClassName = cx(
BASE_MESSAGE_CLASSNAME,
styles?.message ?? DEFAULT_PROMO_STYLES.message
);
const buttonClassName = cx(
BASE_BUTTON_CLASSNAME,
styles?.button ?? DEFAULT_PROMO_STYLES.button
);
const trimmedMessage = message.trim();
function handleButtonClick() {
trackEvent("Promo CTA", "Promo CTA button", "Ampknob Revc Muse Hub");
if (!tracking) return;
trackEvent(tracking.category, tracking.action, tracking.name);
}
return (
<>
{showBanner && (
<div
id="promo-banner"
className="flex flex-col lg:flex-row justify-center items-center align-start min-h-24 bg-yellow-300 py-2 gap-4 lg:gap-8"
>
<div className="lg:flex text-center gap-4">
<p className="text-lg text-gray-900 font-bold">
Heavy guitar tone in seconds. One knob, no distractions.
</p>
{
<div id="promo-banner" className={containerClassName}>
<div className="lg:flex text-center gap-4 flex-wrap justify-center">
<p className={messageClassName}>{trimmedMessage}</p>
</div>
<a
href={getHref()}
href={cta.link}
id="promo-button"
onClick={handleButtonClick}
className="flex text-lg font-bold h-8 justify-center items-center px-4 border-2 border-gray-900 bg-gray-900 rounded-md hover:bg-yellow-300 text-white hover:text-gray-900 hover:border-gray-900"
className={buttonClassName}
>
Try for free
{cta.text}
</a>
</div>
)}
}
</>
);
}
};
export default PromoBanner;

View File

@@ -1,38 +0,0 @@
import React from "react";
import "../../styles/icons.css";
import { trackEvent } from "../../utils/matomo";
function SurveyBanner(url) {
//no survey going on at the moment
return null;
function handleButtonClick() {
trackEvent("Survey CTA", "Survey CTA button", "Go to Survey");
}
return (
<div
id="survey-banner"
className="flex items-center justify-center min-h-24 bg-yellow-300 gap-4 flex-wrap"
>
<div className="flex gap-2 flex-wrap my-4 mx-2">
<p className="text-lg font-bold text-gray-900">3 minute survey:</p>
<p className="text-lg text-gray-900">
Help us understand what features you want in Audacity and Audio.com next
</p>
</div>
<a
href="https://docs.google.com/forms/d/e/1FAIpQLScxH_f64JPCWt5nwqa8MTPXfmi453mqYwy1xZFPF_mx9mYkNw/viewform"
id="survey-button"
onClick={() => {
handleButtonClick();
}}
className="flex text-lg h-12 my-4 justify-center items-center px-4 border-2 border-gray-900 rounded-md hover:bg-gray-900 hover:text-white"
>
Take the survey
</a>
</div>
);
}
export default SurveyBanner;

View File

@@ -1,5 +1,9 @@
---
import "../../styles/fonts.css";
import {
hasDownloadAssets,
preReleaseList,
} from "../../assets/data/audacityReleases";
import MuseHubSVG from "../inlineSVG/MuseHubSVG";
import MuseGroupSVG from '../inlineSVG/MuseGroupSVG';
import AudioSVG from '../inlineSVG/AudioSVG';
@@ -44,6 +48,14 @@ const legal = [
{ href: "/privacy-policy", label: "Privacy policy" },
{ href: "/brand-kit", label: "Brand kit" },
];
const activeDownloadCampaign = preReleaseList.find((entry) =>
entry.isActive && hasDownloadAssets(entry.downloads)
) ?? null;
const hasPreRelease = Boolean(activeDownloadCampaign);
const activePreReleaseAriaLabel = hasPreRelease && activeDownloadCampaign
? `Link to ${activeDownloadCampaign.label.toLowerCase()} page`
: "";
---
<footer class="bg-background-dark py-12">

View File

@@ -0,0 +1,75 @@
---
---
<div class="mt-6">
<p>
The purpose of this alpha build is to test the core features of Audacity 4 on a
wide selection of hardware and operating systems.
</p>
<p>
If you experience severe issues or crashes, please file an issue on GitHub
(requires a free GitHub account).
</p>
<ul class="py-2 list-none">
<li>
<a href="https://github.com/audacity/audacity/issues" class="hyperlink">
Github issues
</a>
</li>
</ul>
<h2 class="mt-6 text-2xl">What's implemented</h2>
<ul class="list-disc mt-2 ml-6">
<li>Recording and playback</li>
<li>Editing audio</li>
<li>Applying effects (destructive and real-time)</li>
<li>Exporting audio files</li>
<li>
Configuring and re-arranging the interface (including the new Workspaces
feature)
</li>
</ul>
<h3 class="mt-2">A note on compatibility ⚠️</h3>
<p class="mt-2">
Projects saved in Audacity 4 are not backwards compatible. We recommend you make
a copy of your important projects before opening them in Audacity 4.
</p>
<h2 class="mt-6 text-2xl">Unimplemented features</h2>
<p class="mt-2">
Some yet-to-be finalized features might appear non-functional, or have been
excluded from Audacity's interface. These are features not yet ready for user
testing, and have been disabled.
</p>
<ul class="list-disc mt-2 ml-6">
<li>Nyquist, LADSPA and VAMP and the OpenVINO plugins</li>
<li>Preferences from Audacity 3 are not carried over</li>
<li>Envelopes and label tracks</li>
<li>Spectrogram view and the spectral editing mode</li>
<li>Most built-in effects, including generators and analyzers</li>
<li>Opening multiple projects at the same time</li>
</ul>
<div>
<h2 class="mt-4 text-2xl">Submit your feedback!</h2>
<p class="mt-2">
We're always eager to hear what you have to say, but it's especially important
we hear from you during this early phase. Please let us know what you think,
using the following places:
</p>
<ul class="py-2 list-none">
<li class="hyperlink">
<a href="https://forum.audacityteam.org/c/au4/64">Audacity Forum</a>
</li>
<li class="hyperlink">
<a href="https://discord.gg/audacity">Audacity Discord</a>
</li>
<li>
<a href="https://github.com/audacity/audacity/issues" class="hyperlink">
Github issues
</a>
</li>
</ul>
</div>
</div>

View File

@@ -0,0 +1,17 @@
---
---
<div class="my-6">
<p>
Download the latest beta build and help us test upcoming Audacity features.
</p>
<p class="my-2">
If you'd like to help us with testing, instructions can be found below.
</p>
<div class="flex items-center justify-center w-fit rounded-md text-white my-6">
<a href="https://support.audacityteam.org/community/contributing/testing">
<div class="flex items-center gap-3 h-10 pl-4 pr-3 rounded-md bg-blue-700 hover:bg-blue-600">
Instructions for testing
</div>
</a>
</div>
</div>

8
src/env.d.ts vendored
View File

@@ -1,2 +1,10 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
interface ImportMetaEnv {
readonly NETLIFY_CONTEXT?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View File

@@ -1,24 +1,32 @@
---
import promoData from "../assets/data/promotions";
import CookieConsent from "../components/banner/CookieConsent";
import PromoBanner from "../components/banner/PromoBanner";
import Footer from "../components/footer/Footer.astro";
import NavigationReact from "../components/navigation/NavigationReact";
import CookieConsent from "../components/banner/CookieConsent";
import BetaBanner from "../components/banner/BetaBanner";
import SurveyBanner from "../components/banner/SurveyBanner";
import PromoBanner from "../components/banner/PromoBanner";
import useBrowserOS from "../hooks/useDetectOS";
export interface Props {
title: string;
description?: string;
index?: string;
showPromoBanner?: boolean;
}
const {
title = "Audacity | Free Audio editor, recorder, music making and more!",
description = "Audacity is the world's most popular audio editing and recording app. Edit, mix, and enhance your audio tracks with the power of Audacity. Download now!",
index = "all",
showPromoBanner = true,
} = Astro.props;
const isNoAdPage = Astro.request.url.includes("/post-download") || Astro.request.url.includes("/au4");
const isNoAdPage =
Astro.request.url.includes("/post-download") ||
Astro.request.url.includes("/next");
const hasActiveStaticPromo = Object.values(promoData).some((promo) => {
const isActive = promo.isActive ?? true;
return isActive && Boolean(promo.cta);
});
const shouldShowPromoBanner =
showPromoBanner && !isNoAdPage && hasActiveStaticPromo;
---
<!doctype html>
@@ -26,7 +34,7 @@ const isNoAdPage = Astro.request.url.includes("/post-download") || Astro.request
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<style is:global>
@import "../styles/input.css";
</style>
@@ -53,9 +61,11 @@ const isNoAdPage = Astro.request.url.includes("/post-download") || Astro.request
>
<header class="z-50 sticky left-0 right-0 top-0">
<NavigationReact client:load currentURL={Astro.request.url} />
{!isNoAdPage && <BetaBanner client:load url={Astro.request.url} />}
{!isNoAdPage && <SurveyBanner client:load url={Astro.request.url} />}
{!isNoAdPage && <PromoBanner client:load />}
{
shouldShowPromoBanner && (
<PromoBanner client:load requestPath={Astro.url.pathname} />
)
}
</header>
<slot />

View File

@@ -1,149 +0,0 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import "../styles/icons.css";
import { betaReleases } from "../assets/data/betaReleases";
import SplitDownloadButton from "../components/button/SplitDownloadButton.jsx";
import FeaturedVideo from "../components/video/FeaturedVideo";
const { win, mac, lin } = betaReleases;
---
<BaseLayout
title="Audacity 4 alpha download"
description="Test the latest Audacity 4 alpha for Windows, macOS and Linux"
index="none"
>
<main id="main" class="max-w-screen-xl mx-auto text-text-primary">
<div class="grid grid-cols-12 pt-8 pb-32 gap-y-12">
<section class="col-start-2 col-span-10 sm:col-start-2 sm:col-span-5">
<h1>Audacity&nbsp;4 Alpha 1</h1>
<div class="mt-4">
<p>
The purpose of this alpha build is to test the core features of
Audacity 4 on a wide selection of hardware and operating systems.
</p>
<p>
If you experience severe issues or crashes, please file an issue on
GitHub (requires a free GitHub account).
</p>
<ul class="py-2 list-none">
<li>
<a
href="https://github.com/audacity/audacity/issues"
class="hyperlink"
>
Github issues
</a>
</li>
</ul>
<h2 class="mt-6 text-2xl">What's implemented</h2>
<ul class="list-disc mt-2 ml-6">
<li>Recording and playback</li>
<li>Editing audio</li>
<li>Applying effects (destructive and real-time)</li>
<li>Exporting audio files</li>
<li>
Configuring and re-arranging the interface (including the new
Workspaces feature)
</li>
</ul>
<h3 class="mt-2 text-18 lg:text-24 text-text-primary font-semibold">A note on compatibility ⚠️</h3>
<p class="mt-2">
Projects saved in Audacity 4 are not backwards compatible. We
recommend you make a copy of your important projects before opening
them in Audacity 4.
</p>
<h2 class="mt-6 text-2xl">Unimplemented features</h2>
<p class="mt-2">
Some yet-to-be finalized features might appear non-functional, or
have been excluded from Audacity's interface. These are features not
yet ready for user testing, and have been disabled.
</p>
<ul class="list-disc mt-2 ml-6">
<li>Nyquist, LADSPA and VAMP and the OpenVINO plugins</li>
<li>Preferences from Audacity 3 are not carried over</li>
<li>Envelopes and label tracks</li>
<li>Spectrogram view and the spectral editing mode</li>
<li>Most built-in effects, including generators and analyzers</li>
<li>Opening multiple projects at the same time</li>
</ul>
<div>
<h2 class="mt-4 text-2xl">Submit your feedback!</h2>
<p class="mt-2">
We're always eager to hear what you have to say, but it's
especially important we hear from you during this early phase.
Please let us know what you think, using the following places:
</p>
<ul class="py-2 list-none">
<li class="hyperlink">
<a href="https://forum.audacityteam.org/c/au4/64"
>Audacity Forum</a
>
</li>
<li class="hyperlink">
<a href="https://discord.gg/audacity">Audacity Discord</a>
</li>
<li>
<a
href="https://github.com/audacity/audacity/issues"
class="hyperlink"
>
Github issues
</a>
</li>
</ul>
</div>
</div>
<FeaturedVideo
client:load
placeholderImage="https://i.ytimg.com/vi/QYM3TWf_G38/maxresdefault.jpg"
imageAltText="Video: How we made Audacity 4"
videoURL="https://www.youtube-nocookie.com/embed/QYM3TWf_G38?autoplay=1"
title="Video: How we made Audacity 4"
label="Watch a behind-the-scenes video on the making of Audacity 4"
class="mt-8"
/>
</section>
<aside
class="row-start-2 sm:row-start-1 col-start-2 col-span-10 sm:col-start-8 sm:col-span-4"
>
<h2 class="text-12 uppercase font-normal text-text-primary">Download links</h2>
<div class="flex flex-col gap-6 mt-2">
Coming soon...
<!--
<div class="border border-bg-200 rounded-md p-6">
<h3 class="additional-resource-heading">
Audacity 4 alpha 1 for Windows
</h3>
<div class="mt-2">
<SplitDownloadButton OS="Windows" releaseData={win} client:load />
</div>
</div>
<div class="border border-bg-200 rounded-md p-6">
<h3 class="additional-resource-heading">
Audacity 4 alpha 1 for macOS
</h3>
<div class="mt-2">
<SplitDownloadButton OS="macOS" releaseData={mac} client:load />
</div>
</div>
<div class="border border-bg-200 rounded-md p-6">
<h3 class="additional-resource-heading">
Audacity 4 alpha 1 for Linux
</h3>
<div class="mt-2">
<SplitDownloadButton OS="Linux" releaseData={lin} client:load />
</div>
</div>-->
</div>
</aside>
</div>
</main></BaseLayout
>

View File

@@ -1,66 +0,0 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import "../styles/icons.css";
import { betaReleases } from "../assets/data/betaReleases";
import SplitDownloadButton from "../components/button/SplitDownloadButton.jsx";
const { win, mac, lin } = betaReleases;
---
<BaseLayout
title="Audacity ® | Beta download"
description="Test the latest Audacity Beta for Windows, macOS and Linux"
>
<main id="main" class="max-w-screen-xl mx-auto">
<div class="grid grid-cols-12 pt-8 sm:pt-20 pb-32 gap-y-12">
<section class="col-start-2 col-span-10 sm:col-start-2 sm:col-span-5">
<div>
<h1>Audacity Beta releases</h1>
<div class="my-6">
<p>
<strong>There currently is no beta release available.</strong> You
can download an pre-alpha version instead.
</p>
<p class="my-2">
If you'd like to help us with testing, instructions can be found
below.
</p>
<div
class="flex items-center justify-center w-fit rounded-md text-white my-6"
>
<a
href="https://support.audacityteam.org/community/contributing/testing"
>
<div
class="flex items-center gap-3 h-10 pl-4 pr-3 rounded-md bg-blue-700 hover:bg-blue-600"
>
Instructions for testing
</div></a
>
</div>
</div>
</div>
<h2 class="mt-4 text-20 lg:text-32 text-text-primary font-semibold leading-tight">Audacity 4 alpha</h2>
<div class="flex gap-4">
<div
class="flex items-center justify-center w-fit rounded-md text-white my-6"
>
<a href="/au4">
<div
class="flex items-center gap-3 h-10 pl-4 pr-3 rounded-md bg-blue-700 hover:bg-blue-600"
>
Go to Audacity 4 alpha 1 page
</div></a
>
</div>
</div>
<p>
<strong
>The Audacity 4 alpha versions are unfinished at this time.</strong
> This means that some buttons may not do anything yet, or placeholder
items be used. For details, check the alpha 1 page.
</p>
</section>
</div>
</main>
</BaseLayout>

View File

@@ -2,14 +2,40 @@
import BaseLayout from "../layouts/BaseLayout.astro";
import OperatingSystemCard from "../components/card/OperatingSystemCard";
import "../styles/icons.css";
import { audacityReleases } from "../assets/data/audacityReleases";
import {
audacityReleases,
hasDownloadAssets,
preReleaseList,
type PreReleaseEntry,
} from "../assets/data/audacityReleases";
import ChecksumAccordion from "../components/accordion/ChecksumAccordion";
const { version, src } = audacityReleases;
const { version } = audacityReleases;
const sourceDownloads = audacityReleases.src ?? [];
const primarySourceDownload = sourceDownloads[0] ?? null;
const activeDownloadEntries = preReleaseList.filter(
(entry) => entry.isActive && hasDownloadAssets(entry.downloads),
);
const hasDownloadCampaigns = activeDownloadEntries.length > 0;
const formatCampaignLinkLabel = (
entry: PreReleaseEntry,
platform: string,
buildName?: string,
) =>
`${entry.downloads.version} for ${platform}${
buildName ? ` (${buildName})` : ""
}`;
---
<BaseLayout title="Audacity ® | Downloads" description="Download Audacity for Windows, macOS and Linux">
<main id="main" class="max-w-screen-xl mx-auto flex-1 flex flex-col min-h-[calc(100vh-471px)]">
<BaseLayout
title="Audacity ® | Downloads"
description="Download Audacity for Windows, macOS and Linux"
>
<main
id="main"
class="max-w-screen-xl mx-auto flex-1 flex flex-col min-h-[calc(100vh-471px)]"
>
<div class="grid grid-cols-12 py-12 gap-y-12">
<div class="col-start-2 col-span-10 sm:text-center">
<h1>Downloads</h1>
@@ -48,7 +74,78 @@ const { version, src } = audacityReleases;
</div>
</section>
<aside class="col-start-2 col-span-10 sm:col-start-8 sm:col-span-3">
<h2 class="text-12 uppercase font-normal text-text-primary">
{hasDownloadCampaigns && (
<div>
<h2 class="text-12 uppercase font-normal text-text-primary">
Pre-release builds
</h2>
<div class="flex flex-col gap-6 mt-2">
{activeDownloadEntries.map((entry) => {
const downloads = entry.downloads;
const windowsBuilds = downloads.win ?? [];
const macBuilds = downloads.mac ?? [];
const linuxBuilds = downloads.lin ?? [];
const versionLabel = downloads.version;
return (
<div class="flex flex-col gap-3">
<h3 class="text-base font-semibold text-gray-900">
{versionLabel}
</h3>
<p class="text-sm text-gray-700">
{entry.summary}
</p>
<ul class="flex flex-col gap-2 text-sm">
{
windowsBuilds.map((build) => (
<li>
<a
class="hyperlink"
href={build.browser_download_url}
>
{formatCampaignLinkLabel(entry, "Windows", build.name)}
</a>
</li>
))
}
{
macBuilds.map((build) => (
<li>
<a
class="hyperlink"
href={build.browser_download_url}
>
{formatCampaignLinkLabel(entry, "macOS", build.name)}
</a>
</li>
))
}
{
linuxBuilds.map((build) => (
<li>
<a
class="hyperlink"
href={build.browser_download_url}
>
{formatCampaignLinkLabel(entry, "Linux", build.name)}
</a>
</li>
))
}
</ul>
<div class="flex items-center gap-1">
<a href={entry.pageHref} class="hyperlink text-sm">
{`Learn more about ${versionLabel}`}
</a>
<span class="align-middle icon icon-share text-blue-600"></span>
</div>
</div>
);
})}
</div>
</div>
)}
<h2 class="text-12 uppercase font-normal text-text-primary mt-10">
Additional resources
</h2>
<div class="flex flex-col gap-6 mt-2">
@@ -78,13 +175,19 @@ const { version, src } = audacityReleases;
<div>
<h3 class="additional-resource-heading">Source code</h3>
<div class="flex flex-col gap-2">
<!-- TODO - Ensure accordions are inclueded in the tab hierarchy -->
<ChecksumAccordion
title={src[0].type}
downloadUrl={src[0].browser_download_url}
checksum={src[0].checksum}
client:load
/>
<!-- TODO - Ensure accordions are included in the tab hierarchy -->
{primarySourceDownload ? (
<ChecksumAccordion
title={primarySourceDownload.type}
downloadUrl={primarySourceDownload.browser_download_url}
checksum={primarySourceDownload.checksum}
client:load
/>
) : (
<p class="text-sm text-gray-700">
Source code packages are not available right now.
</p>
)}
</div>
</div>

166
src/pages/next.astro Normal file
View File

@@ -0,0 +1,166 @@
---
import {
alphaPreRelease,
betaPreRelease,
} from "../assets/data/audacityReleases";
import SplitDownloadButton from "../components/button/SplitDownloadButton.jsx";
import FeaturedVideo from "../components/video/FeaturedVideo";
import AlphaDetails from "../components/next/AlphaDetails.astro";
import BetaDetails from "../components/next/BetaDetails.astro";
import BaseLayout from "../layouts/BaseLayout.astro";
import "../styles/icons.css";
const pageTitle = "Audacity ® | Pre-release downloads";
const pageDescription =
"Test upcoming Audacity builds for Windows, macOS, and Linux.";
const releaseConfigurations = [
{
entry: betaPreRelease,
Details: BetaDetails,
},
{
entry: alphaPreRelease,
Details: AlphaDetails,
},
];
const activeConfig =
releaseConfigurations.find(({ entry }) => entry.isActive) ?? null;
const activeRelease = activeConfig?.entry ?? null;
const DetailsComponent = activeConfig?.Details ?? null;
const releaseDownloads = activeRelease?.downloads ?? null;
const releaseVersion = releaseDownloads?.version ?? "";
const windowsDownloads = releaseDownloads?.win ?? [];
const macDownloads = releaseDownloads?.mac ?? [];
const linuxDownloads = releaseDownloads?.lin ?? [];
const isAlphaRelease = activeRelease?.id === "alpha";
const downloadSections = activeRelease
? [
{
id: "windows",
title: `${releaseVersion} for Windows`,
os: "Windows",
entries: windowsDownloads,
},
{
id: "mac",
title: `${releaseVersion} for macOS`,
os: "macOS",
entries: macDownloads,
},
{
id: "linux",
title: `${releaseVersion} for Linux`,
os: "Linux",
entries: linuxDownloads,
},
].filter((section) => section.entries.length > 0)
: [];
---
<BaseLayout
title={pageTitle}
description={pageDescription}
showPromoBanner={false}
>
<main id="main" class="max-w-screen-xl mx-auto text-gray-700">
<div class="grid grid-cols-12 pt-8 pb-32 gap-y-12">
<section class="col-start-2 col-span-10 sm:col-start-2 sm:col-span-5">
{
activeRelease ? (
<>
<h1>{releaseVersion}</h1>
{DetailsComponent && <DetailsComponent />}
{isAlphaRelease && (
<FeaturedVideo
client:load
placeholderImage="https://i.ytimg.com/vi/QYM3TWf_G38/maxresdefault.jpg"
imageAltText="Video: How we made Audacity 4"
videoURL="https://www.youtube-nocookie.com/embed/QYM3TWf_G38?autoplay=1"
title="Video: How we made Audacity 4"
label="Watch a behind-the-scenes video on the making of Audacity 4"
class="mt-8"
/>
)}
</>
) : (
<div class="border border-yellow-300 bg-yellow-50 text-yellow-900 rounded-md p-6">
<h1 class="text-3xl font-semibold text-yellow-900">
Pre-release downloads are currently unavailable.
</h1>
<p class="mt-4">
Check back soon or download the latest stable version of Audacity.
</p>
<a
class="hyperlink inline-flex items-center gap-1 mt-4"
href="/download"
>
Browse stable downloads
<span class="align-middle icon icon-share text-blue-600" />
</a>
</div>
)
}
</section>
<aside
class="row-start-2 sm:row-start-1 col-start-2 col-span-10 sm:col-start-8 sm:col-span-4"
>
<h2 class="text-sm uppercase font-normal">Download links</h2>
{
activeRelease ? (
downloadSections.length > 0 ? (
<div class="flex flex-col gap-6 mt-2">
{downloadSections.map((section) => (
<div class="border border-bg-200 rounded-md p-6">
<h3 class="additional-resource-heading">
{section.title}
</h3>
<div class="mt-2">
<SplitDownloadButton
OS={section.os}
releaseData={section.entries}
client:load
/>
</div>
</div>
))}
</div>
) : (
<div class="mt-4 p-4 border border-gray-200 rounded-md bg-gray-50">
<p class="text-sm text-gray-700">
Download packages for this pre-release are not available right now.
</p>
<a
class="hyperlink text-sm inline-flex items-center gap-1 mt-3"
href="/download"
>
Browse stable downloads
<span class="align-middle icon icon-share text-blue-600" />
</a>
</div>
)
) : (
<div class="mt-4 p-4 border border-gray-200 rounded-md bg-gray-50">
<p class="text-sm text-gray-700">
Pre-release downloads are currently unavailable.
</p>
<p class="text-sm text-gray-700 mt-1">
You can still download the latest stable version of Audacity.
</p>
<a
class="hyperlink text-sm inline-flex items-center gap-1 mt-3"
href="/download"
>
Browse stable downloads
<span class="align-middle icon icon-share text-blue-600" />
</a>
</div>
)
}
</aside>
</div>
</main>
</BaseLayout>