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:
+214
-143
@@ -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">▲</button>
|
||||
<span class="vote-count" id="vote-score">0</span>
|
||||
<button class="vote-btn" id="vote-down">▼</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 ↗</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\">▲</button>"
|
||||
+ el_span("class=\"vote-count\" id=\"vote-score\"", "0")
|
||||
+ "<button class=\"vote-btn\" id=\"vote-down\">▼</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 ↗")
|
||||
)
|
||||
|
||||
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'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 →</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'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 →")
|
||||
+ el_a("/", "class=\"btn-ghost\"", "Back to home")
|
||||
)
|
||||
)
|
||||
return page_open() + badge_css + success_body + page_close()
|
||||
}
|
||||
|
||||
// ── Account dashboard ─────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user