From 5d4a783c64682d05f008abcd9ae2743a6832fa7c Mon Sep 17 00:00:00 2001 From: LWinterberg <87814144+LWinterberg@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:45:57 +0200 Subject: [PATCH 1/9] refactor: split up releaseData into separate files --- src/pages/au4a1.astro | 130 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/pages/au4a1.astro diff --git a/src/pages/au4a1.astro b/src/pages/au4a1.astro new file mode 100644 index 0000000..9005d9f --- /dev/null +++ b/src/pages/au4a1.astro @@ -0,0 +1,130 @@ +--- +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; +--- + + + + +
+
+
+
+

Help us test the first Audacity 4 alpha!

+
+

+ The first alpha version of Audacity 4 is intended as an early preview and feedback opportunity on some core interactions we have designed. +

+ +

What should work

+

We expect the following flow to work reasonably well:

+
    +
  • Recording, generating and importing audio
  • +
  • The core editing flow of applying effects (destructive or realtime), making cuts, moving clips and such.
  • +
  • Exporting (without metadata)
  • +
  • Customization of the app (themes, and editing/moving the toolbar)
  • +
+
+

What doesn't work yet

+

We haven't completed all development tasks for the full app yet. As such, a lot of features are missing, or appear disabled for the moment. We expect the following things to not work right now:

+
    +
  • Various menu items and buttons haven't been hooked up to functionality yet, you'll see them greyed out.
  • +
  • Various plugins – Nyquist, LADSPA and VAMP and the OpenVINO plugins – have not been ported over yet.
  • +
  • Preferences from Audacity 3 are not carried over.
  • +
  • Some more advanced features, like envelopes, labels, spectrograms aren't available yet.
  • +
  • Most effects/generators/analyzers aren't available yet.
  • +
  • Opening multiple projects at the same time is not supported yet.
  • +
+
+ +
+

Submit your feedback!

+

+ 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: +

+ +

+ And with all that out of the way: Happy testing! +

+
+

System requirements

+

Audacity 4 has higher system requirements than Audacity 3, mostly due to Qt's platform support. We expect Audacity 4 Alpha 1 to work on the following platforms:

+
    +
  • + Windows 10 & 11 (x64, but not ARM yet) +
  • +
  • + macOS 12 and later +
  • +
  • + Linux: Ubuntu 22.04, RedHat 8.6, openSUSE 15.6, Debian 11.6 and later +
  • +
