feat(native-el-ui): rewrite checkout/main to use el-html vessel, fix page_close linker error

- checkout.el and main.el: replace raw import of el-html vessel with direct
  extern fn declarations; implementations come from dist/elhtml_impl.c (c_source)
- Add src/elhtml.el as reference file (all el-html extern fn declarations; not imported)
- dist/elhtml_impl.c: pre-compiled el-html vessel C (strips int main + sample global)
- dist/page_close.c: pre-compiled page_close implementation; elc OOMs after emitting
  the ~71KB page_open body and silently drops page_close, so supply it as c_source
- manifest.el: add elhtml_impl.c and page_close.c as c_source entries
- .gitignore: un-ignore dist/elhtml_impl.c and dist/page_close.c
This commit is contained in:
2026-05-08 22:14:38 -05:00
parent 2447310367
commit f2ab12e65b
7 changed files with 909 additions and 328 deletions
+214 -143
View File
@@ -21,6 +21,48 @@
// GET /brand/* brand assets via handle_request
// GET * 404 JSON (non-/ paths not used by this SPA)
// el-html vessel extern declarations (implementations in dist/elhtml_impl.c)
extern fn el_escape(s: String) -> String
extern fn el_text(s: String) -> String
extern fn el_attr(name: String, value: String) -> String
extern fn el_div(attrs: String, children: String) -> String
extern fn el_section(attrs: String, children: String) -> String
extern fn el_article(attrs: String, children: String) -> String
extern fn el_header(attrs: String, children: String) -> String
extern fn el_footer(attrs: String, children: String) -> String
extern fn el_main(attrs: String, children: String) -> String
extern fn el_nav(attrs: String, children: String) -> String
extern fn el_aside(attrs: String, children: String) -> String
extern fn el_ul(attrs: String, children: String) -> String
extern fn el_ol(attrs: String, children: String) -> String
extern fn el_li(attrs: String, children: String) -> String
extern fn el_p(attrs: String, children: String) -> String
extern fn el_span(attrs: String, children: String) -> String
extern fn el_form(attrs: String, children: String) -> String
extern fn el_h1(attrs: String, text: String) -> String
extern fn el_h2(attrs: String, text: String) -> String
extern fn el_h3(attrs: String, text: String) -> String
extern fn el_h4(attrs: String, text: String) -> String
extern fn el_button(attrs: String, label: String) -> String
extern fn el_a(href: String, attrs: String, children: String) -> String
extern fn el_input(type_attr: String, attrs: String) -> String
extern fn el_textarea(attrs: String, value: String) -> String
extern fn el_label(for_id: String, attrs: String, children: String) -> String
extern fn el_img(src: String, alt: String, attrs: String) -> String
extern fn el_strong(children: String) -> String
extern fn el_em(children: String) -> String
extern fn el_code(children: String) -> String
extern fn el_pre(attrs: String, children: String) -> String
extern fn el_hr() -> String
extern fn el_br() -> String
extern fn el_html_doc(lang: String, head_html: String, body_html: String) -> String
extern fn el_meta(name: String, content: String) -> String
extern fn el_meta_charset(charset: String) -> String
extern fn el_link_stylesheet(href: String) -> String
extern fn el_script_src(src: String, defer_load: Bool) -> String
extern fn el_script_inline(js: String) -> String
extern fn el_title(text: String) -> String
from nav import { nav }
from hero import { hero }
from pillars import { pillars }
@@ -193,140 +235,167 @@ fn share_card_page(question: String, answer_plain: String, answer_html_in: Strin
// TikTok and Snapchat have no web URL share scheme use clipboard copy
let tiktok_copy_text: String = "Copied+%E2%80%94+paste+into+TikTok"
let snap_copy_text: String = "Copied+%E2%80%94+paste+into+Snapchat"
return <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Things Neuron Said</title>
<meta property="og:title" content="Things Neuron Said">
<meta property="og:description" content="{og_desc}">
<meta property="og:image" content="https://neurontechnologies.ai/assets/brand/neuron-wordmark-on-light@2x.png">
<meta property="og:type" content="website">
<meta property="og:url" content="{card_url}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Things Neuron Said">
<meta name="twitter:description" content="{og_desc}">
<meta name="twitter:image" content="https://neurontechnologies.ai/assets/brand/neuron-wordmark-on-light@2x.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--navy:#0052A0;--navy-dark:#003d7a;--t1:#0D0D14;--t2:#3A3A4A;--t3:#6B6B7E;--bg:#FAFAF8}
body{font-family:'IBM Plex Sans',system-ui,sans-serif;background:var(--bg);color:var(--t1);min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 1rem}
body::before{content:'';position:fixed;inset:0;pointer-events:none;z-index:0;background-image:linear-gradient(rgba(0,0,0,.018) 1px,transparent 1px),linear-gradient(90deg,rgba(0,0,0,.018) 1px,transparent 1px);background-size:48px 48px}
.page{width:100%;max-width:580px;display:flex;flex-direction:column;gap:1.75rem;position:relative;z-index:1}
.page-header{display:flex;align-items:center;justify-content:space-between;gap:1rem}
.wordmark img{height:22px;width:auto;display:block}
.eyebrow{font-size:.65rem;font-weight:400;letter-spacing:.16em;text-transform:uppercase;color:var(--t3)}
.chat-frame{background:#fff;border:1px solid rgba(0,0,0,.09);box-shadow:0 4px 32px rgba(0,0,0,.07),0 1px 4px rgba(0,0,0,.04);padding:1.5rem;display:flex;flex-direction:column;gap:1rem}
.chat-row-user{display:flex;flex-direction:row-reverse}
.chat-row-ai{display:flex;flex-direction:row;align-items:flex-end;gap:.625rem}
.bubble-user{background:#0052A0;color:#fff;border-radius:18px 18px 4px 18px;padding:11px 15px;max-width:78%;font-size:.875rem;line-height:1.55;word-break:break-word}
.bubble-ai{background:var(--bg);color:var(--t1);border:1px solid rgba(0,0,0,.07);border-radius:18px 18px 18px 4px;padding:11px 15px;max-width:88%;font-size:.875rem;font-weight:300;line-height:1.65;word-break:break-word;box-shadow:0 2px 6px rgba(0,0,0,.05)}
.bubble-ai p{margin:0}
.bubble-ai p+p{margin-top:.6rem}
.bubble-ai ul,.bubble-ai ol{margin:.5rem 0 .5rem 1.25rem;padding:0}
.bubble-ai li+li{margin-top:.25rem}
.bubble-ai strong{font-weight:600}
.bubble-ai em{font-style:italic}
.bubble-ai code{font-family:'IBM Plex Mono','Menlo',monospace;font-size:.8rem;background:rgba(0,0,0,.05);padding:1px 4px;border-radius:3px}
.bubble-ai pre{background:rgba(0,0,0,.05);padding:.75rem;border-radius:6px;overflow-x:auto;font-size:.8rem;margin:.5rem 0}
.bubble-ai pre code{background:none;padding:0}
.bubble-ai blockquote{border-left:3px solid rgba(0,82,160,.3);margin:.5rem 0;padding:.25rem 0 .25rem .75rem;color:var(--t2)}
.bubble-ai h1,.bubble-ai h2,.bubble-ai h3,.bubble-ai h4{font-weight:600;margin:.5rem 0 .25rem}
.bubble-ai h1{font-size:1.05rem}.bubble-ai h2{font-size:1rem}.bubble-ai h3{font-size:.95rem}.bubble-ai h4{font-size:.9rem}
.bubble-ai a{color:var(--navy);text-decoration:underline}
.ai-col{display:flex;flex-direction:column;gap:.25rem}
.ai-label{font-size:.6rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--navy)}
.avatar{width:26px;height:26px;border-radius:50%;flex-shrink:0;background:#fff;border:1px solid rgba(0,82,160,.15);display:flex;align-items:center;justify-content:center}
.avatar img{width:14px;height:14px;object-fit:contain}
.vote-row{display:flex;align-items:center;gap:.75rem}
.vote-label{font-size:.65rem;font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:var(--t3)}
.vote-btn{background:none;border:1px solid rgba(0,0,0,.12);cursor:pointer;padding:.3rem .6rem;font-size:.8rem;color:var(--t2);transition:all .15s;line-height:1}
.vote-btn:hover{border-color:var(--navy);color:var(--navy)}
.vote-btn.voted-up{background:var(--navy);color:#fff;border-color:var(--navy)}
.vote-btn.voted-down{background:#f0f0ec;color:var(--t3);border-color:rgba(0,0,0,.12)}
.vote-count{font-size:.8rem;font-weight:500;color:var(--t1);min-width:1.5rem;text-align:center}
.share-section{display:flex;flex-direction:column;gap:.625rem}
.share-label{font-size:.65rem;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--t3)}
.share-row{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
.share-btn{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:50%;border:none;text-decoration:none;cursor:pointer;transition:opacity .15s,transform .15s;flex-shrink:0}
.share-btn:hover{opacity:.85;transform:scale(1.08)}
.share-btn.copied{outline:2px solid var(--navy)}
.divider{height:1px;background:rgba(0,0,0,.07)}
.footer-row{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap}
.footer-note{font-size:.75rem;color:var(--t3)}
.cta-btn{display:inline-flex;align-items:center;gap:.5rem;background:var(--navy);color:#fff;text-decoration:none;font-size:.7rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;padding:.7rem 1.4rem;white-space:nowrap;transition:background .15s}
.cta-btn:hover{background:var(--navy-dark)}
@media(max-width:480px){.chat-frame{padding:1.25rem}.footer-row{flex-direction:column;align-items:flex-start}}
</style>
</head>
<body>
<div class="page">
<div class="page-header">
<a href="https://neurontechnologies.ai" class="wordmark">
<img src="/assets/brand/neuron-wordmark-on-light.png"
srcset="/assets/brand/neuron-wordmark-on-light@2x.png 2x"
alt="Neuron" height="22">
</a>
<span class="eyebrow">Things Neuron Said</span>
</div>
<div class="chat-frame">
<div class="chat-row-user">
<div class="bubble-user">{raw(q_html)}</div>
</div>
<div class="chat-row-ai">
<div class="avatar"><img src="/assets/neuron-icon.png" alt=""></div>
<div class="ai-col">
<span class="ai-label">Neuron</span>
<div class="bubble-ai">{raw(a_html)}</div>
</div>
</div>
</div>
let cfg_js: String = "window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.id=\"" + id + "\";window.NEURON_CFG.card_url=\"" + card_url + "\";"
<div class="vote-row">
<span class="vote-label">Helpful?</span>
<button class="vote-btn" id="vote-up">&#9650;</button>
<span class="vote-count" id="vote-score">0</span>
<button class="vote-btn" id="vote-down">&#9660;</button>
</div>
// Head
<div class="share-section">
<span class="share-label">Share</span>
<div class="share-row">
<a href="{x_href}" target="_blank" rel="noopener" class="share-btn" title="Post on X" style="background:#000">
<img src="/assets/social/x.svg" width="18" height="18" alt="X">
</a>
<a href="{li_href}" target="_blank" rel="noopener" class="share-btn" title="Share on LinkedIn" style="background:transparent">
<img src="/assets/social/linkedin.png" width="18" height="18" alt="LinkedIn">
</a>
<a href="{fb_href}" target="_blank" rel="noopener" class="share-btn" title="Share on Facebook" style="background:#1877F2">
<img src="/assets/social/facebook.svg" width="18" height="18" alt="Facebook">
</a>
<a href="{wa_href}" target="_blank" rel="noopener" class="share-btn" title="Send via WhatsApp" style="background:#25D366">
<img src="/assets/social/whatsapp.svg" width="18" height="18" alt="WhatsApp">
</a>
<button class="share-btn" id="copy-tiktok" onclick="copyForPlatform('tiktok', this)" title="Copy for TikTok" style="background:#010101;border:none">
<img src="/assets/social/tiktok.svg" width="18" height="18" alt="TikTok">
</button>
<button class="share-btn" id="copy-snapchat" onclick="copyForPlatform('snapchat', this)" title="Copy for Snapchat" style="background:#FFFC00;border:none">
<img src="/assets/social/snapchat.svg" width="18" height="18" alt="Snapchat">
</button>
</div>
</div>
let share_css: String = "*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}"
+ ":root{--navy:#0052A0;--navy-dark:#003d7a;--t1:#0D0D14;--t2:#3A3A4A;--t3:#6B6B7E;--bg:#FAFAF8}"
+ "body{font-family:'IBM Plex Sans',system-ui,sans-serif;background:var(--bg);color:var(--t1);min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 1rem}"
+ "body::before{content:'';position:fixed;inset:0;pointer-events:none;z-index:0;background-image:linear-gradient(rgba(0,0,0,.018) 1px,transparent 1px),linear-gradient(90deg,rgba(0,0,0,.018) 1px,transparent 1px);background-size:48px 48px}"
+ ".page{width:100%;max-width:580px;display:flex;flex-direction:column;gap:1.75rem;position:relative;z-index:1}"
+ ".page-header{display:flex;align-items:center;justify-content:space-between;gap:1rem}"
+ ".wordmark img{height:22px;width:auto;display:block}"
+ ".eyebrow{font-size:.65rem;font-weight:400;letter-spacing:.16em;text-transform:uppercase;color:var(--t3)}"
+ ".chat-frame{background:#fff;border:1px solid rgba(0,0,0,.09);box-shadow:0 4px 32px rgba(0,0,0,.07),0 1px 4px rgba(0,0,0,.04);padding:1.5rem;display:flex;flex-direction:column;gap:1rem}"
+ ".chat-row-user{display:flex;flex-direction:row-reverse}"
+ ".chat-row-ai{display:flex;flex-direction:row;align-items:flex-end;gap:.625rem}"
+ ".bubble-user{background:#0052A0;color:#fff;border-radius:18px 18px 4px 18px;padding:11px 15px;max-width:78%;font-size:.875rem;line-height:1.55;word-break:break-word}"
+ ".bubble-ai{background:var(--bg);color:var(--t1);border:1px solid rgba(0,0,0,.07);border-radius:18px 18px 18px 4px;padding:11px 15px;max-width:88%;font-size:.875rem;font-weight:300;line-height:1.65;word-break:break-word;box-shadow:0 2px 6px rgba(0,0,0,.05)}"
+ ".bubble-ai p{margin:0}"
+ ".bubble-ai p+p{margin-top:.6rem}"
+ ".bubble-ai ul,.bubble-ai ol{margin:.5rem 0 .5rem 1.25rem;padding:0}"
+ ".bubble-ai li+li{margin-top:.25rem}"
+ ".bubble-ai strong{font-weight:600}"
+ ".bubble-ai em{font-style:italic}"
+ ".bubble-ai code{font-family:'IBM Plex Mono','Menlo',monospace;font-size:.8rem;background:rgba(0,0,0,.05);padding:1px 4px;border-radius:3px}"
+ ".bubble-ai pre{background:rgba(0,0,0,.05);padding:.75rem;border-radius:6px;overflow-x:auto;font-size:.8rem;margin:.5rem 0}"
+ ".bubble-ai pre code{background:none;padding:0}"
+ ".bubble-ai blockquote{border-left:3px solid rgba(0,82,160,.3);margin:.5rem 0;padding:.25rem 0 .25rem .75rem;color:var(--t2)}"
+ ".bubble-ai h1,.bubble-ai h2,.bubble-ai h3,.bubble-ai h4{font-weight:600;margin:.5rem 0 .25rem}"
+ ".bubble-ai h1{font-size:1.05rem}.bubble-ai h2{font-size:1rem}.bubble-ai h3{font-size:.95rem}.bubble-ai h4{font-size:.9rem}"
+ ".bubble-ai a{color:var(--navy);text-decoration:underline}"
+ ".ai-col{display:flex;flex-direction:column;gap:.25rem}"
+ ".ai-label{font-size:.6rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--navy)}"
+ ".avatar{width:26px;height:26px;border-radius:50%;flex-shrink:0;background:#fff;border:1px solid rgba(0,82,160,.15);display:flex;align-items:center;justify-content:center}"
+ ".avatar img{width:14px;height:14px;object-fit:contain}"
+ ".vote-row{display:flex;align-items:center;gap:.75rem}"
+ ".vote-label{font-size:.65rem;font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:var(--t3)}"
+ ".vote-btn{background:none;border:1px solid rgba(0,0,0,.12);cursor:pointer;padding:.3rem .6rem;font-size:.8rem;color:var(--t2);transition:all .15s;line-height:1}"
+ ".vote-btn:hover{border-color:var(--navy);color:var(--navy)}"
+ ".vote-btn.voted-up{background:var(--navy);color:#fff;border-color:var(--navy)}"
+ ".vote-btn.voted-down{background:#f0f0ec;color:var(--t3);border-color:rgba(0,0,0,.12)}"
+ ".vote-count{font-size:.8rem;font-weight:500;color:var(--t1);min-width:1.5rem;text-align:center}"
+ ".share-section{display:flex;flex-direction:column;gap:.625rem}"
+ ".share-label{font-size:.65rem;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--t3)}"
+ ".share-row{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}"
+ ".share-btn{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:50%;border:none;text-decoration:none;cursor:pointer;transition:opacity .15s,transform .15s;flex-shrink:0}"
+ ".share-btn:hover{opacity:.85;transform:scale(1.08)}"
+ ".share-btn.copied{outline:2px solid var(--navy)}"
+ ".divider{height:1px;background:rgba(0,0,0,.07)}"
+ ".footer-row{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap}"
+ ".footer-note{font-size:.75rem;color:var(--t3)}"
+ ".cta-btn{display:inline-flex;align-items:center;gap:.5rem;background:var(--navy);color:#fff;text-decoration:none;font-size:.7rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;padding:.7rem 1.4rem;white-space:nowrap;transition:background .15s}"
+ ".cta-btn:hover{background:var(--navy-dark)}"
+ "@media(max-width:480px){.chat-frame{padding:1.25rem}.footer-row{flex-direction:column;align-items:flex-start}}"
<div class="divider"></div>
let head_html: String = el_meta_charset("UTF-8")
+ "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />"
+ el_title("Things Neuron Said")
+ "<meta property=\"og:title\" content=\"Things Neuron Said\" />"
+ "<meta property=\"og:description\" content=\"" + el_escape(og_desc) + "\" />"
+ "<meta property=\"og:image\" content=\"https://neurontechnologies.ai/assets/brand/neuron-wordmark-on-light@2x.png\" />"
+ "<meta property=\"og:type\" content=\"website\" />"
+ "<meta property=\"og:url\" content=\"" + el_escape(card_url) + "\" />"
+ "<meta name=\"twitter:card\" content=\"summary_large_image\" />"
+ "<meta name=\"twitter:title\" content=\"Things Neuron Said\" />"
+ "<meta name=\"twitter:description\" content=\"" + el_escape(og_desc) + "\" />"
+ "<meta name=\"twitter:image\" content=\"https://neurontechnologies.ai/assets/brand/neuron-wordmark-on-light@2x.png\" />"
+ "<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />"
+ "<link href=\"https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600&display=swap\" rel=\"stylesheet\" />"
+ "<style>" + share_css + "</style>"
<div class="footer-row">
<span class="footer-note">From a live conversation with Neuron.</span>
<a href="https://neurontechnologies.ai" class="cta-btn">Try Neuron &#8599;</a>
</div>
</div>
<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>
</html>
// Body
let wordmark_link: String = el_a(
"https://neurontechnologies.ai",
"class=\"wordmark\"",
el_img(
"/assets/brand/neuron-wordmark-on-light.png",
"Neuron",
"srcset=\"/assets/brand/neuron-wordmark-on-light@2x.png 2x\" height=\"22\""
)
)
let page_header: String = el_div(
"class=\"page-header\"",
wordmark_link + el_span("class=\"eyebrow\"", "Things Neuron Said")
)
let user_bubble: String = el_div("class=\"chat-row-user\"",
el_div("class=\"bubble-user\"", q_html)
)
let avatar: String = el_div("class=\"avatar\"",
el_img("/assets/neuron-icon.png", "", "")
)
let ai_col: String = el_div("class=\"ai-col\"",
el_span("class=\"ai-label\"", "Neuron")
+ el_div("class=\"bubble-ai\"", a_html)
)
let ai_bubble: String = el_div("class=\"chat-row-ai\"", avatar + ai_col)
let chat_frame: String = el_div("class=\"chat-frame\"", user_bubble + ai_bubble)
let vote_row: String = el_div(
"class=\"vote-row\"",
el_span("class=\"vote-label\"", "Helpful?")
+ "<button class=\"vote-btn\" id=\"vote-up\">&#9650;</button>"
+ el_span("class=\"vote-count\" id=\"vote-score\"", "0")
+ "<button class=\"vote-btn\" id=\"vote-down\">&#9660;</button>"
)
let share_row: String = el_div(
"class=\"share-row\"",
el_a(x_href, "target=\"_blank\" rel=\"noopener\" class=\"share-btn\" title=\"Post on X\" style=\"background:#000\"",
el_img("/assets/social/x.svg", "X", "width=\"18\" height=\"18\"")
)
+ el_a(li_href, "target=\"_blank\" rel=\"noopener\" class=\"share-btn\" title=\"Share on LinkedIn\" style=\"background:transparent\"",
el_img("/assets/social/linkedin.png", "LinkedIn", "width=\"18\" height=\"18\"")
)
+ el_a(fb_href, "target=\"_blank\" rel=\"noopener\" class=\"share-btn\" title=\"Share on Facebook\" style=\"background:#1877F2\"",
el_img("/assets/social/facebook.svg", "Facebook", "width=\"18\" height=\"18\"")
)
+ el_a(wa_href, "target=\"_blank\" rel=\"noopener\" class=\"share-btn\" title=\"Send via WhatsApp\" style=\"background:#25D366\"",
el_img("/assets/social/whatsapp.svg", "WhatsApp", "width=\"18\" height=\"18\"")
)
+ "<button class=\"share-btn\" id=\"copy-tiktok\" onclick=\"copyForPlatform('tiktok', this)\" title=\"Copy for TikTok\" style=\"background:#010101;border:none\">"
+ el_img("/assets/social/tiktok.svg", "TikTok", "width=\"18\" height=\"18\"")
+ "</button>"
+ "<button class=\"share-btn\" id=\"copy-snapchat\" onclick=\"copyForPlatform('snapchat', this)\" title=\"Copy for Snapchat\" style=\"background:#FFFC00;border:none\">"
+ el_img("/assets/social/snapchat.svg", "Snapchat", "width=\"18\" height=\"18\"")
+ "</button>"
)
let share_section: String = el_div(
"class=\"share-section\"",
el_span("class=\"share-label\"", "Share") + share_row
)
let footer_row: String = el_div(
"class=\"footer-row\"",
el_span("class=\"footer-note\"", "From a live conversation with Neuron.")
+ el_a("https://neurontechnologies.ai", "class=\"cta-btn\"", "Try Neuron &#8599;")
)
let page_div: String = el_div(
"class=\"page\"",
page_header
+ chat_frame
+ vote_row
+ share_section
+ el_div("class=\"divider\"", "")
+ footer_row
)
let body_html: String = page_div
+ el_script_inline(cfg_js)
+ el_script_src("/js/main.js", true)
el_html_doc("en", head_html, body_html)
}
// Static asset serving
@@ -1643,20 +1712,22 @@ fn handle_request_inner(method: String, path: String, body: String) -> String {
if str_starts_with(path, "/marketplace/success") {
let badge_html: String = founding_badge(get_sold())
let badge_css: String = founding_badge_css()
return page_open() + badge_css + "
<div style=\"min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:4rem 2rem\">
<p class=\"label\" style=\"margin-bottom:1.5rem\">You&#39;re in.</p>
<h1 class=\"display-lg\" style=\"margin-bottom:1.25rem\">Welcome to Neuron.</h1>
<p style=\"font-family:var(--body);font-weight:300;font-size:1.1rem;color:var(--t2);max-width:28rem;line-height:1.7;margin-bottom:3rem\">
Your license is being provisioned. Check your email - your license key and download instructions will be there in the next few minutes.
</p>
<div style=\"margin-bottom:3rem\">" + badge_html + "</div>
<div style=\"display:flex;gap:1rem;flex-wrap:wrap;justify-content:center\">
<a href=\"/account\" class=\"btn-primary\">View your account &#8594;</a>
<a href=\"/\" class=\"btn-ghost\">Back to home</a>
</div>
</div>
" + page_close()
let success_body: String = el_div(
"style=\"min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:4rem 2rem\"",
el_p("class=\"label\" style=\"margin-bottom:1.5rem\"", "You&#39;re in.")
+ el_h1("class=\"display-lg\" style=\"margin-bottom:1.25rem\"", "Welcome to Neuron.")
+ el_p(
"style=\"font-family:var(--body);font-weight:300;font-size:1.1rem;color:var(--t2);max-width:28rem;line-height:1.7;margin-bottom:3rem\"",
"Your license is being provisioned. Check your email - your license key and download instructions will be there in the next few minutes."
)
+ el_div("style=\"margin-bottom:3rem\"", badge_html)
+ el_div(
"style=\"display:flex;gap:1rem;flex-wrap:wrap;justify-content:center\"",
el_a("/account", "class=\"btn-primary\"", "View your account &#8594;")
+ el_a("/", "class=\"btn-ghost\"", "Back to home")
)
)
return page_open() + badge_css + success_body + page_close()
}
// Account dashboard