diff --git a/web/app/api/signup/route.ts b/web/app/api/signup/route.ts new file mode 100644 index 00000000..b08636d5 --- /dev/null +++ b/web/app/api/signup/route.ts @@ -0,0 +1,17 @@ +import { Analytics } from '@segment/analytics-node' +import { v4 as uuid } from 'uuid' + +const analytics = new Analytics({ writeKey: process.env.TELEMETRY_WRITE_KEY || '' }) + +export async function POST(req: Request) { + const { email } = await req.json() + + analytics.identify({ + anonymousId: uuid(), + traits: { + email, + }, + }) + + return new Response(null, { status: 200 }) +} diff --git a/web/app/download/downloader.tsx b/web/app/download/downloader.tsx new file mode 100644 index 00000000..7c46d02e --- /dev/null +++ b/web/app/download/downloader.tsx @@ -0,0 +1,11 @@ +'use client' + +import { useEffect } from 'react' + +export default function ({ url }: { url: string }) { + useEffect(() => { + window.location.href = url + }, []) + + return null +} diff --git a/web/app/download/page.tsx b/web/app/download/page.tsx index 4b10baeb..17d89d0b 100644 --- a/web/app/download/page.tsx +++ b/web/app/download/page.tsx @@ -1,18 +1,19 @@ -import { redirect } from 'next/navigation' +import Downloader from './downloader' +import Signup from './signup' export default async function Download() { const res = await fetch('https://api.github.com/repos/jmorganca/ollama/releases', { next: { revalidate: 60 } }) const data = await res.json() if (data.length === 0) { - return new Response('not found', { status: 404 }) + return null } const latest = data[0] const assets = latest.assets || [] if (assets.length === 0) { - return new Response('not found', { status: 404 }) + return null } // todo: get the correct asset for the current arch/os @@ -21,12 +22,26 @@ export default async function Download() { ) if (!asset) { - return new Response('not found', { status: 404 }) + return null } - if (asset) { - redirect(asset.browser_download_url) - } - - return null + return ( +
+ +
+

Downloading Ollama

+

+ Problems downloading?{' '} + + Try again + +

+ +
+
+

Sign up for updates

+ +
+
+ ) } diff --git a/web/app/download/signup.tsx b/web/app/download/signup.tsx new file mode 100644 index 00000000..82725325 --- /dev/null +++ b/web/app/download/signup.tsx @@ -0,0 +1,46 @@ +'use client' + +import { useState } from 'react' + +export default function () { + const [email, setEmail] = useState('') + const [success, setSuccess] = useState(false) + + return ( +
{ + e.preventDefault() + + await fetch('/api/signup', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ email }), + }) + + setSuccess(true) + setEmail('') + + return false + }} + className='flex self-stretch flex-col gap-3 h-32' + > + setEmail(e.target.value)} + type='email' + placeholder='your@email.com' + className='bg-neutral-100 rounded-lg px-4 py-2 focus:outline-none placeholder-neutral-500' + /> + + {success &&

You're signed up for updates

} +
+ ) +} diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 72b6c888..5379cb7f 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -8,7 +8,7 @@ export const metadata = { export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - {children} + {children} ) } diff --git a/web/app/page.tsx b/web/app/page.tsx index 55793ec2..ecb0e9e9 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -5,7 +5,7 @@ import models from '../../models.json' export default async function Home() { return (
- +

@@ -14,7 +14,7 @@ export default async function Home() { is a tool for running large language models, currently for macOS with Windows and Linux coming soon.

-
+ diff --git a/web/package-lock.json b/web/package-lock.json index 13878021..939bf782 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@octokit/rest": "^19.0.13", "@octokit/types": "^11.0.0", + "@segment/analytics-node": "^1.0.0", "@types/node": "20.4.0", "@types/react": "18.2.14", "@types/react-dom": "18.2.6", @@ -24,10 +25,14 @@ "react-icons": "^4.10.1", "semver": "^7.5.3", "tailwindcss": "3.3.2", - "typescript": "5.1.6" + "typescript": "5.1.6", + "uuid": "^9.0.0" }, "devDependencies": { - "@types/semver": "^7.5.0" + "@types/semver": "^7.5.0", + "@types/uuid": "^9.0.2", + "prettier": "^3.0.0", + "prettier-plugin-tailwindcss": "^0.4.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -190,6 +195,25 @@ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@lukeed/uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@lukeed/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==", + "dependencies": { + "@lukeed/csprng": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@next/env": { "version": "13.4.9", "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.9.tgz", @@ -599,6 +623,31 @@ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz", "integrity": "sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==" }, + "node_modules/@segment/analytics-core": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@segment/analytics-core/-/analytics-core-1.3.0.tgz", + "integrity": "sha512-ujScWZH49NK1hYlp2/EMw45nOPEh+pmTydAnR6gSkRNucZD4fuinvpPL03rmFCw8ibaMuKLAdgPJfQ0gkLKZ5A==", + "dependencies": { + "@lukeed/uuid": "^2.0.0", + "dset": "^3.1.2", + "tslib": "^2.4.1" + } + }, + "node_modules/@segment/analytics-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@segment/analytics-node/-/analytics-node-1.0.0.tgz", + "integrity": "sha512-UWFujSxRkRauZuMVF4MPOT5QPvX4i7kiC2QCsozHhltoTiR2SBWRI86cYO/JI/Uk7qKaOxxGFDkJarCyIP7uLA==", + "dependencies": { + "@lukeed/uuid": "^2.0.0", + "@segment/analytics-core": "1.3.0", + "buffer": "^6.0.3", + "node-fetch": "^2.6.7", + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/@swc/helpers": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", @@ -651,6 +700,12 @@ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "node_modules/@types/uuid": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", + "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", + "dev": true + }, "node_modules/@typescript-eslint/parser": { "version": "5.60.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", @@ -991,6 +1046,25 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -1074,6 +1148,29 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -1390,6 +1487,14 @@ "node": ">=6.0.0" } }, + "node_modules/dset": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", + "integrity": "sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==", + "engines": { + "node": ">=4" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.447", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.447.tgz", @@ -1747,9 +1852,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1784,9 +1889,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1858,9 +1963,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -2385,6 +2490,25 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -3521,6 +3645,95 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.4.0.tgz", + "integrity": "sha512-Rna0sDPETA0KNhMHlN8wxKNgfSa8mTl2hPPAGxnbv6tUcHT6J4RQmQ8TLXyhB7Dm5Von4iHloBxTyClYM6wT0A==", + "dev": true, + "engines": { + "node": ">=12.17.0" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@shufo/prettier-plugin-blade": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "prettier": "^2.2 || ^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*", + "prettier-plugin-twig-melody": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@shufo/prettier-plugin-blade": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "prettier-plugin-twig-melody": { + "optional": true + } + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4360,6 +4573,14 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/web/package.json b/web/package.json index c57c8c09..3b4f05ec 100644 --- a/web/package.json +++ b/web/package.json @@ -10,6 +10,7 @@ "dependencies": { "@octokit/rest": "^19.0.13", "@octokit/types": "^11.0.0", + "@segment/analytics-node": "^1.0.0", "@types/node": "20.4.0", "@types/react": "18.2.14", "@types/react-dom": "18.2.6", @@ -24,9 +25,13 @@ "react-icons": "^4.10.1", "semver": "^7.5.3", "tailwindcss": "3.3.2", - "typescript": "5.1.6" + "typescript": "5.1.6", + "uuid": "^9.0.0" }, "devDependencies": { - "@types/semver": "^7.5.0" + "@types/semver": "^7.5.0", + "@types/uuid": "^9.0.2", + "prettier": "^3.0.0", + "prettier-plugin-tailwindcss": "^0.4.0" } }