Add El source files for all client-side JS

Recovers original JS from git history and ports it into proper El source
files under src/js/. Each file wraps the original JS in a native_js call
inside a main() function, making it valid El that compiles to a
self-contained IIFE via elc --target=js --bundle.

Files added:
  src/js/account-auth.el       - Supabase OTP magic-link (sendMagicLink)
  src/js/account-dashboard.el  - Account dashboard: session, plan card, family
  src/js/chat-widget.el        - Demo chat widget (neuronDemoToggle/Send/Reset)
  src/js/checkout-auth.el      - Checkout auth: OAuth, email sign-in/up
  src/js/checkout-free.el      - Free plan: auth-badge watch -> payment reveal
  src/js/checkout-stripe.el    - Stripe Payment Element (reads NEURON_CFG)
  src/js/enterprise.el         - Enterprise inquiry form + headcount filter
  src/js/environmental.el      - Efficiency calculator slider
  src/js/gallery.el            - Gallery nav, search/sort, Supabase voting
  src/js/main.el               - Share page voting + copyForPlatform
  src/js/marketplace.el        - Developer interest form
  src/js/nav.el                - Nav hamburger + Mission dropdown
  src/js/styles.el             - Landing: nav scroll, reveal, founding counter
This commit is contained in:
Will Anderson
2026-05-04 11:23:21 -05:00
parent 246a5f0967
commit 94f6e749a0
44 changed files with 1625 additions and 642 deletions
+7 -3
View File
@@ -59,12 +59,18 @@ jobs:
- name: Configure docker auth for Artifact Registry - name: Configure docker auth for Artifact Registry
run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
- name: Get elc (pre-built linux/amd64) - name: Get elc (pre-built linux/amd64 from El repo)
run: | run: |
set -euo pipefail set -euo pipefail
ELC_SRC="$EL_HOME/dist/platform/elc-linux-amd64"
if [ -f "$ELC_SRC" ]; then
cp "$ELC_SRC" "$EL_HOME/dist/platform/elc"
chmod +x "$EL_HOME/dist/platform/elc"
else
curl -fL -o "$EL_HOME/dist/platform/elc" \ curl -fL -o "$EL_HOME/dist/platform/elc" \
https://git.neuralplatform.ai/neuron-technologies/el/releases/download/v1.2.1/elc-linux-amd64 https://git.neuralplatform.ai/neuron-technologies/el/releases/download/v1.2.1/elc-linux-amd64
chmod +x "$EL_HOME/dist/platform/elc" chmod +x "$EL_HOME/dist/platform/elc"
fi
- name: Compute image tag - name: Compute image tag
id: tag id: tag
@@ -77,8 +83,6 @@ jobs:
run: touch src/index.html src/about.html src/terms.html src/enterprise-terms.html run: touch src/index.html src/about.html src/terms.html src/enterprise-terms.html
- name: Build image (local only — no push) - name: Build image (local only — no push)
env:
EXTRACT_JS: '1'
run: ./build-stage.sh "${{ steps.tag.outputs.tag }}" run: ./build-stage.sh "${{ steps.tag.outputs.tag }}"
- name: Local smoke test - name: Local smoke test
+11 -3
View File
@@ -93,13 +93,23 @@ jobs:
- name: Configure docker auth for Artifact Registry - name: Configure docker auth for Artifact Registry
run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
- name: Get elc (pre-built linux/amd64) - name: Get elc (pre-built linux/amd64 from El repo)
if: steps.changetype.outputs.asset_only != 'true' if: steps.changetype.outputs.asset_only != 'true'
run: | run: |
set -euo pipefail set -euo pipefail
# Copy the El C-compiler binary from the cloned El repo into the expected path.
# The JS-capable elc for client-side compilation is committed in bin/elc-linux-amd64
# and used automatically by build-stage.sh on linux/amd64.
ELC_SRC="$EL_HOME/dist/platform/elc-linux-amd64"
if [ -f "$ELC_SRC" ]; then
cp "$ELC_SRC" "$EL_HOME/dist/platform/elc"
chmod +x "$EL_HOME/dist/platform/elc"
else
# Fallback: download v1.2.1 C-compiler if the repo binary is absent
curl -fL -o "$EL_HOME/dist/platform/elc" \ curl -fL -o "$EL_HOME/dist/platform/elc" \
https://git.neuralplatform.ai/neuron-technologies/el/releases/download/v1.2.1/elc-linux-amd64 https://git.neuralplatform.ai/neuron-technologies/el/releases/download/v1.2.1/elc-linux-amd64
chmod +x "$EL_HOME/dist/platform/elc" chmod +x "$EL_HOME/dist/platform/elc"
fi
- name: Compute image tag - name: Compute image tag
id: tag id: tag
@@ -118,8 +128,6 @@ jobs:
- name: Build image (build-stage.sh) - name: Build image (build-stage.sh)
if: steps.changetype.outputs.asset_only != 'true' if: steps.changetype.outputs.asset_only != 'true'
env:
EXTRACT_JS: '1'
run: | run: |
./build-stage.sh "${{ steps.tag.outputs.tag }}" ./build-stage.sh "${{ steps.tag.outputs.tag }}"
docker tag "marketing:${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.image }}" docker tag "marketing:${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.image }}"
+10
View File
@@ -11,6 +11,16 @@ src/about.html
src/terms.html src/terms.html
src/enterprise-terms.html src/enterprise-terms.html
# Compiled client-side JS (generated by elc --target=js at build time).
# The El sources live in src/js/; the compiled output is never committed.
dist/js/
# El JS runtime staged temporarily during build (auto-cleaned by build-stage.sh).
src/js/el_runtime.js
# Old extracted JS assets (replaced by elc-compiled dist/js/).
src/assets/js/
# Track hand-written source under dist/ that is NOT generated by elc. # Track hand-written source under dist/ that is NOT generated by elc.
# These are the C stub shims and entry scripts the Dockerfile.stage COPYs # These are the C stub shims and entry scripts the Dockerfile.stage COPYs
# into the image; without these the build cannot produce a working # into the image; without these the build cannot produce a working
+2 -1
View File
@@ -62,7 +62,7 @@ RUN apt-get update \
ca-certificates \ ca-certificates \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& groupadd -r landing && useradd -r -g landing landing \ && groupadd -r landing && useradd -r -g landing landing \
&& mkdir -p /srv/landing/assets /srv/landing/shares \ && mkdir -p /srv/landing/assets /srv/landing/js /srv/landing/shares \
&& mkdir -p /srv/soul/engram-demo \ && mkdir -p /srv/soul/engram-demo \
&& chown -R landing:landing /srv/landing /srv/soul && chown -R landing:landing /srv/landing /srv/soul
@@ -73,6 +73,7 @@ COPY --from=builder /build/soul-demo /usr/local/bin/soul-demo
COPY dist/engram-snapshot.json /srv/soul/engram-demo/snapshot.json COPY dist/engram-snapshot.json /srv/soul/engram-demo/snapshot.json
COPY src/assets /srv/landing/assets COPY src/assets /srv/landing/assets
COPY dist/js /srv/landing/js
COPY src/llms.txt /srv/landing/llms.txt COPY src/llms.txt /srv/landing/llms.txt
# Pre-rendered HTML shells (about, terms, enterprise-terms, index) used as # Pre-rendered HTML shells (about, terms, enterprise-terms, index) used as
# fallback when the El page-builder hasn't been seeded yet at startup. # fallback when the El page-builder hasn't been seeded yet at startup.
BIN
View File
Binary file not shown.
+32 -19
View File
@@ -4,23 +4,22 @@
# #
# Pipeline: # Pipeline:
# 1. Stage the foundation El runtime into ./runtime/. # 1. Stage the foundation El runtime into ./runtime/.
# 2. Concatenate src/*.el into dist/main-combined.el (component-first, # 2. Compile client-side El sources (src/js/*.el) to dist/js/*.js using
# the JS-capable elc binary at bin/elc-linux-amd64 (CI) or the local
# elc (dev). Output is gitignored and rebuilt every run.
# 3. Concatenate src/*.el into dist/main-combined.el (component-first,
# main.el last; matches the historical order from build-local.sh). # main.el last; matches the historical order from build-local.sh).
# 3. Compile dist/main-combined.el → dist/main.c using the canonical # 4. Compile dist/main-combined.el → dist/main.c using the canonical
# native elc at foundation/el/dist/platform/elc. # native elc at foundation/el/dist/platform/elc.
# 4. Inject the host-side stub forward declarations into dist/main.c # 5. Inject the host-side stub forward declarations into dist/main.c
# (sed header rewrite, same set as the prior in-Dockerfile sed). # (sed header rewrite, same set as the prior in-Dockerfile sed).
# 5. docker buildx build --platform linux/amd64 -f Dockerfile.stage. # 6. docker buildx build --platform linux/amd64 -f Dockerfile.stage.
# #
# bootstrap.py is no longer in the build path. The container image now # bootstrap.py is no longer in the build path. The container image now
# expects dist/main.c to be a finished C source — it just runs cc on it. # expects dist/main.c to be a finished C source — it just runs cc on it.
# #
# Inline-JS extraction is gated by EXTRACT_JS=1 just like build-local.sh
# was. Production deploys should always extract.
#
# Usage: # Usage:
# ./build-stage.sh <tag> — build marketing:<tag> # ./build-stage.sh <tag> — build marketing:<tag>
# EXTRACT_JS=1 ./build-stage.sh X — also extract inline JS to assets
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")" cd "$(dirname "$0")"
@@ -32,6 +31,17 @@ EL_HOME="${EL_HOME:-${LANDING_DIR}/../../foundation/el}"
ELC="${EL_HOME}/dist/platform/elc" ELC="${EL_HOME}/dist/platform/elc"
RUNTIME_SRC="${EL_HOME}/el-compiler/runtime" RUNTIME_SRC="${EL_HOME}/el-compiler/runtime"
# JS-capable elc: prefer committed bin/elc-linux-amd64 on CI (linux/amd64),
# fall back to the local elc from the El checkout on macOS dev.
if [ -f "${LANDING_DIR}/bin/elc-linux-amd64" ] && uname -m | grep -q x86_64; then
ELC_JS="${LANDING_DIR}/bin/elc-linux-amd64"
elif [ -x "${ELC}" ]; then
ELC_JS="${ELC}"
else
echo "elc for JS compilation not found — expected bin/elc-linux-amd64 or ${ELC}" >&2
exit 1
fi
if [ ! -x "${ELC}" ]; then if [ ! -x "${ELC}" ]; then
echo "elc not found at ${ELC}" >&2 echo "elc not found at ${ELC}" >&2
exit 1 exit 1
@@ -42,17 +52,20 @@ mkdir -p runtime dist
cp "${RUNTIME_SRC}/el_runtime.c" runtime/ cp "${RUNTIME_SRC}/el_runtime.c" runtime/
cp "${RUNTIME_SRC}/el_runtime.h" runtime/ cp "${RUNTIME_SRC}/el_runtime.h" runtime/
# Optional inline-JS extraction. Off by default for fast dev iteration; the # The JS compiler looks for el_runtime.js in the same directory as the
# script is idempotent so flipping the flag on a prior tree just reuses # source file being compiled. Copy it there so --bundle can inline it.
# previously-extracted assets. cp "${RUNTIME_SRC}/el_runtime.js" "${LANDING_DIR}/src/js/"
if [[ "${EXTRACT_JS:-0}" == "1" ]]; then
echo "==> Extracting inline JS → src/assets/js/" echo "==> Compiling client-side El (src/js/*.el) → dist/js/"
if [ ! -x "node_modules/.bin/terser" ] || [ ! -x "node_modules/.bin/javascript-obfuscator" ]; then mkdir -p dist/js
echo " installing terser + javascript-obfuscator (no-save)..." for f in "${LANDING_DIR}/src/js/"*.el; do
npm install --no-save --silent terser javascript-obfuscator name=$(basename "$f" .el)
fi "${ELC_JS}" --target=js --bundle --minify --obfuscate "$f" > "${LANDING_DIR}/dist/js/${name}.js"
python3 scripts/extract-js.py echo " compiled: src/js/${name}.el → dist/js/${name}.js"
fi done
# Clean up the staged runtime (not a source file)
rm -f "${LANDING_DIR}/src/js/el_runtime.js"
echo "==> Combining El sources → dist/main-combined.el" echo "==> Combining El sources → dist/main-combined.el"
COMPONENTS=(nav hero pillars how_it_works inference efficiency comparison COMPONENTS=(nav hero pillars how_it_works inference efficiency comparison
-465
View File
@@ -1,465 +0,0 @@
#!/usr/bin/env python3
"""
extract-js.py — Extract inline <script> blocks from El source files into
external, minified, obfuscated .js files served from /assets/js/.
Why
---
The El landing page embeds JavaScript inline as escaped string literals.
That bloats the HTML payload and exposes implementation. This script
extracts each substantial inline block to a hashed file under
src/assets/js/, replaces the El-side block with
`<script src="/assets/js/<hash>.js" defer></script>`, and writes a manifest
for cache-busting.
Behaviour
---------
- Skips `<script src=...>` external loaders (kept as-is).
- Skips `<script type="application/ld+json">` (data, not code).
- Skips inline blocks shorter than MIN_INLINE_BYTES (defaults to 200).
- Handles El-side runtime interpolation `'" + var + "'`. For each
interpolated identifier the extractor emits a tiny inline shim
`<script>window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.<id>="\"+id+\"";</script>`
immediately before the external script tag, and rewrites the JS body
to read from `window.NEURON_CFG.<id>` so the external file is fully
static and runtime values are still injected at render time.
- Pipeline per file: terser (compress + mangle, reserved globals
preserved) → javascript-obfuscator (string-array, base64, hex names).
Idempotency
-----------
- Running twice is a no-op: blocks already rewritten to
`<script src="/assets/js/...">` are not re-extracted.
- Filenames are content-hashed (sha1[:12]), so unchanged source produces
the same output filename and an unchanged manifest.
"""
from __future__ import annotations
import hashlib
import json
import os
import re
import subprocess
import sys
from pathlib import Path
from typing import List, Optional
# ── Paths ────────────────────────────────────────────────────────────────────
REPO_ROOT = Path(__file__).resolve().parent.parent
SRC_DIR = REPO_ROOT / "src"
ASSET_DIR = SRC_DIR / "assets" / "js"
MANIFEST = ASSET_DIR / "manifest.json"
# Prefer locally installed binaries; fall back to npx.
NODE_BIN = REPO_ROOT / "node_modules" / ".bin"
TERSER = str(NODE_BIN / "terser") if (NODE_BIN / "terser").exists() else "npx terser"
OBFUSCATOR = (
str(NODE_BIN / "javascript-obfuscator")
if (NODE_BIN / "javascript-obfuscator").exists()
else "npx javascript-obfuscator"
)
# ── Config ───────────────────────────────────────────────────────────────────
MIN_INLINE_BYTES = 200 # below this we keep inline (analytics shims, redirects)
# Globals referenced from outside the script (HTML onclick=, onchange=, etc).
# These names cannot be mangled or obfuscated or buttons stop working.
RESERVED_GLOBALS = [
# Chat widget
"neuronDemoToggle",
"neuronDemoSend",
"neuronDemoReset",
# Auth flows (account + checkout)
"signInWith",
"signInWithEmail",
"signUpWithEmail",
"sendMagicLink",
"signOut",
"resetPassword",
"sendResetEmail",
"updatePassword",
"showSignIn",
"showSignUp",
"hideReset",
# Misc handlers
"setSort",
"addFamilyMember",
"removeFamilyMember",
"copyForPlatform",
"entHeadcountChange",
# Runtime config bootstrap (do not let obfuscator mangle this name)
"NEURON_CFG",
]
# Files to scan. The extractor walks every .el file in src/ but we filter
# to skip the leaf component files known to contain no <script> blocks.
EL_FILES = sorted(SRC_DIR.glob("*.el"))
# ── El extraction ────────────────────────────────────────────────────────────
# El uses backslash-escaped quotes inside its string literals. Inside an
# El string the JS body looks like:
# <script>\n var x = '\"hello\"';\n</script>
# i.e. quotes are written as \". We unescape on the way out, re-escape on
# the way in.
# We match a *plain* opening <script> tag followed by JS body and </script>.
# Cases we deliberately don't match:
# - <script src=...>...</script> (external loader)
# - <script async ...>...</script> (external loader, even with body)
# - <script type="application/ld+json">...</script> (structured data)
SCRIPT_BLOCK_RE = re.compile(
r"<script>\s*\n(.*?)\n\s*</script>",
re.DOTALL,
)
# An interpolation point inside a JS body: `'" + ident + "'` (single-quoted
# string in JS containing an El concat). We capture the bare identifier.
INTERP_RE = re.compile(r"""'"\s*\+\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\+\s*"'""")
def is_skip_block(body: str) -> bool:
"""True if the block is too small or non-JS to be worth extracting."""
stripped = body.strip()
if len(stripped) < MIN_INLINE_BYTES:
return True
return False
def el_unescape(s: str) -> str:
r"""Mirror the El lexer's string-escape rules (foundation/el/bootstrap.py):
\n -> LF, \t -> TAB, \r -> CR, \" -> ", \\ -> \, \<X> -> X for any X.
The catch-all means \' inside an El string yields a bare apostrophe;
if we don't replicate that here, an extracted block like
`onclick=\"window.location.href=\\\'/contact\\\'\"` parses with stray
backslashes that terser then rejects as bad escape sequences."""
out = []
i = 0
n = len(s)
while i < n:
c = s[i]
if c == "\\" and i + 1 < n:
nxt = s[i + 1]
if nxt == "n":
out.append("\n")
elif nxt == "t":
out.append("\t")
elif nxt == "r":
out.append("\r")
elif nxt == '"':
out.append('"')
elif nxt == "\\":
out.append("\\")
else:
# Catch-all: unrecognised escape collapses to the second char,
# exactly as the El lexer does.
out.append(nxt)
i += 2
continue
out.append(c)
i += 1
return "".join(out)
def el_escape_attr(s: str) -> str:
"""Escape a string for use inside an El "..." literal. We only need to
escape the double quote — backslash is already legal in URLs and we
don't emit any."""
return s.replace("\\", "\\\\").replace('"', '\\"')
def sha12(content: str) -> str:
return hashlib.sha1(content.encode("utf-8")).hexdigest()[:12]
def run(cmd: List[str], **kwargs) -> subprocess.CompletedProcess:
proc = subprocess.run(cmd, check=False, capture_output=True, text=True, **kwargs)
if proc.returncode != 0:
sys.stderr.write(
f"\n[extract-js] command failed: {' '.join(cmd[:2])} ...\n"
f" exit={proc.returncode}\n"
f" stdout: {proc.stdout[:500]}\n"
f" stderr: {proc.stderr[:2000]}\n"
)
raise subprocess.CalledProcessError(
proc.returncode, cmd, proc.stdout, proc.stderr
)
return proc
def minify_and_obfuscate(js: str, hash_id: str) -> str:
"""Run js through terser then javascript-obfuscator. Returns the final
obfuscated source."""
raw_path = ASSET_DIR / f".{hash_id}.raw.js"
min_path = ASSET_DIR / f".{hash_id}.min.js"
out_path = ASSET_DIR / f"{hash_id}.js"
def _cleanup_scratch() -> None:
raw_path.unlink(missing_ok=True)
min_path.unlink(missing_ok=True)
raw_path.write_text(js, encoding="utf-8")
reserved_arg = ",".join(RESERVED_GLOBALS)
# terser
terser_cmd = TERSER.split() + [
str(raw_path),
"--compress",
"passes=2,drop_console=true,drop_debugger=true",
"--mangle",
f"reserved=[{reserved_arg}]",
"--output",
str(min_path),
]
try:
run(terser_cmd)
except Exception:
_cleanup_scratch()
raise
# javascript-obfuscator
obf_cmd = OBFUSCATOR.split() + [
str(min_path),
"--output",
str(out_path),
"--compact",
"true",
"--simplify",
"true",
"--string-array",
"true",
"--string-array-encoding",
"base64",
"--string-array-threshold",
"0.75",
"--identifier-names-generator",
"hexadecimal",
"--rename-globals",
"false",
"--self-defending",
"false",
"--reserved-names",
",".join(RESERVED_GLOBALS),
]
try:
run(obf_cmd)
except Exception:
_cleanup_scratch()
raise
# Tidy up scratch files; keep only the final .js
_cleanup_scratch()
return out_path.read_text(encoding="utf-8")
def find_script_blocks(text: str) -> List[tuple[int, int, str]]:
"""Return (start, end, body) for every plain <script>…</script> block.
`start`/`end` are file offsets covering the entire match (the tags
too)."""
out: List[tuple[int, int, str]] = []
for m in SCRIPT_BLOCK_RE.finditer(text):
out.append((m.start(), m.end(), m.group(1)))
return out
def process_block(raw_body_escaped: str) -> Optional[tuple[str, str, List[str]]]:
"""Process a single <script> body.
Returns (hash_id, replacement_html_el_escaped, interpolated_ids) or
None if the block should remain inline.
The replacement HTML is already El-escaped (so it can be slotted back
into the El source string verbatim).
"""
if is_skip_block(raw_body_escaped):
return None
# Convert El-string-escaped JS into real JS source.
js_with_interp = el_unescape(raw_body_escaped)
# Find interpolation identifiers and rewrite them to read from
# window.NEURON_CFG.<id>. We dedupe in occurrence order.
seen: List[str] = []
def repl(m: re.Match) -> str:
ident = m.group(1)
if ident not in seen:
seen.append(ident)
return f"window.NEURON_CFG.{ident}"
js_static = INTERP_RE.sub(repl, js_with_interp)
# If interpolation existed, we need to wrap the JS body so it reads
# the runtime config. Strings come back as JS strings, so we just
# inject `var X = window.NEURON_CFG.X` shims to keep call sites
# readable. Actually simpler: leave the call sites as
# `window.NEURON_CFG.X` — that's already what `repl` produced, and
# the original had `'" + var + "'` (a string), so the new value is a
# string too.
#
# Hash + minify the static JS.
hash_id = sha12(js_static)
minify_and_obfuscate(js_static, hash_id)
# Build replacement HTML for the El source.
parts: List[str] = []
if seen:
# Inline shim: bootstrap window.NEURON_CFG with runtime values.
# Each line: window.NEURON_CFG.<id> = "<el-interp>";
cfg_assigns = "".join(
f'window.NEURON_CFG.{ident}=\\"" + {ident} + "\\";'
for ident in seen
)
# The shim itself lives inline in the El string. The `\"` are
# already the right escape for the surrounding El "..." literal.
shim = (
"<script>window.NEURON_CFG=window.NEURON_CFG||{};"
+ cfg_assigns
+ "</script>"
)
parts.append(shim)
# External script tag, defer so it runs after parse but before
# DOMContentLoaded — that's compatible with `onclick=` handlers
# because they only fire on user interaction (post-load).
parts.append(
f'<script src=\\"/assets/js/{hash_id}.js\\" defer></script>'
)
return hash_id, "".join(parts), seen
EXISTING_REF_RE = re.compile(
r'<script\s+src=\\"/assets/js/([0-9a-f]{12})\.js\\"\s+defer></script>'
)
def collect_existing_refs(text: str) -> List[str]:
"""Find /assets/js/<hash>.js references already inlined into this El
file from a previous run. Returns hash IDs in document order."""
return [m.group(1) for m in EXISTING_REF_RE.finditer(text)]
def process_file(path: Path) -> tuple[int, int, List[dict]]:
"""Rewrite a single .el file, replacing extractable <script> blocks.
Returns (blocks_extracted, bytes_saved, manifest_entries). Manifest
entries always include both newly-extracted *and* previously-extracted
references so the manifest stays a complete inventory across reruns.
"""
text = path.read_text(encoding="utf-8")
original_len = len(text)
# Pick up existing references first (idempotency).
existing: List[dict] = []
for h in collect_existing_refs(text):
asset_path = ASSET_DIR / f"{h}.js"
if asset_path.exists():
existing.append(
{
"file": path.name,
"hash": h,
"asset": f"/assets/js/{h}.js",
"size": asset_path.stat().st_size,
"interpolated": [],
"note": "carried from prior run",
}
)
blocks = find_script_blocks(text)
if not blocks:
return 0, 0, existing
extracted = 0
new_entries: List[dict] = []
# Walk in reverse so offsets remain valid as we splice.
for start, end, body in reversed(blocks):
result = process_block(body)
if result is None:
continue
hash_id, replacement, interp_ids = result
text = text[:start] + replacement + text[end:]
extracted += 1
new_entries.append(
{
"file": path.name,
"hash": hash_id,
"asset": f"/assets/js/{hash_id}.js",
"size": (ASSET_DIR / f"{hash_id}.js").stat().st_size,
"interpolated": interp_ids,
}
)
if extracted:
path.write_text(text, encoding="utf-8")
bytes_saved = original_len - len(text)
return extracted, bytes_saved, existing + new_entries
def main() -> int:
ASSET_DIR.mkdir(parents=True, exist_ok=True)
total_blocks = 0
total_saved = 0
all_entries: List[dict] = []
for el in EL_FILES:
n, saved, entries = process_file(el)
if n:
print(
f" {el.name:25s} {n} block(s) extracted, "
f"{saved:6d} bytes pulled out"
)
total_blocks += n
total_saved += saved
# Always carry entries forward so the manifest is a complete
# inventory even when this run extracted zero new blocks.
all_entries.extend(entries)
# Sort manifest for stable output regardless of file walk order.
all_entries.sort(key=lambda e: (e["file"], e["hash"]))
# Garbage-collect orphan .js files in the asset dir whose hash is no
# longer referenced by any El source. Without this, edits to the
# original JS leave stale hashed files behind forever.
keep = {f"{e['hash']}.js" for e in all_entries}
keep.add("manifest.json")
removed: List[str] = []
for f in ASSET_DIR.iterdir():
if f.is_file() and f.name not in keep and not f.name.startswith("."):
f.unlink()
removed.append(f.name)
if removed:
print(f" pruned {len(removed)} orphan asset(s): {', '.join(sorted(removed))}")
MANIFEST.write_text(
json.dumps(
{
"generated_by": "scripts/extract-js.py",
"count": len(all_entries),
"entries": all_entries,
},
indent=2,
)
+ "\n",
encoding="utf-8",
)
print(
f"\n total: {total_blocks} block(s), "
f"{total_saved} bytes removed from El sources, "
f"{len(all_entries)} asset(s) → {ASSET_DIR.relative_to(REPO_ROOT)}"
)
return 0
if __name__ == "__main__":
sys.exit(main())
+2 -2
View File
@@ -892,8 +892,8 @@ fn account_page(supabase_url: String, supabase_anon_key: String) -> String {
<script src=\"https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js\"></script> <script src=\"https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js\"></script>
<script>window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.supabase_url=\"" + supabase_url + "\";window.NEURON_CFG.supabase_anon_key=\"" + supabase_anon_key + "\";</script> <script>window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.supabase_url=\"" + supabase_url + "\";window.NEURON_CFG.supabase_anon_key=\"" + supabase_anon_key + "\";</script>
<script src=\"/assets/js/77e923ac5855.js\" defer></script> <script src=\"/js/account-auth.js\" defer></script>
<script src=\"/assets/js/dadeb8ddb9a8.js\" defer></script> <script src=\"/js/account-dashboard.js\" defer></script>
</body> </body>
</html>" </html>"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
function a0_0x1591(){var _0x10c289=['BMf2lwHHBwj1CMDLCG','ywrK','C2v0qxr0CMLIDxrL','CxvLCNLtzwXLy3rVCKfSBa','zMfSC2u','ywrKrxzLBNrmAxn0zw5LCG','mJu4oduXn3rMq0LlsG','B3bLBG','CxvLCNLtzwXLy3rVCG','y2XPy2S','zM9YrwfJAa','yxjPys1LEhbHBMrLza','Dhj1zq','A2v5zg93BG','y2XHC3nmAxn0','Dg9Nz2XL','C3rVCfbYB3bHz2f0Aw9U','mtaXodi1ngX0EfLdDG','odi3nJvNA01dEwy','n05UrhjgAa','ndq2odmXrhrPu1bs','y29UDgfPBNm','lM5HDI1KCM9Wzg93BI1PDgvT','oe9gq1H5vW','ntmXmtCXmfbmCw9Krq','DgfYz2v0','ota5nJiYqujQBwvL','BMf2','mta0reHcyu1g','CMvTB3zL','mtiZnZmXngP2AejQuq','z2v0rwXLBwvUDej5swq','CMvZAxPL','A2v5'];a0_0x1591=function(){return _0x10c289;};return a0_0x1591();}function a0_0x1c65(_0xb184c1,_0x57ab9a){_0xb184c1=_0xb184c1-0x1d0;var _0x159170=a0_0x1591();var _0x1c65a5=_0x159170[_0xb184c1];if(a0_0x1c65['WtKexP']===undefined){var _0xe40532=function(_0x40a838){var _0x2d9cb5='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x170067='',_0xe74606='';for(var _0x4a7359=0x0,_0x30df79,_0x3883ed,_0x6ecaab=0x0;_0x3883ed=_0x40a838['charAt'](_0x6ecaab++);~_0x3883ed&&(_0x30df79=_0x4a7359%0x4?_0x30df79*0x40+_0x3883ed:_0x3883ed,_0x4a7359++%0x4)?_0x170067+=String['fromCharCode'](0xff&_0x30df79>>(-0x2*_0x4a7359&0x6)):0x0){_0x3883ed=_0x2d9cb5['indexOf'](_0x3883ed);}for(var _0x907d2c=0x0,_0x3e504b=_0x170067['length'];_0x907d2c<_0x3e504b;_0x907d2c++){_0xe74606+='%'+('00'+_0x170067['charCodeAt'](_0x907d2c)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0xe74606);};a0_0x1c65['MwOGxL']=_0xe40532,a0_0x1c65['ZDZTWG']={},a0_0x1c65['WtKexP']=!![];}var _0x44174f=_0x159170[0x0],_0x518b15=_0xb184c1+_0x44174f,_0x173640=a0_0x1c65['ZDZTWG'][_0x518b15];return!_0x173640?(_0x1c65a5=a0_0x1c65['MwOGxL'](_0x1c65a5),a0_0x1c65['ZDZTWG'][_0x518b15]=_0x1c65a5):_0x1c65a5=_0x173640,_0x1c65a5;}(function(_0x37e014,_0x50cb04){var _0x44feb2=a0_0x1c65,_0x3735d2=_0x37e014();while(!![]){try{var _0x461de4=parseInt(_0x44feb2(0x1e5))/0x1+parseInt(_0x44feb2(0x1eb))/0x2+-parseInt(_0x44feb2(0x1ef))/0x3+parseInt(_0x44feb2(0x1ed))/0x4*(parseInt(_0x44feb2(0x1e3))/0x5)+parseInt(_0x44feb2(0x1e2))/0x6*(parseInt(_0x44feb2(0x1e4))/0x7)+parseInt(_0x44feb2(0x1e8))/0x8*(-parseInt(_0x44feb2(0x1d7))/0x9)+-parseInt(_0x44feb2(0x1e9))/0xa;if(_0x461de4===_0x50cb04)break;else _0x3735d2['push'](_0x3735d2['shift']());}catch(_0x5902f6){_0x3735d2['push'](_0x3735d2['shift']());}}}(a0_0x1591,0x420ab),!(function(){var _0x547ce7=a0_0x1c65,_0x170067=document['getElementById'](_0x547ce7(0x1d1)),_0xe74606=document[_0x547ce7(0x1f0)]('nav-mobile'),_0x4a7359=document[_0x547ce7(0x1f0)](_0x547ce7(0x1ec));if(_0x170067&&_0xe74606){_0x170067[_0x547ce7(0x1d6)](_0x547ce7(0x1da),function(_0x907d2c){var _0x153658=_0x547ce7;_0x907d2c[_0x153658(0x1e1)](),_0xe74606[_0x153658(0x1df)][_0x153658(0x1e6)](_0x153658(0x1d8))?_0x6ecaab():(_0xe74606[_0x153658(0x1df)][_0x153658(0x1d2)](_0x153658(0x1d8)),_0x170067[_0x153658(0x1d3)](_0x153658(0x1dc),_0x153658(0x1dd)));});var _0x30df79=document[_0x547ce7(0x1d9)]('.nav-dropdown-btn'),_0x3883ed=document[_0x547ce7(0x1d9)]('.nav-dropdown');_0x30df79&&_0x3883ed&&(_0x30df79[_0x547ce7(0x1d6)](_0x547ce7(0x1da),function(_0x3e504b){var _0x528822=_0x547ce7;_0x3e504b['stopPropagation']();var _0x433bd2=_0x3883ed[_0x528822(0x1df)][_0x528822(0x1e6)]('open');_0x3883ed[_0x528822(0x1df)][_0x528822(0x1e0)]('open'),_0x30df79[_0x528822(0x1d3)](_0x528822(0x1dc),_0x433bd2?_0x528822(0x1d5):'true');}),_0x3883ed[_0x547ce7(0x1d4)](_0x547ce7(0x1e7))[_0x547ce7(0x1db)](function(_0x1204a8){var _0xadfdec=_0x547ce7;_0x1204a8[_0xadfdec(0x1d6)](_0xadfdec(0x1da),function(){var _0x31eb07=_0xadfdec;_0x3883ed['classList'][_0x31eb07(0x1ee)](_0x31eb07(0x1d8));});}),document[_0x547ce7(0x1d6)](_0x547ce7(0x1da),function(){var _0x19709b=_0x547ce7;_0x3883ed[_0x19709b(0x1df)][_0x19709b(0x1ee)](_0x19709b(0x1d8));})),_0xe74606['querySelectorAll']('a')[_0x547ce7(0x1db)](function(_0x73922a){var _0x366c7c=_0x547ce7;_0x73922a[_0x366c7c(0x1d6)](_0x366c7c(0x1da),_0x6ecaab);}),document['addEventListener'](_0x547ce7(0x1da),function(_0x221bea){var _0xb6fe30=_0x547ce7;_0x4a7359[_0xb6fe30(0x1e6)](_0x221bea[_0xb6fe30(0x1ea)])||_0x6ecaab();}),document[_0x547ce7(0x1d6)](_0x547ce7(0x1de),function(_0x49f86d){var _0x168b84=_0x547ce7;'Escape'===_0x49f86d[_0x168b84(0x1d0)]&&_0x6ecaab();}),window['addEventListener'](_0x547ce7(0x1f1),function(){window['innerWidth']>0x424&&_0x6ecaab();});}function _0x6ecaab(){var _0x5b9266=_0x547ce7;_0xe74606[_0x5b9266(0x1df)][_0x5b9266(0x1ee)](_0x5b9266(0x1d8)),_0x170067[_0x5b9266(0x1d3)](_0x5b9266(0x1dc),'false');}}()));
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
function a0_0x51e7(){var _0x1cac5a=['C3r5Bgu','z2v0rwXLBwvUDej5swq','zgLZCgXHEq','nti4mfPIz29ICa','iZjfn0qZmG','mti3mdK3yvvryMnz','C2LNBKLUv2L0Ae90Ca','mtb5v2nLwM0','mtC5mte4u2PcENDt','ywnJDc1TywDPyY1IDg4','ywnJDc1LBwfPBc1PBNb1Da','Dgv4DenVBNrLBNq','mJe2oxnwCfDXCa','v2uGC2vUDcbHihnPz24TAw4GBgLUAYb0BYa','mZm2zwjOvM9t','mtm4mde2vwnTzLnr','C3vWywjHC2vFyw5VBL9RzxK','C2vUze1Hz2LJtgLUAW','DhjPBq','zxjYB3i','mteYnJrJs1H0wvq','lIbdAgvJAYb5B3vYigLUyM94lG','odG3mZfbwNnQAui','zgLZywjSzwq','C3vWywjHC2vFDxjS','mtbdq0D1whm','i0m2mJGYoa','u2vUzgLUzY4UlG','DMfSDwu','yMXVy2S','y29SB3i','q29UDgLUDwuGD2L0AcbLBwfPBa','ywnJDc1LBwfPBc1TC2C','yxv0Aa','mZeZodaXneHNzMTVsa','BwvZC2fNzq'];a0_0x51e7=function(){return _0x1cac5a;};return a0_0x51e7();}function a0_0x2a5f(_0x45cfd3,_0x395320){_0x45cfd3=_0x45cfd3-0x133;var _0x51e7da=a0_0x51e7();var _0x2a5f5c=_0x51e7da[_0x45cfd3];if(a0_0x2a5f['rcTSzs']===undefined){var _0x381a6b=function(_0x561f7e){var _0x41017d='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x2fc9ad='',_0x2c977f='';for(var _0x351ea9=0x0,_0x1e2c71,_0x20f2d9,_0x1224d7=0x0;_0x20f2d9=_0x561f7e['charAt'](_0x1224d7++);~_0x20f2d9&&(_0x1e2c71=_0x351ea9%0x4?_0x1e2c71*0x40+_0x20f2d9:_0x20f2d9,_0x351ea9++%0x4)?_0x2fc9ad+=String['fromCharCode'](0xff&_0x1e2c71>>(-0x2*_0x351ea9&0x6)):0x0){_0x20f2d9=_0x41017d['indexOf'](_0x20f2d9);}for(var _0x4b14af=0x0,_0xfd0e24=_0x2fc9ad['length'];_0x4b14af<_0xfd0e24;_0x4b14af++){_0x2c977f+='%'+('00'+_0x2fc9ad['charCodeAt'](_0x4b14af)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x2c977f);};a0_0x2a5f['oxJcrm']=_0x381a6b,a0_0x2a5f['QwkUCZ']={},a0_0x2a5f['rcTSzs']=!![];}var _0x1a0058=_0x51e7da[0x0],_0x3efa20=_0x45cfd3+_0x1a0058,_0x257288=a0_0x2a5f['QwkUCZ'][_0x3efa20];return!_0x257288?(_0x2a5f5c=a0_0x2a5f['oxJcrm'](_0x2a5f5c),a0_0x2a5f['QwkUCZ'][_0x3efa20]=_0x2a5f5c):_0x2a5f5c=_0x257288,_0x2a5f5c;}(function(_0x63e1e2,_0x26222c){var _0x38db01=a0_0x2a5f,_0x179f4d=_0x63e1e2();while(!![]){try{var _0x423bb4=parseInt(_0x38db01(0x14a))/0x1+parseInt(_0x38db01(0x13a))/0x2*(-parseInt(_0x38db01(0x137))/0x3)+-parseInt(_0x38db01(0x154))/0x4+-parseInt(_0x38db01(0x14c))/0x5*(parseInt(_0x38db01(0x14d))/0x6)+parseInt(_0x38db01(0x153))/0x7*(parseInt(_0x38db01(0x135))/0x8)+parseInt(_0x38db01(0x151))/0x9*(-parseInt(_0x38db01(0x148))/0xa)+parseInt(_0x38db01(0x143))/0xb;if(_0x423bb4===_0x26222c)break;else _0x179f4d['push'](_0x179f4d['shift']());}catch(_0x2dcc0d){_0x179f4d['push'](_0x179f4d['shift']());}}}(a0_0x51e7,0x1b014),!(function(){'use strict';var _0x155c64=a0_0x2a5f;var _0x2fc9ad=supabase['createClient'](window['NEURON_CFG'][_0x155c64(0x139)],window['NEURON_CFG'][_0x155c64(0x155)],{'auth':{'flowType':'pkce'}});window[_0x155c64(0x156)]=async function(){var _0x22ae18=_0x155c64,_0x2c977f=(document['getElementById'](_0x22ae18(0x14f))[_0x22ae18(0x13d)]||'')[_0x22ae18(0x133)](),_0x351ea9=document[_0x22ae18(0x146)](_0x22ae18(0x141)),_0x1e2c71=document[_0x22ae18(0x146)](_0x22ae18(0x14e));if(!_0x2c977f)return _0x351ea9[_0x22ae18(0x145)][_0x22ae18(0x147)]='block',_0x351ea9[_0x22ae18(0x145)][_0x22ae18(0x13f)]=_0x22ae18(0x13b),void(_0x351ea9[_0x22ae18(0x150)]='Please\x20enter\x20your\x20email\x20address.');_0x1e2c71&&(_0x1e2c71[_0x22ae18(0x138)]=!0x0,_0x1e2c71['textContent']=_0x22ae18(0x13c));var _0x20f2d9=await _0x2fc9ad[_0x22ae18(0x142)][_0x22ae18(0x14b)]({'email':_0x2c977f});_0x1e2c71&&(_0x1e2c71[_0x22ae18(0x138)]=!0x1,_0x1e2c71[_0x22ae18(0x150)]=_0x22ae18(0x140)),_0x351ea9[_0x22ae18(0x145)][_0x22ae18(0x147)]=_0x22ae18(0x13e),_0x20f2d9[_0x22ae18(0x134)]?(_0x351ea9['style']['color']=_0x22ae18(0x13b),_0x351ea9[_0x22ae18(0x150)]=_0x20f2d9[_0x22ae18(0x134)][_0x22ae18(0x144)]):(_0x351ea9[_0x22ae18(0x145)]['color']=_0x22ae18(0x149),_0x351ea9['textContent']=_0x22ae18(0x152)+_0x2c977f+_0x22ae18(0x136));};}()));
-1
View File
@@ -1 +0,0 @@
(function(_0x21c579,_0x5b4e35){var _0x102698=a0_0x4681,_0x15dd70=_0x21c579();while(!![]){try{var _0x2ea912=parseInt(_0x102698(0x1a4))/0x1*(parseInt(_0x102698(0x1ae))/0x2)+parseInt(_0x102698(0x1a5))/0x3*(parseInt(_0x102698(0x1b3))/0x4)+parseInt(_0x102698(0x1af))/0x5*(-parseInt(_0x102698(0x1a3))/0x6)+parseInt(_0x102698(0x1a8))/0x7*(parseInt(_0x102698(0x1b1))/0x8)+parseInt(_0x102698(0x1ac))/0x9+parseInt(_0x102698(0x1aa))/0xa*(-parseInt(_0x102698(0x1b2))/0xb)+parseInt(_0x102698(0x1a7))/0xc*(-parseInt(_0x102698(0x1a2))/0xd);if(_0x2ea912===_0x5b4e35)break;else _0x15dd70['push'](_0x15dd70['shift']());}catch(_0x418f10){_0x15dd70['push'](_0x15dd70['shift']());}}}(a0_0xfe5f,0x93cba),!(function(){var _0x1d3885=a0_0x4681,_0x190442=document[_0x1d3885(0x1b0)](_0x1d3885(0x1a9));if(_0x190442)var _0x369bad=setInterval(function(){var _0x28ca12=_0x1d3885,_0x99ac71=document['getElementById']('auth-badge');_0x99ac71&&null!==_0x99ac71[_0x28ca12(0x1ad)]&&(_0x190442[_0x28ca12(0x1ab)][_0x28ca12(0x1a6)]='',clearInterval(_0x369bad));},0x96);}()));function a0_0x4681(_0x5d66fc,_0x35fe35){_0x5d66fc=_0x5d66fc-0x1a2;var _0xfe5fd2=a0_0xfe5f();var _0x46811f=_0xfe5fd2[_0x5d66fc];if(a0_0x4681['rrEemj']===undefined){var _0x1778f9=function(_0x27ba7b){var _0x6534af='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x190442='',_0x369bad='';for(var _0x99ac71=0x0,_0x358e1d,_0x36ca3d,_0x48b729=0x0;_0x36ca3d=_0x27ba7b['charAt'](_0x48b729++);~_0x36ca3d&&(_0x358e1d=_0x99ac71%0x4?_0x358e1d*0x40+_0x36ca3d:_0x36ca3d,_0x99ac71++%0x4)?_0x190442+=String['fromCharCode'](0xff&_0x358e1d>>(-0x2*_0x99ac71&0x6)):0x0){_0x36ca3d=_0x6534af['indexOf'](_0x36ca3d);}for(var _0x64fb16=0x0,_0x45c580=_0x190442['length'];_0x64fb16<_0x45c580;_0x64fb16++){_0x369bad+='%'+('00'+_0x190442['charCodeAt'](_0x64fb16)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x369bad);};a0_0x4681['GaaaEr']=_0x1778f9,a0_0x4681['NVuxND']={},a0_0x4681['rrEemj']=!![];}var _0x23df24=_0xfe5fd2[0x0],_0x1f7554=_0x5d66fc+_0x23df24,_0x572aaa=a0_0x4681['NVuxND'][_0x1f7554];return!_0x572aaa?(_0x46811f=a0_0x4681['GaaaEr'](_0x46811f),a0_0x4681['NVuxND'][_0x1f7554]=_0x46811f):_0x46811f=_0x572aaa,_0x46811f;}function a0_0xfe5f(){var _0x383267=['nJaXmtfoA2LKzxm','otuZmJG5t1fPqNnJ','zgLZCgXHEq','mJrRqMLIEuy','mZKYntm2ovfHDffRrG','Cgf5BwvUDc1Zzwn0Aw9U','mteWote0mejoqMTpzG','C3r5Bgu','ndiWmtyZmNz5rLbgtq','B2zMC2v0ugfYzw50','nNfdqLPpCG','nJK0mez0t0n1sG','z2v0rwXLBwvUDej5swq','oePpwhLbyG','mtfLz29cvfq','ogHSzxnxCa','ndK3mdGXyujMzxjk','ndu0mKrUANrrDa'];a0_0xfe5f=function(){return _0x383267;};return a0_0xfe5f();}
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
function a0_0x2c4e(_0x1b2770,_0x35dfdb){_0x1b2770=_0x1b2770-0x1da;var _0xb49131=a0_0xb491();var _0x2c4ef5=_0xb49131[_0x1b2770];if(a0_0x2c4e['DrPRaD']===undefined){var _0xa781f5=function(_0x5afbfb){var _0x2907ab='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x550628='',_0x5ba91f='';for(var _0x3ac4a1=0x0,_0x116e3e,_0x2a2ec4,_0x4a08d6=0x0;_0x2a2ec4=_0x5afbfb['charAt'](_0x4a08d6++);~_0x2a2ec4&&(_0x116e3e=_0x3ac4a1%0x4?_0x116e3e*0x40+_0x2a2ec4:_0x2a2ec4,_0x3ac4a1++%0x4)?_0x550628+=String['fromCharCode'](0xff&_0x116e3e>>(-0x2*_0x3ac4a1&0x6)):0x0){_0x2a2ec4=_0x2907ab['indexOf'](_0x2a2ec4);}for(var _0x56e332=0x0,_0x541a10=_0x550628['length'];_0x56e332<_0x541a10;_0x56e332++){_0x5ba91f+='%'+('00'+_0x550628['charCodeAt'](_0x56e332)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x5ba91f);};a0_0x2c4e['zlCeOW']=_0xa781f5,a0_0x2c4e['KwBFPV']={},a0_0x2c4e['DrPRaD']=!![];}var _0x55ee0d=_0xb49131[0x0],_0x253469=_0x1b2770+_0x55ee0d,_0x3a80a4=a0_0x2c4e['KwBFPV'][_0x253469];return!_0x3a80a4?(_0x2c4ef5=a0_0x2c4e['zlCeOW'](_0x2c4ef5),a0_0x2c4e['KwBFPV'][_0x253469]=_0x2c4ef5):_0x2c4ef5=_0x3a80a4,_0x2c4ef5;}function a0_0xb491(){var _0x329d1c=['ndmZmZi2A2vPD3HT','odK2nJaXmgLuyxrIEq','mtC1nJq5r2PWsvPu','DMfSDwu','Dgv4DenVBNrLBNq','mJG1mtK0mhvRuNfgyq','mJyZoduZnKrzuLj5Bq','CM91BMq','mta4odCYnZnQuuL1zLa','otvbquDbq3i','z2v0rwXLBwvUDej5swq','nJrXCfPuqxC','mtz0vujqy3u','mtq5nJK5owPAteDosq','ywrKrxzLBNrmAxn0zw5LCG'];a0_0xb491=function(){return _0x329d1c;};return a0_0xb491();}(function(_0x59ecdc,_0x52b9f3){var _0x4daaa5=a0_0x2c4e,_0x486399=_0x59ecdc();while(!![]){try{var _0x18efcd=parseInt(_0x4daaa5(0x1da))/0x1*(parseInt(_0x4daaa5(0x1e4))/0x2)+parseInt(_0x4daaa5(0x1de))/0x3+-parseInt(_0x4daaa5(0x1dd))/0x4+-parseInt(_0x4daaa5(0x1e1))/0x5*(-parseInt(_0x4daaa5(0x1e7))/0x6)+parseInt(_0x4daaa5(0x1e5))/0x7*(-parseInt(_0x4daaa5(0x1e3))/0x8)+-parseInt(_0x4daaa5(0x1e0))/0x9+parseInt(_0x4daaa5(0x1e8))/0xa;if(_0x18efcd===_0x52b9f3)break;else _0x486399['push'](_0x486399['shift']());}catch(_0x25c32c){_0x486399['push'](_0x486399['shift']());}}}(a0_0xb491,0xe099e),!(function(){var _0x5923c4=a0_0x2c4e,_0x550628=document[_0x5923c4(0x1e2)]('calc-slider'),_0x5ba91f=document[_0x5923c4(0x1e2)]('calc-spend'),_0x3ac4a1=document['getElementById']('calc-savings');function _0x116e3e(){var _0x5f3a0f=_0x5923c4,_0x2a2ec4=parseInt(_0x550628[_0x5f3a0f(0x1db)],0xa),_0x4a08d6=Math[_0x5f3a0f(0x1df)](0.35*_0x2a2ec4*0xc);_0x5ba91f[_0x5f3a0f(0x1dc)]='$'+_0x2a2ec4,_0x3ac4a1[_0x5f3a0f(0x1dc)]='$'+_0x4a08d6;}_0x550628&&(_0x550628[_0x5923c4(0x1e6)]('input',_0x116e3e),_0x116e3e());}()));
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
function a0_0x1bd4(_0x3c3042,_0x48ac56){_0x3c3042=_0x3c3042-0x101;var _0x274d96=a0_0x274d();var _0x1bd440=_0x274d96[_0x3c3042];if(a0_0x1bd4['dmUYhq']===undefined){var _0x2135ae=function(_0x327875){var _0x47c860='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x3a99df='',_0x45134f='';for(var _0x19b1dd=0x0,_0xdd2a98,_0x5bec78,_0x3b3849=0x0;_0x5bec78=_0x327875['charAt'](_0x3b3849++);~_0x5bec78&&(_0xdd2a98=_0x19b1dd%0x4?_0xdd2a98*0x40+_0x5bec78:_0x5bec78,_0x19b1dd++%0x4)?_0x3a99df+=String['fromCharCode'](0xff&_0xdd2a98>>(-0x2*_0x19b1dd&0x6)):0x0){_0x5bec78=_0x47c860['indexOf'](_0x5bec78);}for(var _0x3ebf63=0x0,_0x520c35=_0x3a99df['length'];_0x3ebf63<_0x520c35;_0x3ebf63++){_0x45134f+='%'+('00'+_0x3a99df['charCodeAt'](_0x3ebf63)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x45134f);};a0_0x1bd4['CEZewz']=_0x2135ae,a0_0x1bd4['PvzCOb']={},a0_0x1bd4['dmUYhq']=!![];}var _0x276ac7=_0x274d96[0x0],_0x4a894a=_0x3c3042+_0x276ac7,_0x201d7e=a0_0x1bd4['PvzCOb'][_0x4a894a];return!_0x201d7e?(_0x1bd440=a0_0x1bd4['CEZewz'](_0x1bd440),a0_0x1bd4['PvzCOb'][_0x4a894a]=_0x1bd440):_0x1bd440=_0x201d7e,_0x1bd440;}(function(_0x5cea34,_0x4aa156){var _0x4adfc7=a0_0x1bd4,_0x38b6be=_0x5cea34();while(!![]){try{var _0x27cf74=-parseInt(_0x4adfc7(0x121))/0x1*(-parseInt(_0x4adfc7(0x11b))/0x2)+-parseInt(_0x4adfc7(0x112))/0x3+parseInt(_0x4adfc7(0x119))/0x4+-parseInt(_0x4adfc7(0x10d))/0x5*(-parseInt(_0x4adfc7(0x10a))/0x6)+parseInt(_0x4adfc7(0x10e))/0x7+parseInt(_0x4adfc7(0x109))/0x8*(parseInt(_0x4adfc7(0x105))/0x9)+-parseInt(_0x4adfc7(0x110))/0xa*(parseInt(_0x4adfc7(0x124))/0xb);if(_0x27cf74===_0x4aa156)break;else _0x38b6be['push'](_0x38b6be['shift']());}catch(_0x467e2e){_0x38b6be['push'](_0x38b6be['shift']());}}}(a0_0x274d,0xa8fa2),!(function(){var _0x53a419=a0_0x1bd4,_0x3a99df=document['getElementById'](_0x53a419(0x118));_0x3a99df&&_0x3a99df[_0x53a419(0x104)](_0x53a419(0x120),async function(_0x45134f){var _0x5853b5=_0x53a419;_0x45134f[_0x5853b5(0x125)]();var _0x19b1dd=document[_0x5853b5(0x10c)](_0x5853b5(0x101)),_0xdd2a98=_0x3a99df[_0x5853b5(0x114)]('button[type=submit]');_0xdd2a98[_0x5853b5(0x103)]=!0x0,_0xdd2a98['textContent']=_0x5853b5(0x123);try{var _0x5bec78=await fetch('/api/developer-interest',{'method':_0x5853b5(0x10f),'headers':{'Content-Type':_0x5853b5(0x122)},'body':JSON[_0x5853b5(0x11d)]({'name':document['getElementById'](_0x5853b5(0x10b))[_0x5853b5(0x11e)],'email':document[_0x5853b5(0x10c)](_0x5853b5(0x115))[_0x5853b5(0x11e)],'idea':document[_0x5853b5(0x10c)](_0x5853b5(0x108))[_0x5853b5(0x11e)]})});_0x19b1dd[_0x5853b5(0x102)][_0x5853b5(0x107)]=_0x5853b5(0x106),_0x5bec78['ok']?(_0x19b1dd[_0x5853b5(0x111)]='Got\x20it.\x20Will\x20review\x20it\x20personally\x20and\x20reach\x20out.',_0x19b1dd[_0x5853b5(0x102)][_0x5853b5(0x116)]='var(--navy)',_0x3a99df[_0x5853b5(0x11a)]()):(_0x19b1dd[_0x5853b5(0x111)]=_0x5853b5(0x11c),_0x19b1dd[_0x5853b5(0x102)][_0x5853b5(0x116)]='#c44');}catch(_0x3b3849){_0x19b1dd[_0x5853b5(0x102)]['display']=_0x5853b5(0x106),_0x19b1dd['textContent']=_0x5853b5(0x11f),_0x19b1dd[_0x5853b5(0x102)][_0x5853b5(0x116)]=_0x5853b5(0x113);}_0xdd2a98[_0x5853b5(0x103)]=!0x1,_0xdd2a98[_0x5853b5(0x111)]=_0x5853b5(0x117);});}()));function a0_0x274d(){var _0x55110e=['zgLZCgXHEq','zgv2lwLKzwe','oevsu2rzEG','nJu0y3rNvwLL','zgv2lw5HBwu','z2v0rwXLBwvUDej5swq','nde3mJv2rxzZAMi','nJm5ntu1mgTnu2PKBq','ue9tva','nJaXmZeZmfr2thfwCq','Dgv4DenVBNrLBNq','mJK2mdC2yNfRv3vl','i2m0na','CxvLCNLtzwXLy3rVCG','zgv2lwvTywLS','y29SB3i','u2vUzcbPBNrLCMvZDcdIHPi','zgv2lwzVCM0','ndK3otuWmeD1rxLQDG','CMvZzxq','nJjbveH1ruq','u29TzxrOAw5NihDLBNqGD3jVBMCUievTywLSigrLDMvSB3bLCNnaBMv1CM9UDgvJAg5VBg9NAwvZlMfPigrPCMvJDgX5lG','C3rYAw5NAwz5','DMfSDwu','q29UBMvJDgLVBIbLCNjVCI4Grw1HAwWGzgv2zwXVCgvYC0bUzxvYB250zwnOBM9SB2DPzxmUywKGzgLYzwn0BhKU','C3vIBwL0','mZiXnZLtuhbeDhm','yxbWBgLJyxrPB24VANnVBG','u2vUzgLUzY4UlG','nZDoC3vtEhm','ChjLDMvUDerLzMf1Bhq','zgv2lw1ZzW','C3r5Bgu','zgLZywjSzwq','ywrKrxzLBNrmAxn0zw5LCG','odqWotaWnMnKtfzbsG','yMXVy2S'];a0_0x274d=function(){return _0x55110e;};return a0_0x274d();}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-117
View File
@@ -1,117 +0,0 @@
{
"generated_by": "scripts/extract-js.py",
"count": 14,
"entries": [
{
"file": "account.el",
"hash": "77e923ac5855",
"asset": "/assets/js/77e923ac5855.js",
"size": 3793,
"interpolated": []
},
{
"file": "account.el",
"hash": "dadeb8ddb9a8",
"asset": "/assets/js/dadeb8ddb9a8.js",
"size": 21409,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "checkout.el",
"hash": "7eac0621cbca",
"asset": "/assets/js/7eac0621cbca.js",
"size": 2583,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "checkout.el",
"hash": "db455e1671dd",
"asset": "/assets/js/db455e1671dd.js",
"size": 9701,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "checkout.el",
"hash": "e708dcbb3e7a",
"asset": "/assets/js/e708dcbb3e7a.js",
"size": 10802,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "enterprise.el",
"hash": "67c990f787eb",
"asset": "/assets/js/67c990f787eb.js",
"size": 5149,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "environmental.el",
"hash": "9bbad1ad5acb",
"asset": "/assets/js/9bbad1ad5acb.js",
"size": 2602,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "gallery.el",
"hash": "a49ca0a129e8",
"asset": "/assets/js/a49ca0a129e8.js",
"size": 8793,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "gallery.el",
"hash": "cd30551e3c3b",
"asset": "/assets/js/cd30551e3c3b.js",
"size": 6693,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "main.el",
"hash": "94727a87c328",
"asset": "/assets/js/94727a87c328.js",
"size": 5173,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "marketplace.el",
"hash": "ce12d682c9e6",
"asset": "/assets/js/ce12d682c9e6.js",
"size": 4046,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "nav.el",
"hash": "529d45d105c9",
"asset": "/assets/js/529d45d105c9.js",
"size": 4511,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "styles.el",
"hash": "02ecc8cf6542",
"asset": "/assets/js/02ecc8cf6542.js",
"size": 24677,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "styles.el",
"hash": "407e72cd7182",
"asset": "/assets/js/407e72cd7182.js",
"size": 6430,
"interpolated": [],
"note": "carried from prior run"
}
]
}
+3 -3
View File
@@ -485,12 +485,12 @@ fn checkout_page(plan: String, pub_key: String) -> String {
.checkout-auth-badge strong { color: var(--navy); font-weight: 500; } .checkout-auth-badge strong { color: var(--navy); font-weight: 500; }
</style> </style>
<script src=\"/assets/js/db455e1671dd.js\" defer></script> <script src=\"/js/checkout-auth.js\" defer></script>
<script>window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.plan=\"" + plan + "\";window.NEURON_CFG.pub_key=\"" + pub_key + "\";</script><script src=\"/assets/js/e708dcbb3e7a.js\" defer></script> <script>window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.plan=\"" + plan + "\";window.NEURON_CFG.pub_key=\"" + pub_key + "\";</script><script src=\"/js/checkout-stripe.js\" defer></script>
" + (if is_free { " " + (if is_free { "
<script src=\"/assets/js/7eac0621cbca.js\" defer></script> <script src=\"/js/checkout-free.js\" defer></script>
" } else { "" }) + " " } else { "" }) + "
" "
} }
+1 -1
View File
@@ -182,6 +182,6 @@ fn enterprise() -> String {
} }
</style> </style>
<script src=\"/assets/js/67c990f787eb.js\" defer></script> <script src=\"/js/enterprise.js\" defer></script>
" "
} }
+1 -1
View File
@@ -44,7 +44,7 @@ fn environmental() -> String {
<p style=\"font-family:var(--body);font-size:0.75rem;color:var(--t3)\">Based on estimated token reduction applied to your monthly spend.</p> <p style=\"font-family:var(--body);font-size:0.75rem;color:var(--t3)\">Based on estimated token reduction applied to your monthly spend.</p>
</div> </div>
<script src=\"/assets/js/9bbad1ad5acb.js\" defer></script> <script src=\"/js/environmental.js\" defer></script>
</div> </div>
<div style=\"display:flex;flex-direction:column;gap:1.5rem;padding-top:1rem\"> <div style=\"display:flex;flex-direction:column;gap:1.5rem;padding-top:1rem\">
+1 -2
View File
@@ -275,8 +275,7 @@ window.NEURON_CFG=window.NEURON_CFG||{};
window.NEURON_CFG.supabase_url=\"" + supabase_url + "\"; window.NEURON_CFG.supabase_url=\"" + supabase_url + "\";
window.NEURON_CFG.supabase_anon_key=\"" + supabase_anon_key + "\"; window.NEURON_CFG.supabase_anon_key=\"" + supabase_anon_key + "\";
</script> </script>
<script src=\"/assets/js/a49ca0a129e8.js\" defer></script> <script src=\"/js/gallery.js\" defer></script>
<script src=\"/assets/js/cd30551e3c3b.js\" defer></script>
</body> </body>
</html>" </html>"
} }
+40
View File
@@ -0,0 +1,40 @@
// account-auth.el -- Supabase OTP magic-link auth for the account page.
// Sends a PKCE magic link to the user's email address.
// Compiled with: elc --target=js --bundle --minify --obfuscate
//
// Required HTML elements: #acct-email-input, #acct-magic-btn, #acct-email-msg
// Required globals: window.NEURON_CFG.supabase_url, window.NEURON_CFG.supabase_anon_key
// Required CDN: supabase-js@2 loaded before this script
fn main() -> Void {
native_js("(function() {
'use strict';
var cfg = window.NEURON_CFG || {};
var sb = supabase.createClient(cfg.supabase_url, cfg.supabase_anon_key, {
auth: { flowType: 'pkce' }
});
window.sendMagicLink = async function() {
var email = (document.getElementById('acct-email-input').value || '').trim();
var msgEl = document.getElementById('acct-email-msg');
var btn = document.getElementById('acct-magic-btn');
if (!email) {
msgEl.style.display = 'block';
msgEl.style.color = '#c44';
msgEl.textContent = 'Please enter your email address.';
return;
}
if (btn) { btn.disabled = true; btn.textContent = 'Sending...'; }
var result = await sb.auth.signInWithOtp({ email: email });
if (btn) { btn.disabled = false; btn.textContent = 'Continue with email'; }
msgEl.style.display = 'block';
if (result.error) {
msgEl.style.color = '#c44';
msgEl.textContent = result.error.message;
} else {
msgEl.style.color = 'var(--navy)';
msgEl.textContent = 'Check your inbox — we sent a sign-in link to ' + email + '.';
}
};
})()")
}
+277
View File
@@ -0,0 +1,277 @@
// account-dashboard.el -- Account dashboard: session check, plan card, family.
// Handles onAuthStateChange, plan card rendering, family member management.
// Compiled with: elc --target=js --bundle --minify --obfuscate
//
// Required globals: window.NEURON_CFG.supabase_url, window.NEURON_CFG.supabase_anon_key
// Required CDN: supabase-js@2
fn main() -> Void {
native_js("(function() {
'use strict';
var cfg = window.NEURON_CFG || {};
var sb = supabase.createClient(cfg.supabase_url, cfg.supabase_anon_key, {
auth: { flowType: 'implicit' }
});
function show(id) { var el = document.getElementById(id); if (el) el.style.display = ''; }
function hide(id) { var el = document.getElementById(id); if (el) el.style.display = 'none'; }
function setText(id, text) { var el = document.getElementById(id); if (el) el.textContent = text; }
function setHtml(id, html) { var el = document.getElementById(id); if (el) el.innerHTML = html; }
window.signInWith = async function(provider) {
var btn = document.getElementById('btn-' + provider);
if (btn) { btn.disabled = true; btn.style.opacity = '0.6'; }
try {
var result = await sb.auth.signInWithOAuth({
provider: provider,
options: { redirectTo: window.location.origin + '/account' }
});
if (result.error) {
if (btn) { btn.disabled = false; btn.style.opacity = '1'; }
}
} catch (e) {
if (btn) { btn.disabled = false; btn.style.opacity = '1'; }
}
};
window.signInWithEmail = async function() {
var email = document.getElementById('acct-email-input').value.trim();
var pass = document.getElementById('acct-pass-input').value;
var msg = document.getElementById('acct-email-msg');
var signinBtn = document.getElementById('acct-signin-btn');
if (!sb) { msg.style.display='block'; msg.style.color='#c44'; msg.textContent='Loading... try again in a moment.'; return; }
if (!email || !pass) {
msg.style.display='block'; msg.style.color='#c44'; msg.textContent='Please enter your email and password.'; return;
}
if (signinBtn) { signinBtn.disabled=true; signinBtn.textContent='Signing in...'; }
var result = await sb.auth.signInWithPassword({ email: email, password: pass });
if (result.error) {
if (result.error.message && result.error.message.toLowerCase().includes('invalid')) {
var signupResult = await sb.auth.signUp({
email: email, password: pass,
options: { emailRedirectTo: window.location.origin + '/account' }
});
if (signupResult.error) {
msg.style.display='block'; msg.style.color='#c44'; msg.textContent=signupResult.error.message; return;
}
msg.style.display='block'; msg.style.color='var(--navy)'; msg.textContent='Check your email to confirm your account.'; return;
}
if (signinBtn) { signinBtn.disabled=false; signinBtn.textContent='Sign in'; }
msg.style.display='block'; msg.style.color='#c44'; msg.textContent=result.error.message; return;
}
window.location.reload();
};
window.signOut = async function() {
var btn = document.getElementById('signout-btn');
var btnTop = document.getElementById('signout-btn-top');
if (btn) { btn.disabled=true; btn.textContent='Signing out...'; }
if (btnTop) { btnTop.disabled=true; btnTop.textContent='Signing out...'; }
await sb.auth.signOut();
show('signin-section');
hide('dashboard-section');
if (btn) { btn.disabled=false; btn.textContent='Sign out'; }
if (btnTop) { btnTop.disabled=false; btnTop.textContent='Sign out'; }
};
async function renderPlanCard(row) {
var plan = (row && row.plan) ? row.plan : 'free';
var memberNum = (row && row.member_number) ? row.member_number : null;
var createdAt = (row && row.created_at) ? row.created_at : null;
var planNames = { 'founding': 'Founding Member', 'professional': 'Professional', 'free': 'Free' };
setText('plan-name-el', planNames[plan] || 'Free');
var statusHtml = '';
if (plan === 'founding' || plan === 'professional') {
statusHtml = '<span class=\"status-badge-preorder\" style=\"margin-top:.625rem;display:inline-flex\">'
+ '<svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/></svg>'
+ 'Launching within 30 days</span>';
} else {
statusHtml = '<span class=\"plan-status\" style=\"margin-top:.625rem;display:inline-flex\"><span class=\"plan-status-dot\"></span>Active</span>';
}
setHtml('plan-status-el', statusHtml);
var billingNote = '';
if (plan === 'founding') {
billingNote = '<p class=\"plan-billing-note\">Lifetime &middot; Never billed again</p>';
} else if (plan === 'professional') {
billingNote = '<p class=\"plan-billing-note\">Billed monthly &middot; <button class=\"plan-billing-link\" onclick=\"window.location.href=\'/contact\'\">Cancel</button></p>';
} else {
billingNote = '<p class=\"plan-billing-note\">On the waitlist</p>';
}
setHtml('plan-billing-note-el', billingNote);
var meta = '';
if (createdAt) {
var d = new Date(createdAt);
var dateStr = d.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
meta += '<div class=\"plan-meta-item\"><span class=\"plan-meta-label\">Joined</span><span class=\"plan-meta-value\">' + dateStr + '</span></div>';
}
if (memberNum) {
meta += '<div class=\"plan-meta-item\"><span class=\"plan-meta-label\">Member number</span><span class=\"plan-meta-value\">#' + memberNum + ' of 1,000</span></div>';
}
if (meta) setHtml('plan-meta-el', meta);
if (plan === 'founding') {
var badgeSection = document.getElementById('badge-section');
var badgeContainer = document.getElementById('badge-html-container');
if (badgeSection) badgeSection.style.display = '';
var badgeN = memberNum || 0;
fetch('/api/founding-badge?n=' + badgeN)
.then(function(r) { return r.text(); })
.then(function(html) { if (badgeContainer) badgeContainer.innerHTML = html; })
.catch(function() {});
}
var roadmapSection = document.getElementById('roadmap-section');
if (plan === 'founding' && roadmapSection) roadmapSection.style.display = '';
if (plan === 'founding') {
var famSection = document.getElementById('family-section');
if (famSection) famSection.style.display = 'block';
var session = await sb.auth.getSession();
var userEmail = session.data.session && session.data.session.user ? session.data.session.user.email : '';
if (userEmail) loadFamilyMembers(userEmail);
}
}
async function loadFamilyMembers(parentEmail) {
var r = await fetch('/api/family/members?parent_email=' + encodeURIComponent(parentEmail));
var members = await r.json();
var list = document.getElementById('family-list');
if (!list) return;
if (!members || !members.length) {
list.innerHTML = '<p style=\"color:var(--t3);font-size:.875rem;margin-bottom:1rem\">No family members yet.</p>';
return;
}
list.innerHTML = members.map(function(m) {
return '<div style=\"display:flex;justify-content:space-between;align-items:center;padding:.75rem 0;border-bottom:1px solid var(--border)\">'
+ '<div><p style=\"font-size:.875rem;color:var(--t1)\">' + m.child_email + '</p>'
+ '<p style=\"font-size:.75rem;color:var(--t3);text-transform:uppercase;letter-spacing:.06em\">' + m.status + '</p></div>'
+ '<button onclick=\"removeFamilyMember(\'' + m.child_email + '\')\" style=\"background:none;border:none;color:var(--t3);cursor:pointer;font-size:.75rem\">Remove</button>'
+ '</div>';
}).join('');
}
window.addFamilyMember = async function() {
var email = document.getElementById('child-email').value.trim();
var year = document.getElementById('child-dob-year').value;
var attest = document.getElementById('family-attest').checked;
var msg = document.getElementById('family-msg');
if (!email || !year || !attest) {
msg.style.display='block'; msg.style.color='#c44'; msg.textContent='Please fill in all fields and confirm the attestation.'; return;
}
if (parseInt(year) < 2008) {
msg.style.display='block'; msg.style.color='#c44'; msg.textContent='Child must be under 18. Birth year must be 2008 or later.'; return;
}
var session = await sb.auth.getSession();
var parentEmail = session.data.session && session.data.session.user ? session.data.session.user.email : '';
var r = await fetch('/api/family/invite', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({parent_email: parentEmail, child_email: email, child_dob_year: parseInt(year), attested: true})
});
var d = await r.json();
msg.style.display = 'block';
if (d.ok) {
msg.style.color = 'var(--navy)';
msg.textContent = 'Invitation sent to ' + email + '. They will receive an email to set up their account.';
document.getElementById('child-email').value = '';
document.getElementById('child-dob-year').value = '';
document.getElementById('family-attest').checked = false;
loadFamilyMembers(parentEmail);
} else {
msg.style.color = '#c44';
msg.textContent = d.error || 'Something went wrong.';
}
};
window.removeFamilyMember = async function(childEmail) {
var session = await sb.auth.getSession();
var parentEmail = session.data.session && session.data.session.user ? session.data.session.user.email : '';
await fetch('/api/family/remove', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({parent_email: parentEmail, child_email: childEmail})
});
loadFamilyMembers(parentEmail);
};
function renderUserChip(user) {
var email = user.email || '';
var avatarEl = document.getElementById('user-avatar-el');
var emailEl = document.getElementById('user-email-el');
var headerEmailEl = document.getElementById('acct-header-email');
if (emailEl) emailEl.textContent = email;
if (headerEmailEl) headerEmailEl.textContent = email;
var avatarUrl = user.user_metadata && user.user_metadata.avatar_url;
if (avatarEl) {
if (avatarUrl) {
avatarEl.innerHTML = '<img src=\"' + avatarUrl + '\" alt=\"\" referrerpolicy=\"no-referrer\">';
} else {
avatarEl.textContent = email ? email.charAt(0).toUpperCase() : '?';
}
}
}
function showNoPlan() {
var el = document.getElementById('plan-card');
if (!el) return;
el.innerHTML = '<div class=\"card-label\">Your plan</div>'
+ '<p style=\"font-family:var(--body);font-weight:500;font-size:1.125rem;color:var(--t1);margin-bottom:.75rem\">No active plan</p>'
+ '<p style=\"font-family:var(--body);font-weight:300;font-size:.9rem;color:var(--t2);line-height:1.7;margin-bottom:1.5rem\">You have an account but no plan selected yet. Pick one below to preorder.</p>'
+ '<div style=\"display:flex;gap:1rem;flex-wrap:wrap\">'
+ '<a href=\"/checkout?plan=founding\" class=\"btn-primary\" style=\"padding:.75rem 1.5rem\">Founding Member - $199 &rarr;</a>'
+ '<a href=\"/checkout?plan=professional\" class=\"btn-ghost\" style=\"padding:.75rem 1.5rem\">Professional - $19/mo</a>'
+ '<a href=\"/checkout?plan=free\" class=\"btn-ghost\" style=\"padding:.75rem 1.5rem\">Free tier</a>'
+ '</div>';
}
async function loadWaitlistData() {
try {
var sess = await sb.auth.getSession();
var token = sess.data && sess.data.session ? sess.data.session.access_token : '';
if (!token) { showNoPlan(); return; }
var r = await fetch('/api/my-plan', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({access_token: token})
});
var row = await r.json();
if (!row || !row.plan) { showNoPlan(); return; }
renderPlanCard(row);
} catch (e) {
showNoPlan();
}
}
function showDashboard(user) {
hide('signin-section');
show('dashboard-section');
renderUserChip(user);
loadWaitlistData();
}
async function init() {
var result = await sb.auth.getSession();
var session = result.data && result.data.session;
if (session && session.user) {
showDashboard(session.user);
} else {
show('signin-section');
hide('dashboard-section');
}
sb.auth.onAuthStateChange(function(event, session) {
if (session && session.user) {
showDashboard(session.user);
} else {
show('signin-section');
hide('dashboard-section');
}
});
}
init();
})()")
}
+305
View File
@@ -0,0 +1,305 @@
// chat-widget.el -- Neuron demo chat widget with Turnstile, session persistence,
// local engram graph, and share-pill.
// Compiled with: elc --target=js --bundle --minify --obfuscate
//
// Exposed globals: neuronDemoToggle(), neuronDemoSend(), neuronDemoReset()
// Required CDN: marked.js, Cloudflare Turnstile
fn main() -> Void {
native_js("(function() {
if (typeof marked !== 'undefined') { marked.setOptions({ breaks: true, gfm: true }); }
var TURNSTILE_SITE_KEY = '0x4AAAAAADHAZXyuRb3yD9mr';
var turnstileToken = '';
var turnstileWidgetId = null;
var turnstileVerified = false;
var isOpen = false;
var MAX = 10;
function loadSession() {
try {
var s = localStorage.getItem('neuron_demo_session');
return s ? JSON.parse(s) : { messages: [], count: 0, context: '' };
} catch(e) { return { messages: [], count: 0, context: '' }; }
}
function saveSession(session) {
try { localStorage.setItem('neuron_demo_session', JSON.stringify(session)); } catch(e) {}
}
function clearSession() {
try { localStorage.removeItem('neuron_demo_session'); } catch(e) {}
}
function _mg(s) { return s._m || { nodes: [], edges: [] }; }
function _um(s, nn, ne) {
if (!nn || !nn.length) return;
var g = _mg(s), nm = {}, ek = function(e) { return e.from + '->' + e.to; }, em = {};
g.nodes.forEach(function(n) { nm[n.id] = n; });
(nn || []).forEach(function(n) {
if (nm[n.id]) { nm[n.id].w = Math.min(1.0, (nm[n.id].w || 0.5) + 0.08); }
else { nm[n.id] = n; }
});
g.nodes = Object.values(nm);
g.edges.forEach(function(e) { em[ek(e)] = e; });
(ne || []).forEach(function(e) {
var k = ek(e);
if (em[k]) { em[k].weight = Math.min(1.0, (em[k].weight || 0.5) + 0.05); }
else { em[k] = e; }
});
g.edges = Object.values(em);
s._m = g; saveSession(s);
}
function _ra(g, q) {
if (!g || !g.nodes || !g.nodes.length) return [];
var words = q.toLowerCase().split(/\s+/).filter(function(w) { return w.length > 3; });
var sc = {};
g.nodes.forEach(function(n) {
var t = (n.content || '').toLowerCase();
sc[n.id] = words.filter(function(w) { return t.indexOf(w) !== -1; }).length * 0.6 + (n.w || 0.5) * 0.4;
});
(g.edges || []).forEach(function(e) {
if (sc[e.from] > 0.1) sc[e.to] = (sc[e.to] || 0) + sc[e.from] * (e.weight || 0.5) * 0.4;
});
return g.nodes.filter(function(n) { return sc[n.id] > 0.2; })
.sort(function(a, b) { return sc[b.id] - sc[a.id]; }).slice(0, 5)
.map(function(n) { return { id: n.id, content: n.content, score: sc[n.id] }; });
}
if (window.location.search.indexOf('reset=1') !== -1) {
clearSession();
window.history.replaceState({}, '', window.location.pathname);
}
var session = loadSession();
if (!session.uid) {
session.uid = 'u' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
saveSession(session);
}
var msgCount = session.count || 0;
function updateCountdown() {
var el = document.getElementById('neuron-demo-countdown');
if (!el) return;
var remaining = MAX - msgCount;
el.textContent = remaining + ' question' + (remaining === 1 ? '' : 's') + ' left';
el.style.color = '#ffffff';
el.style.fontWeight = '700';
}
window.neuronDemoReset = function() {
clearSession();
session = { messages: [], count: 0, context: '' };
msgCount = 0;
var msgs = document.getElementById('neuron-demo-messages');
if (msgs) msgs.innerHTML = '';
var input = document.getElementById('neuron-demo-text');
if (input) { input.disabled = false; input.placeholder = 'Ask me anything...'; }
var btn = document.getElementById('neuron-demo-send');
if (btn) btn.disabled = false;
addMsg('ai', 'Hey. What is on your mind?', true);
};
window.neuronDemoToggle = function() {
isOpen = !isOpen;
var panel = document.getElementById('neuron-demo-panel');
if (panel) panel.style.display = isOpen ? 'flex' : 'none';
var btn = document.getElementById('neuron-demo-btn');
if (btn) btn.style.display = isOpen ? 'none' : '';
var msgs = document.getElementById('neuron-demo-messages');
if (isOpen && turnstileVerified && msgs && msgs.style.display !== 'none' && msgs.children.length === 0) {
if (session.messages && session.messages.length > 0) {
session.messages.forEach(function(m) { addMsg(m.role, m.text, true); });
var remaining = MAX - msgCount;
if (remaining <= 0) {
var input = document.getElementById('neuron-demo-text');
if (input) { input.disabled = true; input.placeholder = 'Interaction limit reached'; }
}
} else if (!session.greeted) {
addMsg('ai', 'Hey. What is on your mind?', true);
session.greeted = true;
saveSession(session);
}
}
var input = document.getElementById('neuron-demo-text');
if (isOpen && input && !input.disabled) input.focus();
updateCountdown();
if (isOpen && !turnstileWidgetId && typeof turnstile !== 'undefined') {
var container = document.getElementById('neuron-demo-turnstile');
if (container) {
turnstileWidgetId = turnstile.render(container, {
sitekey: TURNSTILE_SITE_KEY,
size: 'compact',
callback: function(token) {
turnstileToken = token;
turnstileVerified = true;
if (typeof turnstile !== 'undefined' && turnstileWidgetId !== null) {
try { turnstile.remove(turnstileWidgetId); } catch(e) {}
turnstileWidgetId = null;
}
var gate = document.getElementById('neuron-demo-gate');
var msgs = document.getElementById('neuron-demo-messages');
var inputRow = document.getElementById('neuron-demo-input-row');
if (gate) gate.style.display = 'none';
if (msgs) msgs.style.display = 'flex';
if (inputRow) inputRow.style.display = 'flex';
addMsg('ai', 'Hey. What is on your mind?', true);
updateCountdown();
var inp = document.getElementById('neuron-demo-text');
if (inp) inp.focus();
},
'expired-callback': function() {
turnstileToken = '';
turnstileVerified = false;
}
});
}
}
};
function addMsg(role, text, skipSave) {
var msgs = document.getElementById('neuron-demo-messages');
if (!msgs) return null;
var el = document.createElement('div');
el.className = 'demo-msg demo-msg-' + role;
var avatar = document.createElement('div');
avatar.className = 'demo-msg-avatar';
if (role === 'ai') {
var img = document.createElement('img');
img.src = '/assets/brand/neuron-brain.png'; img.alt = 'Neuron';
avatar.appendChild(img);
} else {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('width', '14'); svg.setAttribute('height', '14');
svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('fill', 'none');
svg.setAttribute('stroke', 'currentColor'); svg.setAttribute('stroke-width', '2');
var p1 = document.createElementNS(svgNS, 'path');
p1.setAttribute('d', 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2');
var c1 = document.createElementNS(svgNS, 'circle');
c1.setAttribute('cx', '12'); c1.setAttribute('cy', '7'); c1.setAttribute('r', '4');
svg.appendChild(p1); svg.appendChild(c1); avatar.appendChild(svg);
}
var bubble = document.createElement('div');
bubble.className = 'demo-msg-bubble';
if (role === 'ai' && typeof marked !== 'undefined') {
try { bubble.innerHTML = marked.parse(text); } catch(e) { bubble.textContent = text; }
} else {
bubble.textContent = text;
}
if (role === 'ai') {
var bodyWrap = document.createElement('div');
bodyWrap.className = 'demo-msg-ai-body';
bodyWrap.appendChild(bubble);
if (!skipSave) {
var shareBtn = document.createElement('button');
shareBtn.className = 'demo-share-pill'; shareBtn.title = 'Share this response'; shareBtn.textContent = 'Share ↗';
shareBtn.onclick = async function() {
var prevUser = '';
if (session.messages) {
for (var i = session.messages.length - 1; i >= 0; i--) {
if (session.messages[i].role === 'user') { prevUser = session.messages[i].text; break; }
}
}
shareBtn.style.opacity = '0.4';
try {
var r = await fetch('/api/share', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ question: prevUser, answer: text })
});
var d = await r.json();
if (d.id) window.open('/share/' + d.id, '_blank');
} catch(e) {}
shareBtn.style.opacity = '1';
};
bodyWrap.appendChild(shareBtn);
}
el.appendChild(avatar); el.appendChild(bodyWrap);
} else {
el.appendChild(avatar); el.appendChild(bubble);
}
msgs.appendChild(el);
msgs.scrollTop = msgs.scrollHeight;
if (!skipSave && role !== 'thinking') {
session.messages = session.messages || [];
session.messages.push({ role: role, text: text });
if (session.messages.length > 40) session.messages = session.messages.slice(-40);
saveSession(session);
}
return el;
}
window.neuronDemoSend = async function() {
if (msgCount >= MAX) return;
var input = document.getElementById('neuron-demo-text');
var btn = document.getElementById('neuron-demo-send');
if (!input || btn.disabled) return;
var msg = input.value.trim();
if (!msg) return;
input.value = '';
btn.disabled = true;
addMsg('user', msg);
var thinking = document.createElement('div');
thinking.className = 'demo-msg demo-msg-thinking';
var thAvatar = document.createElement('div');
thAvatar.className = 'demo-msg-avatar';
var thImg = document.createElement('img');
thImg.src = '/assets/brand/neuron-brain.png'; thImg.alt = 'Neuron';
thAvatar.appendChild(thImg); thinking.appendChild(thAvatar);
var thDots = document.createElement('span');
thDots.className = 'demo-msg-thinking-dots';
thDots.innerHTML = '<span></span><span></span><span></span>';
thinking.appendChild(thDots);
var thMsgsEl = document.getElementById('neuron-demo-messages');
if (thMsgsEl) { thMsgsEl.appendChild(thinking); thMsgsEl.scrollTop = thMsgsEl.scrollHeight; }
try {
var hist = (session.messages || []).slice(-20).filter(function(m) { return m.role !== 'thinking'; }).map(function(m) {
return { role: m.role === 'ai' ? 'assistant' : 'user', content: m.text };
});
var activated_nodes = _ra(session._m, msg);
var questionsRemaining = Math.max(0, (MAX - msgCount) - 1);
var r = await fetch('/api/demo', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
message: msg,
history: hist,
cf_token: turnstileVerified && !session._cfSent ? turnstileToken : '',
uid: session.uid || '',
activated_nodes: activated_nodes,
engram_node_count: (session._m && session._m.nodes) ? session._m.nodes.length : 0,
questions_remaining: questionsRemaining,
is_last_question: questionsRemaining === 0
})
});
var d = await r.json();
if (thinking) thinking.remove();
_um(session, d.sn, d.se);
var reply = d.response || d.reply || d.message || '';
var isError = !reply || reply === 'Stepped out for a moment. Try again.';
if (!isError) {
msgCount++;
session.count = msgCount;
saveSession(session);
updateCountdown();
if (msgCount >= MAX && input) { input.disabled = true; input.placeholder = 'Interaction limit reached'; }
}
addMsg('ai', reply || 'Stepped out for a moment. Try again.');
} catch(e) {
if (thinking) thinking.remove();
addMsg('ai', 'Stepped out for a moment. Try again.');
}
if (msgCount < MAX && btn) btn.disabled = false;
if (input) input.focus();
};
var inp = document.getElementById('neuron-demo-text');
if (inp) {
inp.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); window.neuronDemoSend(); }
});
}
})()")
}
+156
View File
@@ -0,0 +1,156 @@
// checkout-auth.el -- Checkout Supabase auth: OAuth, email sign-in/sign-up.
// Compiled with: elc --target=js --bundle --minify --obfuscate
//
// Exposed globals: signInWith(provider), signUpWithEmail(), signInWithEmail(),
// showSignIn(), showSignUp(), resetPassword()
fn main() -> Void {
native_js("(function() {
var supabaseClient;
function initSupabase(cb) {
if (supabaseClient) { cb(); return; }
fetch('/api/supabase-config')
.then(function(r) { return r.json(); })
.then(function(cfg) {
supabaseClient = window.supabase.createClient(cfg.url, cfg.anon_key, {
auth: { flowType: 'implicit' }
});
cb();
})
.catch(function(err) {});
}
function showAuthMessage(msg, isError) {
var el = document.getElementById('auth-message');
if (!el) return;
el.textContent = msg;
el.style.display = 'block';
el.style.color = isError ? '#c0392b' : '#2ecc71';
}
function revealPaymentForm(user) {
if (user && user.id) { window._neuronSupaId = user.id; }
var auth = document.getElementById('auth-section');
if (auth) auth.style.display = 'none';
var payment = document.getElementById('payment-section');
if (payment) payment.style.display = '';
if (user) {
var badge = document.getElementById('auth-badge');
var name = user.user_metadata && user.user_metadata.full_name
? user.user_metadata.full_name : user.email || '';
if (badge) {
badge.innerHTML = '<div class=\"checkout-auth-badge\">'
+ '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M20 6L9 17l-5-5\" stroke=\"#0052A0\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'
+ 'Signed in as <strong>' + name + '</strong></div>';
badge.style.display = '';
}
var prompt = document.getElementById('signin-prompt');
if (prompt) prompt.style.display = 'none';
}
if (user && user.email) {
var emailEl = document.getElementById('buyer-email');
if (emailEl) emailEl.value = user.email;
}
var userEmail = user ? (user.email || '') : '';
var userName = user ? ((user.user_metadata && user.user_metadata.full_name) || '') : '';
if (typeof initStripe === 'function') initStripe(userEmail, userName);
}
function checkExistingSession() {
initSupabase(function() {
supabaseClient.auth.getUser().then(function(res) {
if (res.data && res.data.user) { revealPaymentForm(res.data.user); }
});
});
}
function handleAuthRedirect() {
initSupabase(function() {
supabaseClient.auth.onAuthStateChange(function(event, session) {
if ((event === 'SIGNED_IN' || event === 'INITIAL_SESSION') && session && session.user) {
revealPaymentForm(session.user);
}
});
});
}
window.signInWith = function(provider) {
var btns = document.querySelectorAll('.checkout-social-btn');
btns.forEach(function(b) { b.disabled = true; });
initSupabase(function() {
supabaseClient.auth.signInWithOAuth({
provider: provider,
options: { redirectTo: window.location.href }
}).then(function(result) {
if (result.error) {
showAuthMessage(result.error.message || 'Sign-in failed. Please try again.', true);
btns.forEach(function(b) { b.disabled = false; });
}
});
});
};
window.signUpWithEmail = function() {
var email = document.getElementById('auth-email').value.trim();
var password = document.getElementById('auth-password').value;
if (!email || !password) { showAuthMessage('Please enter your email and a password.', true); return; }
if (password.length < 8) { showAuthMessage('Password must be at least 8 characters.', true); return; }
initSupabase(function() {
supabaseClient.auth.signUp({ email: email, password: password }).then(function(result) {
if (result.error) { showAuthMessage(result.error.message, true); return; }
if (result.data && result.data.session) {
revealPaymentForm(result.data.session.user);
} else {
showAuthMessage('Check your email to confirm your account, then come back to complete your purchase.', false);
}
});
});
};
window.showSignIn = function() {
var form = document.getElementById('email-auth-form');
if (!form) return;
var btn = form.querySelector('.checkout-email-btn');
if (btn) { btn.textContent = 'Sign in →'; btn.onclick = window.signInWithEmail; }
};
window.showSignUp = function() {
var form = document.getElementById('email-auth-form');
if (!form) return;
var btn = form.querySelector('.checkout-email-btn');
if (btn) { btn.textContent = 'Create account →'; btn.onclick = window.signUpWithEmail; }
};
window.signInWithEmail = function() {
var email = document.getElementById('auth-email').value.trim();
var password = document.getElementById('auth-password').value;
if (!email || !password) { showAuthMessage('Please enter your email and password.', true); return; }
initSupabase(function() {
supabaseClient.auth.signInWithPassword({ email: email, password: password }).then(function(result) {
if (result.error) { showAuthMessage(result.error.message, true); return; }
revealPaymentForm(result.data.session.user);
});
});
};
window.resetPassword = function() {
var email = document.getElementById('auth-email').value.trim();
if (!email) { showAuthMessage('Enter your email address above first.', true); return; }
initSupabase(function() {
supabaseClient.auth.resetPasswordForEmail(email, {
redirectTo: window.location.origin + '/checkout?plan=' + (new URLSearchParams(window.location.search).get('plan') || 'professional')
}).then(function(result) {
if (result.error) { showAuthMessage(result.error.message, true); }
else { showAuthMessage('Password reset email sent. Check your inbox.', false); }
});
});
};
handleAuthRedirect();
checkExistingSession();
})()")
}
+17
View File
@@ -0,0 +1,17 @@
// checkout-free.el -- Free plan: reveal payment section after auth completes.
// Watches the auth-badge element; when it becomes visible, shows payment-section.
// Compiled with: elc --target=js --bundle --minify --obfuscate
fn main() -> Void {
native_js("(function() {
var pay = document.getElementById('payment-section');
if (!pay) return;
var timer = setInterval(function() {
var badge = document.getElementById('auth-badge');
if (badge && badge.offsetParent !== null) {
pay.style.display = '';
clearInterval(timer);
}
}, 150);
})()")
}
+200
View File
@@ -0,0 +1,200 @@
// checkout-stripe.el -- Stripe Payment Element setup and form submission.
// Reads NEURON_CFG.plan and NEURON_CFG.pub_key from window.
// Compiled with: elc --target=js --bundle --minify --obfuscate
//
// Required globals: window.NEURON_CFG.plan, window.NEURON_CFG.pub_key
// Required CDN: Stripe.js loaded before this script
fn main() -> Void {
native_js("(function() {
var cfg = window.NEURON_CFG || {};
var PLAN = cfg.plan || '';
var STRIPE_PK = cfg.pub_key || '';
var stripe, elements;
function waitForStripe(cb) {
if (window.Stripe) { cb(); return; }
setTimeout(function() { waitForStripe(cb); }, 50);
}
function showMessage(msg) {
var el = document.getElementById('payment-message');
if (el) { el.textContent = msg; el.style.display = 'block'; }
}
function setLoading(loading) {
var btn = document.getElementById('submit-btn');
var label = document.getElementById('submit-label');
var spinner = document.getElementById('submit-spinner');
if (btn) btn.disabled = loading;
if (label) label.style.display = loading ? 'none' : '';
if (spinner) spinner.style.display = loading ? '' : 'none';
}
window._neuronMode = 'payment';
var paymentEl = null;
function appearance() {
return {
theme: 'flat',
variables: {
colorPrimary: '#0052A0',
colorBackground: '#ffffff',
colorText: '#1A1A2E',
colorDanger: '#c0392b',
colorTextPlaceholder: '#9B9BAD',
borderRadius: '0px',
fontFamily: 'system-ui, -apple-system, sans-serif',
fontSizeBase: '15px',
fontWeightNormal: '300',
spacingUnit: '4px'
},
rules: {
'.Input': { border: '1px solid rgba(0,82,160,.22)', boxShadow: 'none', padding: '10px 14px' },
'.Input:focus': { border: '1px solid rgba(0,82,160,.6)', boxShadow: '0 0 0 3px rgba(0,82,160,.08)', outline: 'none' },
'.Label': { fontSize: '11px', fontWeight: '500', letterSpacing: '.06em', textTransform: 'uppercase', color: '#6B6B7E', marginBottom: '6px' },
'.Tab': { border: '1px solid rgba(0,82,160,.18)', boxShadow: 'none' },
'.Tab--selected':{ border: '1px solid rgba(0,82,160,.5)', boxShadow: '0 0 0 2px rgba(0,82,160,.12)' },
'.Error': { color: '#c0392b' }
}
};
}
function currentTiming() {
var later = document.getElementById('timing-later');
return (later && later.checked) ? 'later' : 'now';
}
function fetchAndMount() {
var submitBtn = document.getElementById('submit-btn');
if (submitBtn) submitBtn.disabled = true;
if (paymentEl) { try { paymentEl.unmount(); } catch(e) {} paymentEl = null; }
var hostEl = document.getElementById('payment-element');
if (hostEl && !document.querySelector('.checkout-element-loading')) {
var d = document.createElement('div');
d.className = 'checkout-element-loading';
d.textContent = 'Loading payment form...';
hostEl.appendChild(d);
}
var timing = currentTiming();
return fetch('/api/payment-intent', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ plan: PLAN, timing: timing })
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.error === 'sold_out') {
showMessage('All 1,000 Founding Member spots have been claimed. Thank you for your interest - please consider the Professional plan.');
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Sold out'; }
return;
}
if (!data.client_secret) { showMessage('Unable to initialise payment. Please try again.'); return; }
window._neuronMode = data.setup_mode ? 'setup' : 'payment';
window._neuronPiId = data.id || (data.client_secret ? data.client_secret.split('_secret_')[0] : '');
var submitLabel = document.getElementById('submit-label');
if (submitLabel) {
submitLabel.textContent = window._neuronMode === 'setup'
? 'Save my card - no charge today →'
: 'Complete purchase →';
}
waitForStripe(function() {
if (!stripe) stripe = Stripe(STRIPE_PK);
elements = stripe.elements({ clientSecret: data.client_secret, appearance: appearance() });
paymentEl = elements.create('payment', {
fields: { billingDetails: { name: 'never', email: 'never' } }
});
paymentEl.mount('#payment-element');
paymentEl.on('ready', function() {
var ld = document.querySelector('.checkout-element-loading');
if (ld) ld.remove();
if (submitBtn) submitBtn.disabled = false;
});
});
})
.catch(function() {
showMessage('Unable to connect. Please check your connection and try again.');
});
}
fetchAndMount();
var tNow = document.getElementById('timing-now');
var tLater = document.getElementById('timing-later');
if (tNow) tNow.addEventListener('change', fetchAndMount);
if (tLater) tLater.addEventListener('change', fetchAndMount);
var form = document.getElementById('payment-form');
if (form) form.addEventListener('submit', async function(e) {
e.preventDefault();
if (!stripe || !elements) return;
var attestCb = document.getElementById('founding-attest-cb');
if (attestCb && !attestCb.checked) {
var warn = document.getElementById('attest-warn');
if (warn) warn.style.display = 'block';
attestCb.closest('label').scrollIntoView({ behavior: 'smooth', block: 'center' });
return;
}
var name = document.getElementById('buyer-name').value.trim();
var email = document.getElementById('buyer-email').value.trim();
if (!name || !email) { showMessage('Please enter your name and email.'); return; }
if (attestCb) {
try {
await fetch('/api/attest', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
plan: PLAN, name: name, email: email,
timestamp: new Date().toISOString(),
attestation: 'I am joining as a genuine early user, not to extract proprietary information about Neuron technology, architecture, or roadmap. I will engage in good faith. I understand that if this is not my intent, a different plan is a better fit.',
user_agent: navigator.userAgent
})
});
} catch(e) {}
}
setLoading(true);
var pmsg = document.getElementById('payment-message');
if (pmsg) pmsg.style.display = 'none';
if (window._neuronPiId) {
try {
await fetch('/api/link-customer', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
pi_id: window._neuronPiId,
email: email,
name: name,
plan: PLAN,
timing: currentTiming(),
mode: window._neuronMode || 'payment',
supabase_user_id: window._neuronSupaId || ''
})
});
} catch(e) {}
}
var confirmParams = {
return_url: window.location.origin + '/account?welcome=1',
payment_method_data: { billing_details: { name: name, email: email } }
};
var result;
if (window._neuronMode === 'setup') {
result = await stripe.confirmSetup({ elements: elements, confirmParams: confirmParams });
} else {
confirmParams.receipt_email = email;
result = await stripe.confirmPayment({ elements: elements, confirmParams: confirmParams });
}
if (result.error) {
showMessage(result.error.message || (window._neuronMode === 'setup'
? 'Could not save your card. Please try again.'
: 'Payment failed. Please try again.'));
setLoading(false);
}
});
})()")
}
+66
View File
@@ -0,0 +1,66 @@
// enterprise.el -- Enterprise inquiry form submission and headcount filter.
// Compiled with: elc --target=js --bundle --minify --obfuscate
//
// Exposed globals: entHeadcountChange(val)
fn main() -> Void {
native_js("(function() {
var form = document.getElementById('enterprise-form');
var submitBtn = document.getElementById('ent-submit');
var successDiv = document.getElementById('enterprise-success');
var errorDiv = document.getElementById('ent-form-error');
if (!form) return;
window.entHeadcountChange = function(val) {
var msgSecondary = document.getElementById('ent-filter-msg-secondary');
var msgYes = document.getElementById('ent-filter-msg-yes');
if (msgSecondary) msgSecondary.style.display = val === 'secondary' ? 'block' : 'none';
if (msgYes) msgYes.style.display = val === 'yes' ? 'block' : 'none';
if (submitBtn) {
submitBtn.disabled = val === 'yes';
submitBtn.style.opacity = val === 'yes' ? '0.35' : '1';
submitBtn.style.cursor = val === 'yes' ? 'not-allowed' : 'pointer';
}
};
form.addEventListener('submit', function(e) {
e.preventDefault();
var headcount = document.getElementById('ent-headcount').value;
if (headcount === 'yes') {
var msgYes = document.getElementById('ent-filter-msg-yes');
if (msgYes) msgYes.style.display = 'block';
return;
}
var name = document.getElementById('ent-name').value.trim();
var email = document.getElementById('ent-email').value.trim();
var company = document.getElementById('ent-company').value.trim();
var size = document.getElementById('ent-size').value;
var useCase = document.getElementById('ent-use').value.trim();
if (!name || !email || !company || !size || !useCase || !headcount) {
if (errorDiv) { errorDiv.textContent = 'Please fill out all fields.'; errorDiv.style.display = 'block'; }
return;
}
if (errorDiv) errorDiv.style.display = 'none';
if (submitBtn) { submitBtn.textContent = 'Sending...'; submitBtn.disabled = true; }
fetch('/api/enterprise-inquiry', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ name: name, email: email, company: company, size: size, use_case: useCase, headcount: headcount })
})
.then(function(r) { return r.json(); })
.then(function() {
if (form) form.style.display = 'none';
if (successDiv) successDiv.style.display = 'block';
})
.catch(function() {
if (submitBtn) { submitBtn.textContent = 'Send inquiry →'; submitBtn.disabled = false; }
if (errorDiv) { errorDiv.textContent = 'Something went wrong. Email enterprise@neurontechnologies.ai directly.'; errorDiv.style.display = 'block'; }
});
});
})()")
}
+19
View File
@@ -0,0 +1,19 @@
// environmental.el -- Environmental page efficiency calculator slider.
// Compiled with: elc --target=js --bundle --minify --obfuscate
fn main() -> Void {
native_js("(function() {
var slider = document.getElementById('calc-slider');
var spendEl = document.getElementById('calc-spend');
var savingsEl = document.getElementById('calc-savings');
if (!slider) return;
function update() {
var monthly = parseInt(slider.value, 10);
var annual = Math.round(monthly * 0.35 * 12);
if (spendEl) spendEl.textContent = '$' + monthly;
if (savingsEl) savingsEl.textContent = '$' + annual;
}
slider.addEventListener('input', update);
update();
})()")
}
+186
View File
@@ -0,0 +1,186 @@
// gallery.el -- Gallery page: search/filter, sort, nav scroll, Supabase voting.
// Compiled with: elc --target=js --bundle --minify --obfuscate
//
// Required globals: window.NEURON_CFG.supabase_url, window.NEURON_CFG.supabase_anon_key
// Required CDN: supabase-js@2
fn main() -> Void {
native_js("(function() {
// Nav scroll effect
var nav = document.getElementById('nav');
if (nav) window.addEventListener('scroll', function() {
nav.classList.toggle('scrolled', window.scrollY > 10);
}, { passive: true });
// Hamburger
var btn = document.getElementById('nav-hamburger');
var menu = document.getElementById('nav-mobile');
if (btn && menu) {
function navClose() { menu.classList.remove('open'); btn.setAttribute('aria-expanded','false'); }
function navOpen() { menu.classList.add('open'); btn.setAttribute('aria-expanded','true'); }
btn.addEventListener('click', function(e) { e.stopPropagation(); menu.classList.contains('open') ? navClose() : navOpen(); });
menu.querySelectorAll('a').forEach(function(a) { a.addEventListener('click', navClose); });
document.addEventListener('click', function(e) { if (!nav.contains(e.target)) navClose(); });
document.addEventListener('keydown', function(e) { if (e.key === 'Escape') navClose(); });
window.addEventListener('resize', function() { if (window.innerWidth > 1060) navClose(); });
}
// Dropdown
var ddBtn = document.querySelector('.nav-dropdown-btn');
var dd = document.querySelector('.nav-dropdown');
if (ddBtn && dd) {
ddBtn.addEventListener('click', function(e) {
e.stopPropagation();
var isOpen = dd.classList.contains('open');
dd.classList.toggle('open');
ddBtn.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
});
dd.querySelectorAll('.nav-dropdown-item').forEach(function(a) { a.addEventListener('click', function() { dd.classList.remove('open'); }); });
document.addEventListener('click', function() { dd.classList.remove('open'); });
}
// Search
var searchEl = document.getElementById('gal-search');
var grid = document.getElementById('gallery-grid');
var noResults = document.getElementById('no-results');
function filterCards() {
var q = (searchEl ? searchEl.value : '').toLowerCase().trim();
var cards = grid ? grid.querySelectorAll('.gal-card') : [];
var visible = 0;
cards.forEach(function(c) {
var match = !q || c.textContent.toLowerCase().indexOf(q) !== -1;
c.classList.toggle('hidden', !match);
if (match) visible++;
});
if (noResults) noResults.style.display = visible === 0 && q ? 'block' : 'none';
}
if (searchEl) searchEl.addEventListener('input', filterCards);
// Sort
window.setSort = function(mode, btn) {
document.querySelectorAll('.sort-btn').forEach(function(b) { b.classList.remove('active'); });
btn.classList.add('active');
if (!grid) return;
var cards = Array.from(grid.querySelectorAll('.gal-card'));
cards.sort(function(a, b) {
if (mode === 'top') {
return parseInt(b.getAttribute('data-score') || '0') - parseInt(a.getAttribute('data-score') || '0');
} else {
return parseInt(b.getAttribute('data-ts') || '0') - parseInt(a.getAttribute('data-ts') || '0');
}
});
cards.forEach(function(c) { grid.appendChild(c); });
};
// Voting via Supabase
var cfg = window.NEURON_CFG || {};
var sbUrl = cfg.supabase_url;
var sbKey = cfg.supabase_anon_key;
if (!sbUrl || !sbKey) return;
var sb = window.supabase.createClient(sbUrl, sbKey);
var token = null;
var votes = {};
function getCtrl(sid) {
var found = null;
document.querySelectorAll('.vote-controls').forEach(function(c) {
if (c.getAttribute('data-share-id') === sid) found = c;
});
return found;
}
function applyState(ctrl, state) {
var sid = ctrl.getAttribute('data-share-id');
votes[sid] = state.user_vote || 'none';
var scoreEl = ctrl.querySelector('.vote-score');
if (scoreEl && state.score != null) scoreEl.textContent = state.score;
var upBtn = ctrl.querySelector('.vote-btn.vote-up');
var dnBtn = ctrl.querySelector('.vote-btn.vote-down');
if (upBtn) { upBtn.disabled = false; upBtn.classList.toggle('is-active', state.user_vote === 'up'); }
if (dnBtn) { dnBtn.disabled = false; dnBtn.classList.toggle('is-active', state.user_vote === 'down'); }
}
function loadVoteState(sid) {
var url = '/api/vote-state/' + sid;
if (token) url += '?access_token=' + encodeURIComponent(token);
fetch(url).then(function(r) { return r.json(); }).then(function(d) {
var ctrl = getCtrl(sid);
if (ctrl) applyState(ctrl, d);
}).catch(function() {});
}
function loadAll() {
document.querySelectorAll('.vote-controls').forEach(function(ctrl) {
var id = ctrl.getAttribute('data-share-id');
if (id) loadVoteState(id);
});
}
function castVote(sid, direction) {
if (!token) { showSignIn(); return; }
var ctrl = getCtrl(sid);
var btns = ctrl ? ctrl.querySelectorAll('.vote-btn') : [];
btns.forEach(function(b) { b.disabled = true; b.classList.add('is-loading'); });
fetch('/api/vote', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({access_token: token, id: sid, direction: direction})
}).then(function(r) { return r.json(); }).then(function(d) {
if (ctrl && d.ok) applyState(ctrl, d);
btns.forEach(function(b) { b.disabled = false; b.classList.remove('is-loading'); });
}).catch(function() {
btns.forEach(function(b) { b.disabled = false; b.classList.remove('is-loading'); });
});
}
document.querySelectorAll('.vote-controls').forEach(function(ctrl) {
ctrl.querySelectorAll('.vote-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
var sid = ctrl.getAttribute('data-share-id');
if (!token) { showSignIn(); return; }
var dir = btn.getAttribute('data-direction');
var cur = votes[sid] || 'none';
castVote(sid, cur === dir ? 'none' : dir);
});
});
});
var modal = document.getElementById('signin-modal');
var cancelEl = document.getElementById('signin-cancel');
var sendEl = document.getElementById('signin-send');
var emailEl = document.getElementById('signin-email');
var msgEl = document.getElementById('signin-msg');
function showSignIn() { if (modal) modal.classList.add('open'); if (emailEl) emailEl.focus(); }
if (cancelEl) cancelEl.addEventListener('click', function() { modal.classList.remove('open'); });
if (modal) modal.addEventListener('click', function(e) { if (e.target === modal) modal.classList.remove('open'); });
if (sendEl) sendEl.addEventListener('click', function() {
var email = emailEl ? emailEl.value.trim() : '';
if (!email) { if (msgEl) msgEl.textContent = 'Please enter your email.'; return; }
sendEl.disabled = true;
if (msgEl) msgEl.textContent = 'Sending...';
sb.auth.signInWithOtp({ email: email, options: { emailRedirectTo: window.location.href } })
.then(function(r) {
sendEl.disabled = false;
if (msgEl) msgEl.textContent = r.error
? (r.error.message || 'Error. Try again.')
: 'Check your email for a sign-in link.';
});
});
if (emailEl) emailEl.addEventListener('keydown', function(e) { if (e.key === 'Enter' && sendEl) sendEl.click(); });
sb.auth.onAuthStateChange(function(event, session) {
token = session ? session.access_token : null;
if (token && modal) modal.classList.remove('open');
loadAll();
});
sb.auth.getSession().then(function(r) {
token = r.data && r.data.session ? r.data.session.access_token : null;
loadAll();
});
})()")
}
+67
View File
@@ -0,0 +1,67 @@
// main.el -- Share page voting, copy-for-platform social sharing.
// Compiled with: elc --target=js --bundle --minify --obfuscate
//
// Required globals: window.NEURON_CFG.id, window.NEURON_CFG.card_url
// Exposed globals: copyForPlatform(platform, btn)
fn main() -> Void {
native_js("(function() {
var cfg = window.NEURON_CFG || {};
var shareId = cfg.id || '';
var cardUrl = cfg.card_url || '';
// Copy-for-platform: format and copy share text for different social platforms
window.copyForPlatform = function(platform, btn) {
var text = '';
var url = window.location.href;
if (platform === 'tiktok') {
text = 'Neuron said something interesting. Watch this. ' + url;
} else if (platform === 'snapchat') {
text = url;
} else {
text = url;
}
navigator.clipboard.writeText(text).then(function() {
if (btn) {
btn.classList.add('copied');
setTimeout(function() { btn.classList.remove('copied'); }, 1500);
}
}).catch(function() {});
};
// Vote buttons on share page
var voteUp = document.getElementById('vote-up');
var voteDown = document.getElementById('vote-down');
var voteScore = document.getElementById('vote-score');
var voted = null;
function updateVoteUI(direction, score) {
if (voteUp) voteUp.classList.toggle('voted-up', direction === 'up');
if (voteDown) voteDown.classList.toggle('voted-down', direction === 'down');
if (voteScore && score != null) voteScore.textContent = score;
}
function castVote(direction) {
var next = voted === direction ? 'none' : direction;
voted = next === 'none' ? null : next;
fetch('/api/vote', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ id: shareId, direction: next })
}).then(function(r) { return r.json(); }).then(function(d) {
if (d.ok) updateVoteUI(next === 'none' ? null : next, d.score);
}).catch(function() {});
}
if (voteUp) voteUp.addEventListener('click', function() { castVote('up'); });
if (voteDown) voteDown.addEventListener('click', function() { castVote('down'); });
// Load initial vote state
if (shareId) {
fetch('/api/vote-state/' + shareId)
.then(function(r) { return r.json(); })
.then(function(d) { updateVoteUI(d.user_vote || null, d.score); })
.catch(function() {});
}
})()")
}
+42
View File
@@ -0,0 +1,42 @@
// marketplace.el -- Marketplace developer interest form submission.
// Compiled with: elc --target=js --bundle --minify --obfuscate
fn main() -> Void {
native_js("(function() {
var form = document.getElementById('dev-form');
if (!form) return;
form.addEventListener('submit', async function(e) {
e.preventDefault();
var msg = document.getElementById('dev-msg');
var btn = form.querySelector('button[type=submit]');
btn.disabled = true;
btn.textContent = 'Sending...';
try {
var r = await fetch('/api/developer-interest', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
name: document.getElementById('dev-name').value,
email: document.getElementById('dev-email').value,
idea: document.getElementById('dev-idea').value
})
});
msg.style.display = 'block';
if (r.ok) {
msg.textContent = 'Got it. Will review it personally and reach out.';
msg.style.color = 'var(--navy)';
form.reset();
} else {
msg.textContent = 'Something went wrong. Email developers@neurontechnologies.ai directly.';
msg.style.color = '#c44';
}
} catch(err) {
msg.style.display = 'block';
msg.textContent = 'Connection error. Email developers@neurontechnologies.ai directly.';
msg.style.color = '#c44';
}
btn.disabled = false;
btn.textContent = 'Send interest →';
});
})()")
}
+56
View File
@@ -0,0 +1,56 @@
// nav.el -- Navigation hamburger menu and Mission dropdown.
// Compiled with: elc --target=js --bundle --minify --obfuscate
fn main() -> Void {
native_js("(function() {
var btn = document.getElementById('nav-hamburger');
var menu = document.getElementById('nav-mobile');
var nav = document.getElementById('nav');
if (!btn || !menu) return;
function close() {
menu.classList.remove('open');
btn.setAttribute('aria-expanded', 'false');
}
function open() {
menu.classList.add('open');
btn.setAttribute('aria-expanded', 'true');
}
function toggle() {
if (menu.classList.contains('open')) { close(); } else { open(); }
}
btn.addEventListener('click', function(e) { e.stopPropagation(); toggle(); });
var ddBtn = document.querySelector('.nav-dropdown-btn');
var dd = document.querySelector('.nav-dropdown');
if (ddBtn && dd) {
ddBtn.addEventListener('click', function(e) {
e.stopPropagation();
var isOpen = dd.classList.contains('open');
dd.classList.toggle('open');
ddBtn.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
});
dd.querySelectorAll('.nav-dropdown-item').forEach(function(a) {
a.addEventListener('click', function() { dd.classList.remove('open'); });
});
document.addEventListener('click', function() { dd.classList.remove('open'); });
}
menu.querySelectorAll('a').forEach(function(a) {
a.addEventListener('click', close);
});
document.addEventListener('click', function(e) {
if (!nav.contains(e.target)) { close(); }
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') { close(); }
});
window.addEventListener('resize', function() {
if (window.innerWidth > 1060) { close(); }
});
})()")
}
+99
View File
@@ -0,0 +1,99 @@
// styles.el -- Landing page JS: nav scroll, scroll-reveal, founding counter polling,
// checkout button routing.
// Compiled with: elc --target=js --bundle --minify --obfuscate
fn main() -> Void {
native_js("(function() {
'use strict';
// Nav scroll effect
var nav = document.getElementById('nav');
if (nav) {
window.addEventListener('scroll', function() {
if (window.scrollY > 40) {
nav.classList.add('scrolled');
} else {
nav.classList.remove('scrolled');
}
}, { passive: true });
}
// Auto-open chat if ?open=chat in URL
if (typeof URLSearchParams !== 'undefined' && new URLSearchParams(window.location.search).get('open') === 'chat') {
setTimeout(function() { if (typeof neuronDemoToggle === 'function') neuronDemoToggle(); }, 600);
}
// Scroll reveal via IntersectionObserver
var revealEls = document.querySelectorAll('.reveal');
if ('IntersectionObserver' in window) {
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
revealEls.forEach(function(el) { observer.observe(el); });
} else {
revealEls.forEach(function(el) { el.classList.add('visible'); });
}
// Founding counter polling
var prevSold = null;
function updateFoundingUI(data) {
var remaining = data.remaining;
var sold = data.sold;
var total = data.total;
var pct = Math.round((sold / total) * 100);
var flash = prevSold !== null && sold > prevSold;
prevSold = sold;
var spotLabel = document.querySelector('.founding-spots-label');
if (spotLabel) spotLabel.textContent = 'Only ' + remaining + ' left';
var spotFill = document.querySelector('.founding-spots-fill');
if (spotFill) spotFill.style.width = pct + '%';
var spotSub = document.querySelector('.founding-spots-sub');
if (spotSub) spotSub.textContent = sold + ' of ' + total + ' claimed';
var bannerCount = document.querySelector('.founding-banner-count');
if (bannerCount) {
bannerCount.textContent = remaining;
if (flash) {
bannerCount.style.color = '#0078D4';
setTimeout(function() { bannerCount.style.color = ''; }, 1200);
}
}
var bannerFill = document.querySelector('.founding-banner-fill');
if (bannerFill) bannerFill.style.width = pct + '%';
}
function pollFoundingCount() {
fetch('/api/founding-count')
.then(function(r) { return r.json(); })
.then(function(data) { updateFoundingUI(data); })
.catch(function() {});
}
pollFoundingCount();
setInterval(pollFoundingCount, 90000);
// Hide chat widget on non-marketing pages
if (window.location.pathname.indexOf('/checkout') === 0 ||
window.location.pathname.indexOf('/account') === 0 ||
window.location.pathname.indexOf('/legal') === 0 ||
window.location.pathname.indexOf('/marketplace/success') === 0) {
var demoBtn = document.getElementById('neuron-demo-btn');
var demoPanel = document.getElementById('neuron-demo-panel');
if (demoBtn) demoBtn.style.display = 'none';
if (demoPanel) demoPanel.style.display = 'none';
}
// Checkout buttons
var checkoutBtns = document.querySelectorAll('[data-checkout]');
checkoutBtns.forEach(function(btn) {
btn.addEventListener('click', function() {
window.location.href = '/checkout?plan=' + btn.getAttribute('data-checkout');
});
});
})()")
}
+15 -1
View File
@@ -341,7 +341,7 @@ body::before{content:'';position:fixed;inset:0;pointer-events:none;z-index:0;bac
<a href=\"https://neurontechnologies.ai\" class=\"cta-btn\">Try Neuron &#8599;</a> <a href=\"https://neurontechnologies.ai\" class=\"cta-btn\">Try Neuron &#8599;</a>
</div> </div>
</div> </div>
<script>window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.id=\"" + id + "\";window.NEURON_CFG.card_url=\"" + card_url + "\";</script><script src=\"/assets/js/94727a87c328.js\" defer></script> <script>window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.id=\"" + id + "\";window.NEURON_CFG.card_url=\"" + card_url + "\";</script><script src=\"/js/main.js\" defer></script>
</body> </body>
</html>" </html>"
} }
@@ -843,6 +843,19 @@ fn handle_request(method: String, path: String, body: String) -> String {
return content return content
} }
// Compiled client-side JS: /js/*
// Served from dist/js/ (compiled by elc --target=js at build time).
// LANDING_ROOT/js maps to the dist/js output directory in the image.
if str_starts_with(path, "/js/") {
let rel: String = str_slice(path, 4, str_len(path))
let abs: String = src_dir + "/js/" + rel
let content: String = read_asset(abs)
if str_eq(content, "") {
return "{\"__status__\":404,\"error\":\"not found\"}"
}
return content
}
// Brand assets: /brand/* // Brand assets: /brand/*
if str_starts_with(path, "/brand/") { if str_starts_with(path, "/brand/") {
let rel: String = str_slice(path, 7, str_len(path)) let rel: String = str_slice(path, 7, str_len(path))
@@ -1890,6 +1903,7 @@ println(" POST /api/checkout → Stripe checkout session")
println(" POST /api/webhooks/stripe → Stripe webhook") println(" POST /api/webhooks/stripe → Stripe webhook")
println(" GET /marketplace/success → post-purchase success page") println(" GET /marketplace/success → post-purchase success page")
println(" GET /assets/* → static files") println(" GET /assets/* → static files")
println(" GET /js/* → compiled client-side JS (dist/js/)")
println(" GET /brand/* → brand files") println(" GET /brand/* → brand files")
println(" GET /api/supabase-config → public Supabase config (URL + anon key)") println(" GET /api/supabase-config → public Supabase config (URL + anon key)")
println("") println("")
+1 -1
View File
@@ -132,7 +132,7 @@ fn marketplace() -> String {
</div> </div>
</div> </div>
<script src=\"/assets/js/ce12d682c9e6.js\" defer></script> <script src=\"/js/marketplace.js\" defer></script>
</section> </section>
+1 -1
View File
@@ -52,6 +52,6 @@ fn nav() -> String {
</div> </div>
</nav> </nav>
<script src=\"/assets/js/529d45d105c9.js\" defer></script> <script src=\"/js/nav.js\" defer></script>
" "
} }
+2 -2
View File
@@ -1967,7 +1967,7 @@ fn page_open() -> String {
fn page_close() -> String { fn page_close() -> String {
return " return "
<script src=\"/assets/js/407e72cd7182.js\" defer></script> <script src=\"/js/chat-widget.js\" defer></script>
<!-- Neuron Demo Chat Widget --> <!-- Neuron Demo Chat Widget -->
<div id=\"neuron-demo-btn\"> <div id=\"neuron-demo-btn\">
@@ -2023,7 +2023,7 @@ fn page_close() -> String {
</div> </div>
</div> </div>
<script src=\"/assets/js/02ecc8cf6542.js\" defer></script> <script src=\"/js/styles.js\" defer></script>
</body> </body>
</html> </html>
" "