From 2b88c5c6248c42719b404a67c33fa25a133a057a Mon Sep 17 00:00:00 2001 From: Nick Winans Date: Thu, 16 Jan 2025 00:14:15 -0800 Subject: [PATCH] feat: Add Download Page (#105) Add a download page to easily allow downloading the latest release. --- .gitignore | 1 + download.html | 13 +++ package-lock.json | 84 ++++++++++++++- package.json | 10 +- public/zmk-mac-app-icon.webp | Bin 0 -> 23052 bytes scripts/generate-release-data.js | 29 +++++ src/ConnectModal.tsx | 2 +- src/DownloadPage.tsx | 177 +++++++++++++++++++++++++++++++ src/download.tsx | 11 ++ tailwind.config.js | 2 +- vite.config.ts | 7 ++ 11 files changed, 326 insertions(+), 10 deletions(-) create mode 100644 download.html create mode 100644 public/zmk-mac-app-icon.webp create mode 100644 scripts/generate-release-data.js create mode 100644 src/DownloadPage.tsx create mode 100644 src/download.tsx diff --git a/.gitignore b/.gitignore index f940a99..5618eb1 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ node_modules dist dist-ssr *.local +src/data # Editor directories and files .vscode/* diff --git a/download.html b/download.html new file mode 100644 index 0000000..5eb770f --- /dev/null +++ b/download.html @@ -0,0 +1,13 @@ + + + + + + + ZMK Studio - Download + + +
+ + + diff --git a/package-lock.json b/package-lock.json index 79ded60..0952720 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "zmk-studio", "version": "0.2.4", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.7.1", + "@fortawesome/free-brands-svg-icons": "^6.7.1", + "@fortawesome/react-fontawesome": "^0.2.2", "@tailwindcss/container-queries": "^0.1.1", "@tauri-apps/api": "^2.0.0", "@tauri-apps/plugin-cli": "^2.0.0", @@ -1078,6 +1081,52 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.1.tgz", + "integrity": "sha512-gbDz3TwRrIPT3i0cDfujhshnXO9z03IT1UKRIVi/VEjpNHtSBIP2o5XSm+e816FzzCFEzAxPw09Z13n20PaQJQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.1.tgz", + "integrity": "sha512-8dBIHbfsKlCk2jHQ9PoRBg2Z+4TwyE3vZICSnoDlnsHA6SiMlTwfmW6yX0lHsRmWJugkeb92sA0hZdkXJhuz+g==", + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.7.1.tgz", + "integrity": "sha512-nJR76eqPzCnMyhbiGf6X0aclDirZriTPRcFm1YFvuupyJOGwlNF022w3YBqu+yrHRhnKRpzFX+8wJKqiIjWZkA==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "dev": true, @@ -8872,7 +8921,6 @@ }, "node_modules/prop-types": { "version": "15.8.1", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -8882,7 +8930,6 @@ }, "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", - "dev": true, "license": "MIT" }, "node_modules/protobufjs": { @@ -11652,6 +11699,35 @@ } } }, + "@fortawesome/fontawesome-common-types": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.1.tgz", + "integrity": "sha512-gbDz3TwRrIPT3i0cDfujhshnXO9z03IT1UKRIVi/VEjpNHtSBIP2o5XSm+e816FzzCFEzAxPw09Z13n20PaQJQ==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.1.tgz", + "integrity": "sha512-8dBIHbfsKlCk2jHQ9PoRBg2Z+4TwyE3vZICSnoDlnsHA6SiMlTwfmW6yX0lHsRmWJugkeb92sA0hZdkXJhuz+g==", + "requires": { + "@fortawesome/fontawesome-common-types": "6.7.1" + } + }, + "@fortawesome/free-brands-svg-icons": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.7.1.tgz", + "integrity": "sha512-nJR76eqPzCnMyhbiGf6X0aclDirZriTPRcFm1YFvuupyJOGwlNF022w3YBqu+yrHRhnKRpzFX+8wJKqiIjWZkA==", + "requires": { + "@fortawesome/fontawesome-common-types": "6.7.1" + } + }, + "@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "requires": { + "prop-types": "^15.8.1" + } + }, "@humanwhocodes/config-array": { "version": "0.11.14", "dev": true, @@ -16727,7 +16803,6 @@ }, "prop-types": { "version": "15.8.1", - "dev": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -16735,8 +16810,7 @@ }, "dependencies": { "react-is": { - "version": "16.13.1", - "dev": true + "version": "16.13.1" } } }, diff --git a/package.json b/package.json index 2802534..06f9453 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,9 @@ "version": "0.2.4", "type": "module", "scripts": { - "dev": "vite", - "build": "tsc && vite build", + "generate-data": "node scripts/generate-release-data.js", + "dev": "npm run generate-data && vite", + "build": "npm run generate-data && tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", "tauri": "tauri", @@ -13,6 +14,9 @@ "build-storybook": "storybook build" }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.7.1", + "@fortawesome/free-brands-svg-icons": "^6.7.1", + "@fortawesome/react-fontawesome": "^0.2.2", "@tailwindcss/container-queries": "^0.1.1", "@tauri-apps/api": "^2.0.0", "@tauri-apps/plugin-cli": "^2.0.0", @@ -60,4 +64,4 @@ "@esbuild/darwin-arm64": "0.23.0", "@rollup/rollup-darwin-arm64": "^4.18.1" } -} +} \ No newline at end of file diff --git a/public/zmk-mac-app-icon.webp b/public/zmk-mac-app-icon.webp new file mode 100644 index 0000000000000000000000000000000000000000..b97b76376663cf96abf3e00e11699ec253e3891c GIT binary patch literal 23052 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
  • Download our{" "} - + cross platform application . diff --git a/src/DownloadPage.tsx b/src/DownloadPage.tsx new file mode 100644 index 0000000..19e01d7 --- /dev/null +++ b/src/DownloadPage.tsx @@ -0,0 +1,177 @@ +import { useEffect, useState } from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faAndroid, + faApple, + faLinux, + faWindows, + IconDefinition, +} from "@fortawesome/free-brands-svg-icons"; +import { DownloadIcon } from "lucide-react"; +import releaseData from "./data/release-data.json"; + +type Platform = "windows" | "mac" | "linux" | "ios" | "android" | "unknown"; + +const PlatformMetadata: Record< + Platform, + { name: string; icon: IconDefinition } +> = { + windows: { + name: "Windows", + icon: faWindows, + }, + mac: { + name: "macOS", + icon: faApple, + }, + linux: { + name: "Linux", + icon: faLinux, + }, + ios: { + name: "iOS", + icon: faApple, + }, + android: { + name: "Android", + icon: faAndroid, + }, + unknown: { + name: "Unknown", + icon: faAndroid, + }, +}; + +type DownloadLink = { + name: string; + urlPattern: RegExp; +}; + +const DownloadLinks: Record = { + windows_exe: { + name: "Windows (exe)", + urlPattern: /.*\.exe/, + }, + windows_msi: { + name: "Windows (msi)", + urlPattern: /.*\.msi/, + }, + macos: { + name: "macOS", + urlPattern: /.*\.dmg/, + }, + linux_appimage: { + name: "Linux (AppImage)", + urlPattern: /.*\.AppImage/, + }, + linux_deb: { + name: "Linux (deb)", + urlPattern: /.*\.deb/, + }, +}; + +const PlatformLinks: Record = { + windows: [DownloadLinks.windows_exe, DownloadLinks.windows_msi], + mac: [DownloadLinks.macos], + linux: [DownloadLinks.linux_appimage, DownloadLinks.linux_deb], + ios: [], + android: [], + unknown: [], +}; + +const ReleaseAssets = releaseData.assets.map((asset: any) => asset.browser_download_url); +const ReleaseVersion = releaseData.tag_name; + +function detectPlatform(): Platform { + if (typeof window === "undefined") return "unknown"; + + const userAgent = window.navigator.userAgent.toLowerCase(); + + if (userAgent.includes("win")) return "windows"; + if (userAgent.includes("mac")) return "mac"; + if (userAgent.includes("linux")) return "linux"; + if (/iphone|ipad|ipod/.test(userAgent)) return "ios"; + if (userAgent.includes("android")) return "android"; + + return "unknown"; +} + +function getUrlFromPattern(assets: string[], pattern: RegExp) { + const asset = assets.find((asset) => pattern.test(asset)); + return asset; +} + +export const Download = () => { + const [platform, setPlatform] = useState("unknown"); + const [showAll, setShowAll] = useState(false); + + useEffect(() => { + const platform = detectPlatform(); + setPlatform(platform); + if (PlatformLinks[platform].length === 0) { + setShowAll(true); + } + }, []); + + return ( +
    + ZMK Studio +
    ZMK Studio
    +
    + {ReleaseVersion} +
    +
    + {PlatformLinks[platform].length > 0 && ( + <> + + + )} +
    + {PlatformLinks[platform].length > 0 && ( + + )} + {showAll && ( +
    + {Object.entries(PlatformLinks).map(([platform, links]) => ( +
    + {links.map((link) => ( + + + {link.name} + + ))} +
    + ))} +
    + )} +
    +
    + + See GitHub Releases → + +
    + ); +}; diff --git a/src/download.tsx b/src/download.tsx new file mode 100644 index 0000000..180a70d --- /dev/null +++ b/src/download.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import "./index.css"; + +import { Download } from "./DownloadPage"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/tailwind.config.js b/tailwind.config.js index 2f14d26..f99539d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -3,7 +3,7 @@ import trac from "tailwindcss-react-aria-components"; import contQueries from "@tailwindcss/container-queries"; export default { - content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + content: ["./index.html", "./download.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { extend: { fontFamily: { diff --git a/vite.config.ts b/vite.config.ts index 5fb6d03..46cb69a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -27,5 +27,12 @@ export default defineConfig({ minify: !process.env.TAURI_DEBUG ? "esbuild" : false, // produce sourcemaps for debug builds sourcemap: !!process.env.TAURI_DEBUG, + // include download page + rollupOptions: { + input: { + main: "./index.html", + download: "./download.html", + }, + } }, });