+
+ + +
+
+
From 6f603597bf92922d3ff75f913768ab76d3295c43 Mon Sep 17 00:00:00 2001 From: Teetow Date: Thu, 16 Oct 2025 13:53:04 +0200 Subject: [PATCH 2/9] refactor: streamline promo banner data --- package.json | 1 + src/assets/data/audacityReleases.ts | 167 ++++++++++++++++++++++- src/assets/data/betaReleases.ts | 98 -------------- src/assets/data/promotions.ts | 85 ++++++++++++ src/components/banner/BetaBanner.jsx | 43 ------ src/components/banner/PromoBanner.tsx | 137 +++++++++++++++---- src/components/banner/SurveyBanner.jsx | 38 ------ src/components/footer/Footer.astro | 24 +++- src/env.d.ts | 8 ++ src/layouts/BaseLayout.astro | 26 ++-- src/pages/au4.astro | 150 ++++++++++++++++----- src/pages/beta.astro | 175 +++++++++++++++++++++---- src/pages/download.astro | 129 ++++++++++++++++-- 13 files changed, 786 insertions(+), 295 deletions(-) delete mode 100644 src/assets/data/betaReleases.ts create mode 100644 src/assets/data/promotions.ts delete mode 100644 src/components/banner/BetaBanner.jsx delete mode 100644 src/components/banner/SurveyBanner.jsx diff --git a/package.json b/package.json index 06bc7fd..6d8d768 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/assets/data/audacityReleases.ts b/src/assets/data/audacityReleases.ts index e90982b..40f6a99 100644 --- a/src/assets/data/audacityReleases.ts +++ b/src/assets/data/audacityReleases.ts @@ -1,18 +1,20 @@ 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 type ActivationState = "active" | "inactive"; + export const audacityReleases: ReleaseDirectory = { version: "3.7.5", win: [ @@ -127,4 +129,161 @@ export const audacityReleases: ReleaseDirectory = { type: ".tar.gz", }, ], -}; \ No newline at end of file +}; + +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; + status: ActivationState; + summary: string; + pageHref: string; + downloads: ReleaseDirectory; +}; + +export const alphaPreRelease: PreReleaseEntry = { + id: "alpha", + label: "Alpha", + status: "active", + summary: + "Get an early look at the next major release. Expect unfinished features and potential bugs.", + pageHref: "/au4", + 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", + status: "inactive", + summary: "Help us test upcoming features before they ship.", + pageHref: "/beta", + 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, +]; \ No newline at end of file diff --git a/src/assets/data/betaReleases.ts b/src/assets/data/betaReleases.ts deleted file mode 100644 index 77371c7..0000000 --- a/src/assets/data/betaReleases.ts +++ /dev/null @@ -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", - }, - ], -}; diff --git a/src/assets/data/promotions.ts b/src/assets/data/promotions.ts new file mode 100644 index 0000000..b39665b --- /dev/null +++ b/src/assets/data/promotions.ts @@ -0,0 +1,85 @@ +export type PromoData = { + isActive?: boolean; + priority?: number; + osTargets?: 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 = { + 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: { + 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, + priority: 40, + 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; diff --git a/src/components/banner/BetaBanner.jsx b/src/components/banner/BetaBanner.jsx deleted file mode 100644 index 2eff130..0000000 --- a/src/components/banner/BetaBanner.jsx +++ /dev/null @@ -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 ( - - ); -} - -export default BetaBanner; diff --git a/src/components/banner/PromoBanner.tsx b/src/components/banner/PromoBanner.tsx index 0bf7972..f95f211 100644 --- a/src/components/banner/PromoBanner.tsx +++ b/src/components/banner/PromoBanner.tsx @@ -1,55 +1,134 @@ -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"; -function PromoBanner() { - // no promo atm - //return false; +const DEFAULT_PROMO_STYLES: NonNullable = { + 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"; +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"; + +const STATIC_PROMOS: PromoData[] = Object.values(promoData); + +const isPromoActive = (promo: PromoData | null | undefined) => + promo?.isActive ?? true; + +const getHighestPriorityPromo = (promos: PromoData[]) => + promos.reduce((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; + } - // Only show the banner for supported OSes - const showBanner = browserOS === "OS X" || browserOS === "Windows"; + const weights = promos.map((promo) => Math.max(promo.priority ?? 1, 0)); + const totalWeight = weights.reduce((sum, weight) => sum + weight, 0); - 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 + 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 = (): PromoData[] => STATIC_PROMOS; +const PromoBanner: React.FC = () => { + const browserOS = useBrowserOS(); + const promos = buildPromoList(); + const eligiblePromos = getEligiblePromos(promos, browserOS); + const fallbackPromos = promos.filter((promo) => isPromoActive(promo)); + const selectionPool = + eligiblePromos.length > 0 ? eligiblePromos : fallbackPromos; + const selectedPromo = selectWeightedPromo(selectionPool); + + 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 && ( -
-
-

- Heavy guitar tone in seconds. One knob, no distractions. -

+ { +
+
+

{trimmedMessage}

- Try for free + {cta.text}
- )} + } ); -} +}; export default PromoBanner; diff --git a/src/components/banner/SurveyBanner.jsx b/src/components/banner/SurveyBanner.jsx deleted file mode 100644 index 015c03e..0000000 --- a/src/components/banner/SurveyBanner.jsx +++ /dev/null @@ -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 ( - - ); -} - -export default SurveyBanner; diff --git a/src/components/footer/Footer.astro b/src/components/footer/Footer.astro index 2d20ab8..10a8a45 100644 --- a/src/components/footer/Footer.astro +++ b/src/components/footer/Footer.astro @@ -1,5 +1,16 @@ --- import "../../styles/fonts.css"; +import { + hasDownloadAssets, + preReleaseList, +} from "../../assets/data/audacityReleases"; +const activeDownloadCampaign = preReleaseList.find((entry) => + entry.status === "active" && hasDownloadAssets(entry.downloads) +) ?? null; +const hasPreRelease = Boolean(activeDownloadCampaign); +const activePreReleaseAriaLabel = hasPreRelease && activeDownloadCampaign + ? `Link to ${activeDownloadCampaign.label.toLowerCase()} page` + : ""; ---
diff --git a/src/pages/download.astro b/src/pages/download.astro index 1343005..f4f6779 100644 --- a/src/pages/download.astro +++ b/src/pages/download.astro @@ -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.status === "active" && hasDownloadAssets(entry.downloads), +); +const hasDownloadCampaigns = activeDownloadEntries.length > 0; + +const formatCampaignLinkLabel = ( + entry: PreReleaseEntry, + platform: string, + buildName?: string, +) => + `${entry.downloads.version} for ${platform}${ + buildName ? ` (${buildName})` : "" + }`; --- - -
+ +

Downloads

@@ -48,7 +74,82 @@ const { version, src } = audacityReleases;