Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 37c7dca30d | |||
| 73c435eb90 | |||
| 7be2b49300 | |||
| e5c05cbece | |||
| c7f4d0248c | |||
| 4c5d67c321 | |||
| 9feb9e24b6 | |||
| 941faccb3f | |||
| 6a040afcc5 | |||
| a346a2197e | |||
| e268b424f5 | |||
| 20029d36df | |||
| 54d48ed679 | |||
| 28f9ecd1a3 | |||
| b6bb25e79e |
@@ -82,7 +82,12 @@ jobs:
|
||||
echo "Changed files:"
|
||||
echo "$CHANGED"
|
||||
NON_ASSET=$(echo "$CHANGED" | grep -v '^src/assets/' | grep -v '^src/shares/' | grep -v '^src/index\.html' | grep -v '^src/about\.html' | grep -v '^src/terms\.html' | grep -v '^src/enterprise-terms\.html' | grep -v '^src/llms\.txt' | grep -v '^migrations/' | grep -v '^scripts/' | grep -v '^tests/' | grep -v '^\.gitea/' | grep -v '^$' || true)
|
||||
if [ -z "$NON_ASSET" ] && [ "$CHANGED" != "unknown" ]; then
|
||||
if [ -z "$CHANGED" ] || [ "$CHANGED" = "unknown" ]; then
|
||||
# No diff (workflow_dispatch with no new commits, or git error).
|
||||
# Registry may not have a stage-latest base image, so force full build.
|
||||
echo "asset_only=false" >> "$GITHUB_OUTPUT"
|
||||
echo "=> No changed files detected (workflow_dispatch?), forcing full build"
|
||||
elif [ -z "$NON_ASSET" ]; then
|
||||
echo "asset_only=true" >> "$GITHUB_OUTPUT"
|
||||
echo "=> Asset-only change detected, will use fast path"
|
||||
else
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#
|
||||
# CI pre-build steps (in stage.yaml):
|
||||
# - neuron-web: built by `elb build` → dist/neuron-landing
|
||||
# Last rebuilt: 2026-05-11
|
||||
|
||||
FROM ubuntu:24.04
|
||||
|
||||
|
||||
Vendored
+21
-20
@@ -1,3 +1,4 @@
|
||||
// elhtml_impl.c — El HTML element stubs.
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
@@ -19,11 +20,11 @@ el_val_t el_li(el_val_t attrs, el_val_t children);
|
||||
el_val_t el_p(el_val_t attrs, el_val_t children);
|
||||
el_val_t el_span(el_val_t attrs, el_val_t children);
|
||||
el_val_t el_form(el_val_t attrs, el_val_t children);
|
||||
el_val_t el_h1(el_val_t attrs, el_val_t text);
|
||||
el_val_t el_h2(el_val_t attrs, el_val_t text);
|
||||
el_val_t el_h3(el_val_t attrs, el_val_t text);
|
||||
el_val_t el_h4(el_val_t attrs, el_val_t text);
|
||||
el_val_t el_button(el_val_t attrs, el_val_t label);
|
||||
el_val_t el_h1(el_val_t attrs, el_val_t children);
|
||||
el_val_t el_h2(el_val_t attrs, el_val_t children);
|
||||
el_val_t el_h3(el_val_t attrs, el_val_t children);
|
||||
el_val_t el_h4(el_val_t attrs, el_val_t children);
|
||||
el_val_t el_button(el_val_t attrs, el_val_t children);
|
||||
el_val_t el_a(el_val_t href, el_val_t attrs, el_val_t children);
|
||||
el_val_t el_input(el_val_t type_attr, el_val_t attrs);
|
||||
el_val_t el_textarea(el_val_t attrs, el_val_t value);
|
||||
@@ -176,43 +177,43 @@ el_val_t el_form(el_val_t attrs, el_val_t children) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t el_h1(el_val_t attrs, el_val_t text) {
|
||||
el_val_t el_h1(el_val_t attrs, el_val_t children) {
|
||||
if (str_eq(attrs, EL_STR(""))) {
|
||||
return el_str_concat(el_str_concat(EL_STR("<h1>"), el_escape(text)), EL_STR("</h1>"));
|
||||
return el_str_concat(el_str_concat(EL_STR("<h1>"), children), EL_STR("</h1>"));
|
||||
}
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<h1 "), attrs), EL_STR(">")), el_escape(text)), EL_STR("</h1>"));
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<h1 "), attrs), EL_STR(">")), children), EL_STR("</h1>"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t el_h2(el_val_t attrs, el_val_t text) {
|
||||
el_val_t el_h2(el_val_t attrs, el_val_t children) {
|
||||
if (str_eq(attrs, EL_STR(""))) {
|
||||
return el_str_concat(el_str_concat(EL_STR("<h2>"), el_escape(text)), EL_STR("</h2>"));
|
||||
return el_str_concat(el_str_concat(EL_STR("<h2>"), children), EL_STR("</h2>"));
|
||||
}
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<h2 "), attrs), EL_STR(">")), el_escape(text)), EL_STR("</h2>"));
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<h2 "), attrs), EL_STR(">")), children), EL_STR("</h2>"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t el_h3(el_val_t attrs, el_val_t text) {
|
||||
el_val_t el_h3(el_val_t attrs, el_val_t children) {
|
||||
if (str_eq(attrs, EL_STR(""))) {
|
||||
return el_str_concat(el_str_concat(EL_STR("<h3>"), el_escape(text)), EL_STR("</h3>"));
|
||||
return el_str_concat(el_str_concat(EL_STR("<h3>"), children), EL_STR("</h3>"));
|
||||
}
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<h3 "), attrs), EL_STR(">")), el_escape(text)), EL_STR("</h3>"));
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<h3 "), attrs), EL_STR(">")), children), EL_STR("</h3>"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t el_h4(el_val_t attrs, el_val_t text) {
|
||||
el_val_t el_h4(el_val_t attrs, el_val_t children) {
|
||||
if (str_eq(attrs, EL_STR(""))) {
|
||||
return el_str_concat(el_str_concat(EL_STR("<h4>"), el_escape(text)), EL_STR("</h4>"));
|
||||
return el_str_concat(el_str_concat(EL_STR("<h4>"), children), EL_STR("</h4>"));
|
||||
}
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<h4 "), attrs), EL_STR(">")), el_escape(text)), EL_STR("</h4>"));
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<h4 "), attrs), EL_STR(">")), children), EL_STR("</h4>"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t el_button(el_val_t attrs, el_val_t label) {
|
||||
el_val_t el_button(el_val_t attrs, el_val_t children) {
|
||||
if (str_eq(attrs, EL_STR(""))) {
|
||||
return el_str_concat(el_str_concat(EL_STR("<button type=\"button\">"), el_escape(label)), EL_STR("</button>"));
|
||||
return el_str_concat(el_str_concat(EL_STR("<button type=\"button\">"), children), EL_STR("</button>"));
|
||||
}
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<button type=\"button\" "), attrs), EL_STR(">")), el_escape(label)), EL_STR("</button>"));
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("<button type=\"button\" "), attrs), EL_STR(">")), children), EL_STR("</button>"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+5
-11
@@ -800,17 +800,11 @@ fn account_download_card() -> String {
|
||||
el_div(
|
||||
"class=\"card-dark\" style=\"border-top:3px solid var(--navy)\"",
|
||||
el_p("class=\"card-label\"", "Download") +
|
||||
el_p("class=\"download-status\" style=\"color:var(--navy)\"", "Available now — macOS") +
|
||||
el_p("class=\"download-title\"", "Neuron for Mac") +
|
||||
el_p("class=\"download-body\"", "The macOS build (Apple Silicon) is ready to download now. Windows and Linux are coming soon. Your license key is in your launch email.") +
|
||||
el_div(
|
||||
"style=\"display:flex;gap:.75rem;flex-wrap:wrap;align-items:center\"",
|
||||
el_a(
|
||||
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-macos-arm64.dmg",
|
||||
"class=\"btn-primary\" download",
|
||||
account_signin_svg_download() + " Download for macOS"
|
||||
) +
|
||||
el_a("/downloads", "class=\"btn-ghost\"", "All platforms →")
|
||||
el_p("class=\"download-status\" style=\"color:var(--navy)\"", "Shipping within 30 days") +
|
||||
el_p("class=\"download-title\"", "Neuron for Mac & Windows") +
|
||||
el_p("class=\"download-body\"", "macOS and Windows simultaneously. You'll receive a download link and license key by email the moment it ships. Nothing to do right now.") +
|
||||
el_button("type=\"button\" class=\"download-btn-disabled\" disabled aria-disabled=\"true\"",
|
||||
account_signin_svg_download() + " Download arriving soon"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
el_li("", "Persistent memory - never resets")
|
||||
+ el_li("", "Local inference via Ollama (coming)")
|
||||
+ el_li("", "Bring your own API keys")
|
||||
+ el_li("", "Plugin marketplace access (coming soon)")
|
||||
+ el_li("", "3 marketplace plugins included")
|
||||
+ el_li("", "Core built-in capabilities")
|
||||
+ el_li("", "2 devices included")
|
||||
} else {
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
// components/downloads.el - Public downloads page.
|
||||
//
|
||||
// Landing-styled inner page (page_open_seo + nav + content + footer +
|
||||
// page_close, assembled by main.el). Client-side OS detection via
|
||||
// navigator.platform highlights the visitor's platform and updates the
|
||||
// detected-OS banner. macOS is the shipping build; Windows and Linux are
|
||||
// linked but clearly marked "coming soon" per the current product state
|
||||
// (Windows chat not yet enabled, Linux soul backend not yet complete).
|
||||
|
||||
from nav import { nav }
|
||||
from footer import { footer }
|
||||
|
||||
extern fn el_section(attrs: String, children: String) -> String
|
||||
extern fn el_div(attrs: String, children: String) -> String
|
||||
extern fn el_span(attrs: String, children: String) -> String
|
||||
extern fn el_h1(attrs: String, text: String) -> String
|
||||
extern fn el_p(attrs: String, children: String) -> String
|
||||
extern fn el_a(href: String, attrs: String, children: String) -> String
|
||||
|
||||
// Scoped presentation. Uses the landing stylesheet's CSS variables
|
||||
// (--navy, --head, --body, --t2, --t3) so it inherits the site palette.
|
||||
// No # colors or quoted font names inline — routed through vars to stay
|
||||
// clear of the elc CSS tokenizer edge cases.
|
||||
fn dl_style() -> String {
|
||||
"<style>" +
|
||||
".dl-wrap{max-width:960px}" +
|
||||
".dl-detected{font-size:.85rem;font-weight:500;color:var(--navy);margin:1.5rem 0 2.5rem;min-height:1.2em}" +
|
||||
".dl-card{padding:2rem}" +
|
||||
".dl-featured{border-top:3px solid var(--navy)}" +
|
||||
".dl-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;margin-top:1.5rem}" +
|
||||
"@media (max-width:720px){.dl-grid{grid-template-columns:1fr}}" +
|
||||
".dl-badge{display:inline-block;font-size:.6rem;font-weight:700;letter-spacing:.18em;text-transform:uppercase;padding:.35rem .7rem;border-radius:999px;margin-bottom:1rem}" +
|
||||
".dl-badge-ready{color:var(--navy);background:rgba(0,82,160,.10)}" +
|
||||
".dl-badge-soon{color:var(--t3);background:rgba(0,0,0,.05)}" +
|
||||
".dl-title{font-family:var(--head);font-size:1.4rem;font-weight:600;margin-bottom:.4rem}" +
|
||||
".dl-sub{font-family:var(--body);font-weight:300;font-size:.9rem;color:var(--t2);line-height:1.6;margin-bottom:1.25rem}" +
|
||||
".dl-note{font-size:.78rem;color:var(--t3);line-height:1.6;margin-top:1rem}" +
|
||||
".dl-btnrow{display:flex;gap:.75rem;flex-wrap:wrap;align-items:center}" +
|
||||
"[data-detected=true]{box-shadow:0 0 0 2px rgba(0,82,160,.45);border-radius:2px}" +
|
||||
"</style>"
|
||||
}
|
||||
|
||||
fn dl_header() -> String {
|
||||
el_p("class=\"label reveal\"", "Download") +
|
||||
el_h1("class=\"display-lg reveal\" style=\"margin-top:.75rem\"", "Get Neuron.") +
|
||||
el_div("class=\"navy-line-left reveal\" style=\"width:4rem;margin:1.5rem 0 2rem\"", "") +
|
||||
el_p(
|
||||
"class=\"dl-sub reveal\" style=\"max-width:34rem;font-size:1rem\"",
|
||||
"Your AI that remembers you, running on your own machine. macOS is available to download today. Windows and Linux are on the way."
|
||||
) +
|
||||
el_p("id=\"os-detected\" class=\"dl-detected reveal\"", "Detecting your platform…")
|
||||
}
|
||||
|
||||
fn dl_mac_card() -> String {
|
||||
el_div(
|
||||
"id=\"card-mac\" class=\"card-dark dl-card dl-featured reveal\"",
|
||||
el_span("class=\"dl-badge dl-badge-ready\"", "Available now") +
|
||||
el_p("class=\"dl-title\"", "Neuron for macOS") +
|
||||
el_p("class=\"dl-sub\"", "Apple Silicon (M1–M4). macOS 13 Ventura or later.") +
|
||||
el_div(
|
||||
"class=\"dl-btnrow\"",
|
||||
el_a(
|
||||
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-macos-arm64.dmg",
|
||||
"class=\"btn-primary\" download",
|
||||
"Download for macOS →"
|
||||
)
|
||||
) +
|
||||
el_p("class=\"dl-note\"", "Version 1.1.0 · Apple Silicon · .dmg")
|
||||
)
|
||||
}
|
||||
|
||||
fn dl_windows_card() -> String {
|
||||
el_div(
|
||||
"id=\"card-windows\" class=\"card-dark dl-card reveal\"",
|
||||
el_span("class=\"dl-badge dl-badge-soon\"", "Coming soon") +
|
||||
el_p("class=\"dl-title\"", "Neuron for Windows") +
|
||||
el_p("class=\"dl-sub\"", "Windows 10 and 11 (x64).") +
|
||||
el_div(
|
||||
"class=\"dl-btnrow\"",
|
||||
el_a(
|
||||
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-windows-x64.msi",
|
||||
"class=\"btn-ghost\" download",
|
||||
"Download .msi"
|
||||
)
|
||||
) +
|
||||
el_p(
|
||||
"class=\"dl-note\"",
|
||||
"The Windows build is still landing — this link may not be live yet. Chat is not enabled on Windows in this release."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn dl_linux_card() -> String {
|
||||
el_div(
|
||||
"id=\"card-linux\" class=\"card-dark dl-card reveal\"",
|
||||
el_span("class=\"dl-badge dl-badge-soon\"", "Coming soon") +
|
||||
el_p("class=\"dl-title\"", "Neuron for Linux") +
|
||||
el_p("class=\"dl-sub\"", "Debian / Ubuntu (.deb) or portable (.AppImage), x64.") +
|
||||
el_div(
|
||||
"class=\"dl-btnrow\"",
|
||||
el_a(
|
||||
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-linux-x64.deb",
|
||||
"class=\"btn-ghost\" download",
|
||||
"Download .deb"
|
||||
) +
|
||||
el_a(
|
||||
"https://downloads.neurontechnologies.ai/installers/Neuron-1.1.0-linux-x64.AppImage",
|
||||
"class=\"btn-ghost\" download",
|
||||
"Download .AppImage"
|
||||
)
|
||||
) +
|
||||
el_p(
|
||||
"class=\"dl-note\"",
|
||||
"Linux packages install today, but the on-device soul backend is still being finished. Full support is coming soon."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn dl_fineprint() -> String {
|
||||
el_p(
|
||||
"class=\"dl-note reveal\" style=\"margin-top:2.5rem\"",
|
||||
"All installers are served from downloads.neurontechnologies.ai. After checkout your license key arrives by email — manage it from your " +
|
||||
el_a("/account", "style=\"color:var(--navy)\"", "account") +
|
||||
"."
|
||||
)
|
||||
}
|
||||
|
||||
// Inline OS detection. CSP allows script-src 'unsafe-inline'. Single-quoted
|
||||
// JS, ASCII only, no // comments — keeps the .el string simple and avoids
|
||||
// tokenizer surprises.
|
||||
fn dl_script() -> String {
|
||||
"<script>(function(){var p=(navigator.platform||'')+' '+(navigator.userAgent||'');var os='mac';if(/Win/i.test(p)){os='windows';}else if(/Linux|X11/i.test(p)&&!/Android/i.test(p)){os='linux';}else if(/Mac/i.test(p)){os='mac';}var L={mac:'macOS',windows:'Windows',linux:'Linux'};var b=document.getElementById('os-detected');if(b){b.textContent=(os==='mac')?'We detected macOS on this device. You are ready to download.':('We detected '+L[os]+'. '+L[os]+' support is coming soon; macOS is available to download today.');}var c=document.getElementById('card-'+os);if(c){c.setAttribute('data-detected','true');}})();</script>"
|
||||
}
|
||||
|
||||
fn downloads_content() -> String {
|
||||
let grid: String = el_div("class=\"dl-grid\"", dl_windows_card() + dl_linux_card())
|
||||
el_section(
|
||||
"id=\"downloads\" aria-label=\"Download Neuron\"",
|
||||
el_div(
|
||||
"class=\"container dl-wrap\"",
|
||||
dl_header() + dl_mac_card() + grid + dl_fineprint()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn downloads_page() -> String {
|
||||
return nav() + dl_style() + downloads_content() + footer() + dl_script()
|
||||
}
|
||||
+3
-17
@@ -1,20 +1,6 @@
|
||||
// checkout-free.el -- Free plan: show success panel after auth completes.
|
||||
// Watches the auth-badge element; when it becomes visible, hides the auth
|
||||
// section and shows the free-success panel. No card required for free tier.
|
||||
// Compiled with: elc --target=js --bundle --minify --obfuscate
|
||||
// checkout-free.el -- RETIRED. Free plan now uses the standard Stripe
|
||||
// payment flow (checkout-stripe.el) with a $0 PaymentIntent for age
|
||||
// verification. This file is no longer compiled or loaded.
|
||||
|
||||
fn main() -> Void {
|
||||
native_js("(function() {
|
||||
var success = document.getElementById('free-success');
|
||||
var auth = document.getElementById('auth-section');
|
||||
if (!success) return;
|
||||
var timer = setInterval(function() {
|
||||
var badge = document.getElementById('auth-badge');
|
||||
if (badge && badge.offsetParent !== null) {
|
||||
if (auth) auth.style.display = 'none';
|
||||
success.style.display = '';
|
||||
clearInterval(timer);
|
||||
}
|
||||
}, 150);
|
||||
})()")
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ fn main() -> Void {
|
||||
if (spinner) spinner.style.display = loading ? '' : 'none';
|
||||
}
|
||||
|
||||
// Free plan has no payment form — bail out entirely.
|
||||
if (str_eq(PLAN, 'free')) return;
|
||||
// Free plan: Stripe SetupIntent for age verification (card saved, never charged).
|
||||
// Falls through to the same Stripe init path — server returns setup_mode=true for free.
|
||||
|
||||
window._neuronMode = 'payment';
|
||||
var paymentEl = null;
|
||||
@@ -101,7 +101,7 @@ fn main() -> Void {
|
||||
if (submitLabel) {
|
||||
submitLabel.textContent = window._neuronMode === 'setup'
|
||||
? 'Save my card - no charge today →'
|
||||
: 'Complete purchase →';
|
||||
: PLAN === 'free' ? 'Verify age & get started →' : 'Complete purchase →';
|
||||
}
|
||||
waitForStripe(function() {
|
||||
if (!stripe) stripe = Stripe(STRIPE_PK);
|
||||
|
||||
+12
-24
@@ -87,7 +87,6 @@ from checkout import { checkout_page }
|
||||
from safety import { safety }
|
||||
from gallery import { gallery_page }
|
||||
from account import { account_page }
|
||||
from downloads import { downloads_page }
|
||||
|
||||
// ── Share-card HTML allowlist ─────────────────────────────────────────────────
|
||||
//
|
||||
@@ -638,7 +637,7 @@ fn handle_request_inner(method: String, path: String, headers: Map, body: String
|
||||
"Secure your Founding Member spot. Pay once, $199 lifetime — Neuron inference included at launch, priced below the major APIs. First 1,000 only."
|
||||
} else {
|
||||
if str_eq(plan, "free") {
|
||||
"Create your free Neuron account. No credit card required. Your AI that remembers you — runs on your machine, never resets."
|
||||
"Create your free Neuron account. A card verifies you're 18+ — you won't be charged. Your AI that remembers you, runs on your machine, never resets."
|
||||
} else {
|
||||
"Subscribe to Neuron Professional for $19/month. The AI that remembers you — persistent memory, runs locally, bring your own API keys."
|
||||
}
|
||||
@@ -698,23 +697,21 @@ fn handle_request_inner(method: String, path: String, headers: Map, body: String
|
||||
}
|
||||
}
|
||||
|
||||
// Free tier: creates a SetupIntent for age verification (18+ requirement).
|
||||
// No charge — but the user must provide a valid payment method.
|
||||
// Free tier: $0 PaymentIntent for age verification (18+ requirement).
|
||||
// Verifies card is valid. No charge, no capture.
|
||||
// Note: setup_future_usage cannot be used with amount=0.
|
||||
if str_eq(plan, "free") {
|
||||
let free_si_body: String = "automatic_payment_methods[enabled]=true"
|
||||
+ "&usage=off_session"
|
||||
let free_pi_body: String = "amount=0"
|
||||
+ "¤cy=usd"
|
||||
+ "&payment_method_types[]=card"
|
||||
+ "&metadata[plan]=free"
|
||||
+ "&metadata[purpose]=age_verification"
|
||||
let free_si_body = if !str_eq(pi_cus_id, "") { free_si_body + "&customer=" + pi_cus_id } else { free_si_body }
|
||||
let free_si_resp: String = http_post_form_auth(
|
||||
"https://api.stripe.com/v1/setup_intents",
|
||||
free_si_body,
|
||||
let free_pi_body = if !str_eq(pi_cus_id, "") { free_pi_body + "&customer=" + pi_cus_id } else { free_pi_body }
|
||||
let free_pi_resp: String = http_post_form_auth(
|
||||
"https://api.stripe.com/v1/payment_intents",
|
||||
free_pi_body,
|
||||
auth_header)
|
||||
if str_starts_with(free_si_resp, "{") {
|
||||
let inner: String = str_slice(free_si_resp, 1, str_len(free_si_resp))
|
||||
return "{\"setup_mode\":true,\"plan\":\"free\"," + inner
|
||||
}
|
||||
return free_si_resp
|
||||
return free_pi_resp
|
||||
}
|
||||
|
||||
// Setup-mode path: save payment method, do not charge. Only valid
|
||||
@@ -1972,15 +1969,6 @@ fn handle_request_inner(method: String, path: String, headers: Map, body: String
|
||||
) + badge_css + success_body + page_close()
|
||||
}
|
||||
|
||||
// ── Downloads ─────────────────────────────────────────────────────────────
|
||||
// Public installer page. macOS ships today; Windows/Linux linked but marked
|
||||
// coming soon. Live-rendered like /about (page shell wraps downloads_page()).
|
||||
if str_starts_with(path, "/downloads") {
|
||||
let dl_title: String = "Download Neuron - macOS, Windows & Linux"
|
||||
let dl_desc: String = "Download Neuron for macOS. Your AI that remembers you, running on your own machine. Windows and Linux support is on the way."
|
||||
return page_open_seo(dl_title, dl_desc, "/downloads", dl_desc, "false") + downloads_page() + page_close()
|
||||
}
|
||||
|
||||
// ── Account dashboard ─────────────────────────────────────────────────────
|
||||
// Use prefix match so OAuth/email-confirmation redirects with query strings still hit.
|
||||
if str_starts_with(path, "/account") {
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ fn marketplace_categories() -> String {
|
||||
|
||||
el_div(
|
||||
"class=\"marketplace-categories reveal\" style=\"transition-delay:320ms\"",
|
||||
marketplace_tags_block("Connectors (rolling out)", connectors) +
|
||||
marketplace_tags_block("Connectors - day one", connectors) +
|
||||
marketplace_tags_block("Following launch", following) +
|
||||
el_div(
|
||||
"",
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ fn pricing_free_features() -> String {
|
||||
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Local inference via Ollama (coming)")) +
|
||||
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Neuron Inference included when it launches - Q3 2026")) +
|
||||
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Unlimited projects")) +
|
||||
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Plugin marketplace access (coming soon)")) +
|
||||
el_li("", el_span("class=\"dash\"", "-") + el_span("", "3 marketplace plugins included")) +
|
||||
el_li("", el_span("class=\"dash\"", "-") + el_span("", "Core built-in capabilities"))
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ fn pricing(sold: Int, total: Int) -> String {
|
||||
el_span("class=\"pricing-price\"", "$0") +
|
||||
el_span("class=\"pricing-cadence\"", "forever")
|
||||
) +
|
||||
el_p("class=\"pricing-tagline\"", "Start building your memory. No card required.") +
|
||||
el_p("class=\"pricing-tagline\"", "Start building your memory. Card required for age verification — you won't be charged.") +
|
||||
el_ul("class=\"pricing-features\"", pricing_free_features()) +
|
||||
el_div("style=\"flex:1\"", "") +
|
||||
el_div(
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ fn page_head() -> String {
|
||||
return page_head_base()
|
||||
+ page_seo_block(
|
||||
"Neuron — The AI That Remembers You",
|
||||
"Every AI resets when you close the tab. Neuron doesn't. Runs on your machine. Remembers everything. Start free — no credit card required.",
|
||||
"Every AI resets when you close the tab. Neuron doesn't. Runs on your machine. Remembers everything. Start free.",
|
||||
"/",
|
||||
"Every other AI forgets you. Neuron doesn't. Runs on your machine, builds a persistent memory over time, and gets sharper the longer you use it. Free tier available."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user