diff --git a/package-lock.json b/package-lock.json index bde08c1..96a082e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "license": "UNLICENSED", "dependencies": { + "@vercel/analytics": "^2.0.1", "gray-matter": "^4.0.3", "marked": "^18.0.11", "next": "15.5.24", @@ -1040,6 +1041,72 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", @@ -1118,6 +1185,48 @@ "@types/react": "^19.3.0" } }, + "node_modules/@vercel/analytics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-2.0.1.tgz", + "integrity": "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==", + "license": "MIT", + "peerDependencies": { + "@remix-run/react": "^2", + "@sveltejs/kit": "^1 || ^2", + "next": ">= 13", + "nuxt": ">= 3", + "react": "^18 || ^19 || ^19.0.0-rc", + "svelte": ">= 4", + "vue": "^3", + "vue-router": "^4" + }, + "peerDependenciesMeta": { + "@remix-run/react": { + "optional": true + }, + "@sveltejs/kit": { + "optional": true + }, + "next": { + "optional": true + }, + "nuxt": { + "optional": true + }, + "react": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-router": { + "optional": true + } + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", diff --git a/package.json b/package.json index e2143e2..1d84209 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "test:smoke": "node --test tests/runtime.test.mjs" }, "dependencies": { + "@vercel/analytics": "^2.0.1", "gray-matter": "^4.0.3", "marked": "^18.0.11", "next": "15.5.24", diff --git a/public/llms.txt b/public/llms.txt index e96bbdd..9cc8cfa 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -6,7 +6,7 @@ Key facts: - Storage: on-device first (Hive). Sync writes one .atomic file per note to the user's Google Drive, using the drive.file scope (the app sees only files it created). - Server: stores metadata only (note ids, pinned and deleted flags, timestamps, Drive file ids). It never stores note titles or text. With the vault off, note text passes through it in transit to Drive. -- Price: free. Every account holds 30 notes. Cloud sync uses Atomic Energy: +20 every 24 hours, cap 120, standard sync 5 (at most once an hour), instant sync 10, syncs with nothing to upload are free. 1 Atomic Coin = 40 energy. New accounts get 5 coins. Coins are not sold in the app yet. Early supporters on Patreon (https://www.patreon.com/cw/DevBehindYou) who send their account email get Atomic Coins from the developer by hand. +- Price: free. Every account holds 30 notes. Cloud sync uses Atomic Energy: +20 every 24 hours, cap 120, standard sync 5 (at most once an hour), instant sync 10, syncs with nothing to upload are free. 1 Atomic Coin = 40 energy. New accounts get 5 coins. Coins are not sold in the app yet. Early supporters on Patreon (https://www.patreon.com/cw/DevBehindYou) or Ko-fi (https://ko-fi.com/devbehindyou) who send their account email get Atomic Coins from the developer by hand. - Capacity tiers: Tachyon 30 notes (free), Antimatter 40 (10 coins), Monopole 50 (20 coins), Strangelet 100 (30 coins). - Device security: biometric lock, TOTP two-step verification, blocked screenshots (FLAG_SECURE), Android secure storage. - Distribution: signed APKs on GitHub Releases. Certificate SHA-256 cc24ae5ce1dca50fcd5e5c4c252d69e4965c55a975bd0e4739e8938fad9bebeb. Not on Google Play. No iOS build yet. @@ -16,7 +16,7 @@ Key facts: - [Home](https://atomic-notes-community.vercel.app/): features, how sync works, what each party can see, Atomic Energy, FAQ - [Updates](https://atomic-notes-community.vercel.app/updates): live release notes and service status - [Blog](https://atomic-notes-community.vercel.app/blog): development updates and technical write-ups -- [Support](https://atomic-notes-community.vercel.app/support-atomic-notes): support on Patreon and get Atomic Coins early +- [Support](https://atomic-notes-community.vercel.app/support-atomic-notes): support on Patreon or Ko-fi and get Atomic Coins early ## Source and downloads diff --git a/src/app/globals.css b/src/app/globals.css index aa6267c..c088a2d 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1744,6 +1744,27 @@ section h2 { line-height: 1.55; color: #2b2e34; } +.support-platforms { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + margin-top: 30px; +} +.support-platform { + display: flex; + flex-direction: column; +} +.support-platform h3 { + font-size: 2rem; + margin-top: 6px; +} +.support-platform .support-list { + flex: 1; + margin-bottom: 20px; +} +.support-platform .btn-signal { + align-self: flex-start; +} .support-facts { margin: 22px 0 26px; max-width: 640px; @@ -1752,7 +1773,8 @@ section h2 { color: var(--signal); } @media (max-width: 820px) { - .support-grid { + .support-grid, + .support-platforms { grid-template-columns: 1fr; } } @@ -1779,3 +1801,187 @@ section h2 { .legal .prose li { margin-bottom: 6px; } + +/* ---------- community Discord popup ---------- */ +.community-pop { + position: fixed; + left: 20px; + bottom: 20px; + z-index: 60; + width: 320px; + max-width: calc(100vw - 32px); + padding: 12px; + background: + radial-gradient(320px circle at 0% 0%, rgba(58, 47, 240, 0.38), transparent 65%), + var(--ink); + border-radius: 4px; + box-shadow: 5px 5px 0 var(--signal); + opacity: 0; + transform: translateY(16px); + visibility: hidden; + transition: + opacity 0.35s ease, + transform 0.35s ease, + visibility 0s linear 0.35s; +} +.community-pop.is-visible { + opacity: 1; + transform: none; + visibility: visible; + transition: + opacity 0.35s ease, + transform 0.35s ease, + visibility 0s; +} +.community-pop-close { + position: absolute; + top: 8px; + right: 8px; + width: 28px; + height: 28px; + display: grid; + place-items: center; + border: none; + border-radius: 4px; + background: transparent; + color: #cfd0d3; + font-size: 18px; + line-height: 1; + cursor: pointer; +} +.community-pop-close:hover { + background: rgba(244, 245, 241, 0.1); + color: #fff; +} +.community-pop-head { + display: flex; + align-items: center; + gap: 10px; + padding: 2px 34px 12px 4px; +} +.community-pop-mark { + width: 30px; + height: 30px; + flex-shrink: 0; +} +.community-pop-brand { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} +.community-pop-brand b { + font-family: var(--display); + font-weight: 400; + font-size: 1.15rem; + letter-spacing: 0.5px; + line-height: 1; + color: var(--paper); +} +.community-pop-live { + display: inline-flex; + align-items: center; + gap: 6px; + font-family: var(--mono); + font-size: 0.62rem; + letter-spacing: 1.6px; + text-transform: uppercase; + color: #8f88ff; +} +.community-pop-live::before { + content: ""; + width: 6px; + height: 6px; + border-radius: 50%; + background: #3ddc84; + box-shadow: 0 0 0 3px rgba(61, 220, 132, 0.18); +} +.community-pop-card { + background: var(--paper); + border-radius: 3px; + padding: 14px 14px 0; + overflow: hidden; +} +.community-pop-title { + font-family: var(--display); + font-size: 1.55rem; + line-height: 0.98; + letter-spacing: 0.3px; + text-transform: uppercase; + margin: 0 0 8px; +} +.community-pop-copy { + font-size: 0.86rem; + line-height: 1.45; + color: var(--slate); + margin: 0 0 12px; +} +.community-pop-copy strong { + color: var(--ink); +} +.community-pop-cta { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin: 0 -14px; + padding: 11px 14px; + border-top: 3px solid var(--ink); + background: var(--signal); + color: #fff; + text-decoration: none; + transition: background 0.15s ease; +} +.community-pop-cta:hover { + background: #2a20c9; +} +.community-pop-cta-text { + display: flex; + flex-direction: column; + gap: 3px; +} +.community-pop-label { + font-family: var(--mono); + font-size: 0.62rem; + letter-spacing: 1.6px; + text-transform: uppercase; + opacity: 0.85; +} +.community-pop-go { + font-family: var(--display); + font-size: 1.25rem; + letter-spacing: 0.6px; + line-height: 1; +} +.community-pop-arrow { + font-size: 1.2rem; + transition: transform 0.15s ease; +} +.community-pop-cta:hover .community-pop-arrow { + transform: translateX(3px); +} +.community-pop-close:focus-visible, +.community-pop-cta:focus-visible { + outline: 2px solid #8f88ff; + outline-offset: 2px; +} +@media (max-width: 420px) { + .community-pop { + left: 16px; + bottom: 16px; + } +} +@media (prefers-reduced-motion: reduce) { + .community-pop, + .community-pop.is-visible { + transform: none; + transition: + opacity 0.2s ease, + visibility 0s; + } + .community-pop-arrow, + .community-pop-cta:hover .community-pop-arrow { + transition: none; + transform: none; + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 9c5fb1b..6a8fb64 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,6 +2,8 @@ import type { Metadata, Viewport } from "next"; import { Bebas_Neue, Hanken_Grotesk, JetBrains_Mono } from "next/font/google"; import "./globals.css"; import { SITE_URL } from "@/lib/site"; +import { SiteAnalytics } from "@/components/SiteAnalytics"; +import { CommunityPopup } from "@/components/CommunityPopup"; const bebas = Bebas_Neue({ weight: "400", @@ -72,7 +74,11 @@ export default function RootLayout({ }) { return ( -
{children} + + {children} +New accounts get 5 Atomic Coins. If a sync fails and no note gets through, its energy is refunded. Coins can't be bought in the app yet, but early supporters get them:{" "} - support on Patreon. + support on Patreon or Ko-fi.
diff --git a/src/app/privacy/page.tsx b/src/app/privacy/page.tsx index 9116535..ec77874 100644 --- a/src/app/privacy/page.tsx +++ b/src/app/privacy/page.tsx @@ -13,7 +13,7 @@ const CONTACT = "https://github.com/DevBehindYou"; export default function PrivacyPage() { return ( -This policy explains how the Atomic Notes Android app, its sync server and this website handle your information. Atomic Notes is built and operated by Ashutosh Sharma, who publishes as DevBehindYou (“we”, @@ -42,11 +42,21 @@ export default function PrivacyPage() {
- We do not collect your note content, contacts, location, advertising IDs, analytics or crash reports. Our hosting - provider processes standard request data, such as IP addresses, to deliver the app's server and this + The app does not collect your note content, contacts, location, advertising IDs, analytics or crash reports. Our + hosting provider processes standard request data, such as IP addresses, to deliver the app's server and this website. This website sets no tracking or advertising cookies.
++ This website (not the app) uses Vercel Web Analytics to count page views. For each page you open it records the + page address, the referring site, your country, and your browser, operating system and device type. It sets no + cookies and stores no IP address. Visits are told apart by a hash of the request that Vercel resets every day, + so a visitor cannot be followed across days or across other websites. We only see these totals in Vercel's + dashboard, and we never combine them with your Atomic Notes account. A content blocker stops it without breaking + the site. +
+Atomic Notes asks Google for these permissions, and uses each one only as described:
diff --git a/src/app/support-atomic-notes/page.tsx b/src/app/support-atomic-notes/page.tsx index f4c26fb..d8cc53a 100644 --- a/src/app/support-atomic-notes/page.tsx +++ b/src/app/support-atomic-notes/page.tsx @@ -1,12 +1,12 @@ import type { Metadata } from "next"; import { SiteNav } from "@/components/SiteNav"; import { SiteFooter } from "@/components/SiteFooter"; -import { PATREON_URL } from "@/lib/content"; +import { KOFI_URL, PATREON_URL } from "@/lib/content"; export const metadata: Metadata = { title: "Support Atomic Notes, get Atomic Coins early", description: - "Support Atomic Notes on Patreon at the amount you choose. Send your Atomic Notes account email, and the developer sends you Atomic Coins as an early-supporter reward.", + "Support Atomic Notes on Patreon or Ko-fi at the amount you choose. Send your Atomic Notes account email, and the developer sends you Atomic Coins as an early-supporter reward.", alternates: { canonical: "/support-atomic-notes" }, }; @@ -21,15 +21,20 @@ const STEPS: { t: string; d: React.ReactNode }[] = [ ), }, { - t: "Support on Patreon", - d: <>Open the DevBehindYou page on Patreon and support the project at the amount you choose.>, + t: "Pick a platform", + d: ( + <> + Support the DevBehindYou page on Patreon or Ko-fi, at the amount you choose. Both reach the same + developer and earn the same reward. + > + ), }, { t: "Send your email", d: ( <> - Send the developer a Patreon message with your Atomic Notes account email. Check it letter by letter: the coins - go to exactly that account. + On Patreon, send the developer a message. On Ko-fi, write it in the message box when you pay and mark the + message private. Check it letter by letter: the coins go to exactly that account. > ), }, @@ -44,6 +49,31 @@ const STEPS: { t: string; d: React.ReactNode }[] = [ }, ]; +const PLATFORMS: { name: string; tag: string; url: string; label: string; points: string[] }[] = [ + { + name: "Patreon", + tag: "MEMBERSHIP", + url: PATREON_URL, + label: "Support on Patreon", + points: [ + "Usually a monthly membership", + "Send your account email in a Patreon message", + "Cancel on Patreon at any time", + ], + }, + { + name: "Ko-fi", + tag: "ONE-TIME OR MONTHLY", + url: KOFI_URL, + label: "Support on Ko-fi", + points: [ + "Good for a single payment", + "Put your account email in the message and mark it private", + "Monthly support is also available on Ko-fi", + ], + }, +]; + export default function SupportPage() { return ( <> @@ -57,13 +87,16 @@ export default function SupportPage() {
Atomic Notes is built by one developer, with no ads and no trackers. Coins aren't sold in the app - yet. Support the project on Patreon, and the developer sends Atomic Coins to your account as an + yet. Support the project on Patreon or Ko-fi, and the developer sends Atomic Coins to your account as an early-supporter reward.
TWO WAYS TO SUPPORT
+FOUR STEPS
@@ -92,16 +150,19 @@ export default function SupportPage() {IMPORTANT
- The reward is sent to the email you write, not to your Patreon name. A wrong or misspelled email means - the coins can't reach you. If you signed in with more than one Google account, use the one that - holds your notes. + The reward is sent to the email you write, not to your Patreon or Ko-fi name. A wrong or misspelled + email means the coins can't reach you. If you signed in with more than one Google account, use the + one that holds your notes. On Ko-fi, mark the message private so your email isn't shown on the + public page.
GOOD TO KNOW