refactor(welcome): integrate HTMX 4 for dynamic onboarding flows

This commit is contained in:
2026-01-05 13:28:41 -05:00
parent 62d19e61d1
commit 88d521f13c
5 changed files with 190 additions and 104 deletions

View File

@@ -7,9 +7,7 @@ import (
"nebula/views" "nebula/views"
) )
// RegisterRoutes sets up all HTTP routes for the application
func RegisterRoutes(mux *http.ServeMux) { func RegisterRoutes(mux *http.ServeMux) {
// Welcome page routes
mux.HandleFunc("GET /", handleWelcome) mux.HandleFunc("GET /", handleWelcome)
mux.HandleFunc("GET /welcome", handleWelcome) mux.HandleFunc("GET /welcome", handleWelcome)
mux.HandleFunc("GET /welcome/step/{step}", handleWelcomeStep) mux.HandleFunc("GET /welcome/step/{step}", handleWelcomeStep)
@@ -30,8 +28,8 @@ func handleWelcomeStep(w http.ResponseWriter, r *http.Request) {
// Check if this is an HTMX request // Check if this is an HTMX request
if r.Header.Get("HX-Request") == "true" { if r.Header.Get("HX-Request") == "true" {
// Return only the step content fragment // Return step content with OOB stepper update (HTMX 4 pattern)
views.WelcomeStepContent(step).Render(r.Context(), w) views.WelcomeStepWithStepper(step).Render(r.Context(), w)
return return
} }

View File

@@ -8,8 +8,8 @@ templ Base(title string) {
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>{ title } - Sonr Motr Wallet</title> <title>{ title } - Sonr Motr Wallet</title>
<script src="https://cdn.sonr.org/wa/autoloader.js"></script> <script src="https://kit.webawesome.com/47c7425b971f443c.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-alpha5/dist/htmx.min.js"></script>
<style> <style>
:root { :root {
--wa-color-primary: #17c2ff; --wa-color-primary: #17c2ff;

View File

@@ -43,7 +43,7 @@ func Base(title string) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " - Sonr Motr Wallet</title><script src=\"https://cdn.sonr.org/wa/autoloader.js\"></script><script src=\"https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js\"></script><style>\n\t\t\t\t:root {\n\t\t\t\t\t--wa-color-primary: #17c2ff;\n\t\t\t\t}\n\t\t\t\thtml, body {\n\t\t\t\t\tmin-height: 100%;\n\t\t\t\t\tpadding: 0;\n\t\t\t\t\tmargin: 0;\n\t\t\t\t}\n\t\t\t</style></head><body>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " - Sonr Motr Wallet</title><script src=\"https://kit.webawesome.com/47c7425b971f443c.js\" crossorigin=\"anonymous\"></script><script src=\"https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-alpha5/dist/htmx.min.js\"></script><style>\n\t\t\t\t:root {\n\t\t\t\t\t--wa-color-primary: #17c2ff;\n\t\t\t\t}\n\t\t\t\thtml, body {\n\t\t\t\t\tmin-height: 100%;\n\t\t\t\t\tpadding: 0;\n\t\t\t\t\tmargin: 0;\n\t\t\t\t}\n\t\t\t</style></head><body>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View File

@@ -10,12 +10,15 @@ var welcomeSteps = []string{"Welcome", "Learn", "Get Started"}
// WelcomePage renders the full onboarding page with the specified step // WelcomePage renders the full onboarding page with the specified step
templ WelcomePage(currentStep int) { templ WelcomePage(currentStep int) {
@layouts.CenteredCard("Welcome") { @layouts.CenteredCard("Welcome") {
<div slot="header"> <div slot="header" id="stepper-container" hx-swap-oob:inherited="true">
@components.OnboardingStepper(currentStep, welcomeSteps) @components.OnboardingStepper(currentStep, welcomeSteps)
</div> </div>
<div id="step-content"> <div id="step-content" class="step-content">
@WelcomeStepContent(currentStep) @WelcomeStepContent(currentStep)
</div> </div>
<div id="htmx-indicator" class="htmx-indicator">
<wa-spinner></wa-spinner>
</div>
<footer slot="footer"> <footer slot="footer">
@WelcomeFooter() @WelcomeFooter()
</footer> </footer>
@@ -34,6 +37,14 @@ templ WelcomeStepContent(step int) {
} }
} }
// WelcomeStepWithStepper renders step content with OOB stepper update for HTMX 4
templ WelcomeStepWithStepper(step int) {
@WelcomeStepContent(step)
<div id="stepper-container" hx-swap-oob="innerHTML">
@components.OnboardingStepper(step, welcomeSteps)
</div>
}
// WelcomeStep1 - Initial welcome screen with features // WelcomeStep1 - Initial welcome screen with features
templ WelcomeStep1() { templ WelcomeStep1() {
@welcomeStyles() @welcomeStyles()
@@ -59,7 +70,9 @@ templ WelcomeStep1() {
style="width: 100%;" style="width: 100%;"
hx-get="/welcome/step/2" hx-get="/welcome/step/2"
hx-target="#step-content" hx-target="#step-content"
hx-swap="innerHTML" hx-swap="innerHTML transition:true"
hx-indicator="#htmx-indicator"
hx-on:htmx:after:swap="this.closest('.step-content')?.scrollTo(0,0)"
> >
Learn More Learn More
<wa-icon slot="end" name="arrow-right"></wa-icon> <wa-icon slot="end" name="arrow-right"></wa-icon>
@@ -90,7 +103,8 @@ templ WelcomeStep2() {
appearance="outlined" appearance="outlined"
hx-get="/welcome/step/1" hx-get="/welcome/step/1"
hx-target="#step-content" hx-target="#step-content"
hx-swap="innerHTML" hx-swap="innerHTML transition:true"
hx-indicator="#htmx-indicator"
> >
<wa-icon slot="start" name="arrow-left"></wa-icon> <wa-icon slot="start" name="arrow-left"></wa-icon>
Back Back
@@ -99,7 +113,8 @@ templ WelcomeStep2() {
variant="brand" variant="brand"
hx-get="/welcome/step/3" hx-get="/welcome/step/3"
hx-target="#step-content" hx-target="#step-content"
hx-swap="innerHTML" hx-swap="innerHTML transition:true"
hx-indicator="#htmx-indicator"
> >
Get Started Get Started
<wa-icon slot="end" name="arrow-right"></wa-icon> <wa-icon slot="end" name="arrow-right"></wa-icon>
@@ -146,7 +161,8 @@ templ WelcomeStep3() {
style="width: 100%;" style="width: 100%;"
hx-get="/welcome/qr-scanner" hx-get="/welcome/qr-scanner"
hx-target="#step-content" hx-target="#step-content"
hx-swap="innerHTML" hx-swap="innerHTML transition:true"
hx-indicator="#htmx-indicator"
> >
<wa-icon slot="start" name="qrcode"></wa-icon> <wa-icon slot="start" name="qrcode"></wa-icon>
Scan QR Code Scan QR Code
@@ -163,7 +179,8 @@ templ WelcomeStep3() {
style="width: 100%;" style="width: 100%;"
hx-get="/welcome/step/2" hx-get="/welcome/step/2"
hx-target="#step-content" hx-target="#step-content"
hx-swap="innerHTML" hx-swap="innerHTML transition:true"
hx-indicator="#htmx-indicator"
> >
<wa-icon slot="start" name="arrow-left"></wa-icon> <wa-icon slot="start" name="arrow-left"></wa-icon>
Back to How It Works Back to How It Works
@@ -222,6 +239,35 @@ templ InfoCallout(icon string, title string, description string) {
// welcomeStyles contains the CSS specific to the welcome page // welcomeStyles contains the CSS specific to the welcome page
templ welcomeStyles() { templ welcomeStyles() {
<style> <style>
/* HTMX 4 indicator and transition styles */
.htmx-indicator {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 100;
}
.htmx-request .htmx-indicator {
display: flex;
}
.htmx-request.step-content {
opacity: 0.5;
pointer-events: none;
transition: opacity 0.2s ease;
}
/* View transition support for HTMX 4 */
@view-transition {
navigation: auto;
}
::view-transition-old(step-content),
::view-transition-new(step-content) {
animation-duration: 0.25s;
}
.step-content {
view-transition-name: step-content;
position: relative;
}
.hero-icon { .hero-icon {
width: 80px; width: 80px;
height: 80px; height: 80px;

View File

@@ -49,7 +49,7 @@ func WelcomePage(currentStep int) templ.Component {
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div slot=\"header\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div slot=\"header\" id=\"stepper-container\" hx-swap-oob:inherited=\"true\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -57,7 +57,7 @@ func WelcomePage(currentStep int) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</div><div id=\"step-content\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</div><div id=\"step-content\" class=\"step-content\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -65,7 +65,7 @@ func WelcomePage(currentStep int) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div><footer slot=\"footer\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div><div id=\"htmx-indicator\" class=\"htmx-indicator\"><wa-spinner></wa-spinner></div><footer slot=\"footer\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -130,8 +130,8 @@ func WelcomeStepContent(step int) templ.Component {
}) })
} }
// WelcomeStep1 - Initial welcome screen with features // WelcomeStepWithStepper renders step content with OOB stepper update for HTMX 4
func WelcomeStep1() templ.Component { func WelcomeStepWithStepper(step int) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -152,27 +152,19 @@ func WelcomeStep1() templ.Component {
templ_7745c5c3_Var4 = templ.NopComponent templ_7745c5c3_Var4 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = welcomeStyles().Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = WelcomeStepContent(step).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"wa-stack wa-gap-l\"><div class=\"hero-icon\"><wa-icon name=\"wallet\" family=\"duotone\"></wa-icon></div><div class=\"wa-stack wa-gap-xs\" style=\"text-align: center;\"><h1 class=\"wa-heading-xl\">Welcome to Sonr</h1><p class=\"wa-caption-m\" style=\"color: var(--wa-color-neutral-600);\">Your self-sovereign identity wallet powered by WebAssembly</p></div><wa-divider></wa-divider><div class=\"wa-stack wa-gap-s\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div id=\"stepper-container\" hx-swap-oob=\"innerHTML\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = FeatureItem("shield-check", "security", "Passwordless Security", "Authenticate with biometrics or hardware keys - no passwords to remember or steal").Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = components.OnboardingStepper(step, welcomeSteps).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = FeatureItem("bolt", "speed", "WASM-Powered", "Wallet runs entirely in your browser - fast, secure, and always available").Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = FeatureItem("user-shield", "privacy", "You Own Your Data", "Self-sovereign identity means your keys never leave your device").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div><wa-button variant=\"brand\" size=\"large\" style=\"width: 100%;\" hx-get=\"/welcome/step/2\" hx-target=\"#step-content\" hx-swap=\"innerHTML\">Learn More <wa-icon slot=\"end\" name=\"arrow-right\"></wa-icon></wa-button></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -180,8 +172,8 @@ func WelcomeStep1() templ.Component {
}) })
} }
// WelcomeStep2 - How Motr Works explanation // WelcomeStep1 - Initial welcome screen with features
func WelcomeStep2() templ.Component { func WelcomeStep1() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -206,27 +198,23 @@ func WelcomeStep2() templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"wa-stack wa-gap-l\"><div class=\"wa-stack wa-gap-xs\" style=\"text-align: center;\"><h2 class=\"wa-heading-l\">How Motr Works</h2><p class=\"wa-caption-m\" style=\"color: var(--wa-color-neutral-600);\">A next-generation wallet built on WebAuthn and WASM</p></div><wa-divider></wa-divider><div class=\"wa-stack wa-gap-m\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"wa-stack wa-gap-l\"><div class=\"hero-icon\"><wa-icon name=\"wallet\" family=\"duotone\"></wa-icon></div><div class=\"wa-stack wa-gap-xs\" style=\"text-align: center;\"><h1 class=\"wa-heading-xl\">Welcome to Sonr</h1><p class=\"wa-caption-m\" style=\"color: var(--wa-color-neutral-600);\">Your self-sovereign identity wallet powered by WebAssembly</p></div><wa-divider></wa-divider><div class=\"wa-stack wa-gap-s\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = InfoCallout("microchip", "WebAssembly Runtime", "Your wallet logic runs as compiled Go code in a secure WASM sandbox, directly in your browser.").Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = FeatureItem("shield-check", "security", "Passwordless Security", "Authenticate with biometrics or hardware keys - no passwords to remember or steal").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = InfoCallout("fingerprint", "Passkey Authentication", "Use Face ID, Touch ID, or hardware security keys. Your biometrics stay on your device.").Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = FeatureItem("bolt", "speed", "WASM-Powered", "Wallet runs entirely in your browser - fast, secure, and always available").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = InfoCallout("network-wired", "Decentralized Identity", "Connect to any app with OpenID Connect - you control what data to share.").Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = FeatureItem("user-shield", "privacy", "You Own Your Data", "Self-sovereign identity means your keys never leave your device").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = InfoCallout("key", "Multi-Chain Support", "One wallet for Sonr, Ethereum, Cosmos, Bitcoin and more via IBC.").Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div><wa-button variant=\"brand\" size=\"large\" style=\"width: 100%;\" hx-get=\"/welcome/step/2\" hx-target=\"#step-content\" hx-swap=\"innerHTML transition:true\" hx-indicator=\"#htmx-indicator\" hx-on:htmx:after:swap=\"this.closest('.step-content')?.scrollTo(0,0)\">Learn More <wa-icon slot=\"end\" name=\"arrow-right\"></wa-icon></wa-button></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div><div class=\"wa-cluster wa-gap-s\" style=\"justify-content: space-between;\"><wa-button variant=\"neutral\" appearance=\"outlined\" hx-get=\"/welcome/step/1\" hx-target=\"#step-content\" hx-swap=\"innerHTML\"><wa-icon slot=\"start\" name=\"arrow-left\"></wa-icon> Back</wa-button> <wa-button variant=\"brand\" hx-get=\"/welcome/step/3\" hx-target=\"#step-content\" hx-swap=\"innerHTML\">Get Started <wa-icon slot=\"end\" name=\"arrow-right\"></wa-icon></wa-button></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -234,8 +222,8 @@ func WelcomeStep2() templ.Component {
}) })
} }
// WelcomeStep3 - Action selection (Create/Sign In) // WelcomeStep2 - How Motr Works explanation
func WelcomeStep3() templ.Component { func WelcomeStep2() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -260,7 +248,27 @@ func WelcomeStep3() templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div class=\"wa-stack wa-gap-l\"><div class=\"wa-stack wa-gap-xs\" style=\"text-align: center;\"><h2 class=\"wa-heading-l\">Ready to Begin?</h2><p class=\"wa-caption-m\" style=\"color: var(--wa-color-neutral-600);\">Create a new wallet or sign in to an existing one</p></div><wa-divider></wa-divider><div class=\"wa-grid\" style=\"--min-column-size: 180px; gap: var(--wa-space-m);\"><a href=\"/register\" class=\"action-card\"><wa-icon name=\"user-plus\" family=\"duotone\"></wa-icon><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-m\">Create Wallet</span> <span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-500);\">New to Sonr? Set up your wallet in minutes</span></div></a> <a href=\"/login\" class=\"action-card\"><wa-icon name=\"right-to-bracket\" family=\"duotone\"></wa-icon><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-m\">Sign In</span> <span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-500);\">Access your existing wallet</span></div></a></div><wa-divider>or</wa-divider> <wa-button variant=\"neutral\" appearance=\"outlined\" style=\"width: 100%;\" hx-get=\"/welcome/qr-scanner\" hx-target=\"#step-content\" hx-swap=\"innerHTML\"><wa-icon slot=\"start\" name=\"qrcode\"></wa-icon> Scan QR Code</wa-button> <wa-callout variant=\"brand\" appearance=\"filled\"><wa-icon slot=\"icon\" name=\"circle-info\"></wa-icon> <span class=\"wa-caption-s\">Already have a passkey registered on another device? Use QR code to sync your wallet.</span></wa-callout> <wa-button variant=\"neutral\" appearance=\"plain\" style=\"width: 100%;\" hx-get=\"/welcome/step/2\" hx-target=\"#step-content\" hx-swap=\"innerHTML\"><wa-icon slot=\"start\" name=\"arrow-left\"></wa-icon> Back to How It Works</wa-button></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div class=\"wa-stack wa-gap-l\"><div class=\"wa-stack wa-gap-xs\" style=\"text-align: center;\"><h2 class=\"wa-heading-l\">How Motr Works</h2><p class=\"wa-caption-m\" style=\"color: var(--wa-color-neutral-600);\">A next-generation wallet built on WebAuthn and WASM</p></div><wa-divider></wa-divider><div class=\"wa-stack wa-gap-m\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = InfoCallout("microchip", "WebAssembly Runtime", "Your wallet logic runs as compiled Go code in a secure WASM sandbox, directly in your browser.").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = InfoCallout("fingerprint", "Passkey Authentication", "Use Face ID, Touch ID, or hardware security keys. Your biometrics stay on your device.").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = InfoCallout("network-wired", "Decentralized Identity", "Connect to any app with OpenID Connect - you control what data to share.").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = InfoCallout("key", "Multi-Chain Support", "One wallet for Sonr, Ethereum, Cosmos, Bitcoin and more via IBC.").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div><div class=\"wa-cluster wa-gap-s\" style=\"justify-content: space-between;\"><wa-button variant=\"neutral\" appearance=\"outlined\" hx-get=\"/welcome/step/1\" hx-target=\"#step-content\" hx-swap=\"innerHTML transition:true\" hx-indicator=\"#htmx-indicator\"><wa-icon slot=\"start\" name=\"arrow-left\"></wa-icon> Back</wa-button> <wa-button variant=\"brand\" hx-get=\"/welcome/step/3\" hx-target=\"#step-content\" hx-swap=\"innerHTML transition:true\" hx-indicator=\"#htmx-indicator\">Get Started <wa-icon slot=\"end\" name=\"arrow-right\"></wa-icon></wa-button></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -268,8 +276,8 @@ func WelcomeStep3() templ.Component {
}) })
} }
// WelcomeFooter renders the network status and links footer // WelcomeStep3 - Action selection (Create/Sign In)
func WelcomeFooter() templ.Component { func WelcomeStep3() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -294,7 +302,41 @@ func WelcomeFooter() templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"wa-stack wa-gap-m wa-align-items-center\"><div class=\"network-status\"><div class=\"status-dot\"></div><span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-600);\">Sonr Network: <strong style=\"color: var(--wa-color-success);\">Operational</strong></span></div><div class=\"wa-cluster wa-justify-content-center wa-gap-m\"><wa-button appearance=\"plain\" size=\"small\" onclick=\"window.open('https://sonr.io', '_blank')\">Learn about Sonr</wa-button> <span style=\"color: var(--wa-color-neutral-300);\">|</span> <wa-button appearance=\"plain\" size=\"small\" onclick=\"window.open('https://docs.sonr.io', '_blank')\">Documentation</wa-button></div></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"wa-stack wa-gap-l\"><div class=\"wa-stack wa-gap-xs\" style=\"text-align: center;\"><h2 class=\"wa-heading-l\">Ready to Begin?</h2><p class=\"wa-caption-m\" style=\"color: var(--wa-color-neutral-600);\">Create a new wallet or sign in to an existing one</p></div><wa-divider></wa-divider><div class=\"wa-grid\" style=\"--min-column-size: 180px; gap: var(--wa-space-m);\"><a href=\"/register\" class=\"action-card\"><wa-icon name=\"user-plus\" family=\"duotone\"></wa-icon><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-m\">Create Wallet</span> <span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-500);\">New to Sonr? Set up your wallet in minutes</span></div></a> <a href=\"/login\" class=\"action-card\"><wa-icon name=\"right-to-bracket\" family=\"duotone\"></wa-icon><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-m\">Sign In</span> <span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-500);\">Access your existing wallet</span></div></a></div><wa-divider>or</wa-divider> <wa-button variant=\"neutral\" appearance=\"outlined\" style=\"width: 100%;\" hx-get=\"/welcome/qr-scanner\" hx-target=\"#step-content\" hx-swap=\"innerHTML transition:true\" hx-indicator=\"#htmx-indicator\"><wa-icon slot=\"start\" name=\"qrcode\"></wa-icon> Scan QR Code</wa-button> <wa-callout variant=\"brand\" appearance=\"filled\"><wa-icon slot=\"icon\" name=\"circle-info\"></wa-icon> <span class=\"wa-caption-s\">Already have a passkey registered on another device? Use QR code to sync your wallet.</span></wa-callout> <wa-button variant=\"neutral\" appearance=\"plain\" style=\"width: 100%;\" hx-get=\"/welcome/step/2\" hx-target=\"#step-content\" hx-swap=\"innerHTML transition:true\" hx-indicator=\"#htmx-indicator\"><wa-icon slot=\"start\" name=\"arrow-left\"></wa-icon> Back to How It Works</wa-button></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// WelcomeFooter renders the network status and links footer
func WelcomeFooter() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
if templ_7745c5c3_Var8 == nil {
templ_7745c5c3_Var8 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = welcomeStyles().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"wa-stack wa-gap-m wa-align-items-center\"><div class=\"network-status\"><div class=\"status-dot\"></div><span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-600);\">Sonr Network: <strong style=\"color: var(--wa-color-success);\">Operational</strong></span></div><div class=\"wa-cluster wa-justify-content-center wa-gap-m\"><wa-button appearance=\"plain\" size=\"small\" onclick=\"window.open('https://sonr.io', '_blank')\">Learn about Sonr</wa-button> <span style=\"color: var(--wa-color-neutral-300);\">|</span> <wa-button appearance=\"plain\" size=\"small\" onclick=\"window.open('https://docs.sonr.io', '_blank')\">Documentation</wa-button></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -319,73 +361,73 @@ func FeatureItem(icon string, variant string, title string, description string)
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var8 := templ.GetChildren(ctx) templ_7745c5c3_Var9 := templ.GetChildren(ctx)
if templ_7745c5c3_Var8 == nil { if templ_7745c5c3_Var9 == nil {
templ_7745c5c3_Var8 = templ.NopComponent templ_7745c5c3_Var9 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"feature-item\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<div class=\"feature-item\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var9 = []any{"feature-icon", variant} var templ_7745c5c3_Var10 = []any{"feature-icon", variant}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...) templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var9).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\"><wa-icon name=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var11 string var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(icon) templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var10).String())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 200, Col: 23} return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 1, Col: 0}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"></wa-icon></div><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-xs\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\"><wa-icon name=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var12 string var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(title) templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(icon)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 203, Col: 38} return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 217, Col: 23}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</span> <span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-500);\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"></wa-icon></div><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-xs\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var13 string var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(description) templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 205, Col: 17} return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 220, Col: 38}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span></div></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</span> <span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-500);\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 222, Col: 17}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</span></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -410,51 +452,51 @@ func InfoCallout(icon string, title string, description string) templ.Component
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var14 := templ.GetChildren(ctx) templ_7745c5c3_Var15 := templ.GetChildren(ctx)
if templ_7745c5c3_Var14 == nil { if templ_7745c5c3_Var15 == nil {
templ_7745c5c3_Var14 = templ.NopComponent templ_7745c5c3_Var15 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<wa-callout variant=\"neutral\"><wa-icon slot=\"icon\" name=\"") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<wa-callout variant=\"neutral\"><wa-icon slot=\"icon\" name=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(icon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 214, Col: 34}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\"></wa-icon><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-xs\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var16 string var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(title) templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(icon)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 216, Col: 38} return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 231, Col: 34}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</span> <span class=\"wa-caption-s\">") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\"></wa-icon><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-xs\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var17 string var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(description) templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 217, Col: 43} return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 233, Col: 38}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</span></div></wa-callout>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</span> <span class=\"wa-caption-s\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 234, Col: 43}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</span></div></wa-callout>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -479,12 +521,12 @@ func welcomeStyles() templ.Component {
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var18 := templ.GetChildren(ctx) templ_7745c5c3_Var19 := templ.GetChildren(ctx)
if templ_7745c5c3_Var18 == nil { if templ_7745c5c3_Var19 == nil {
templ_7745c5c3_Var18 = templ.NopComponent templ_7745c5c3_Var19 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<style>\n\t\t.hero-icon {\n\t\t\twidth: 80px;\n\t\t\theight: 80px;\n\t\t\tborder-radius: var(--wa-radius-l);\n\t\t\tbackground: linear-gradient(135deg, var(--wa-color-primary), #0090ff);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tmargin: 0 auto var(--wa-space-l);\n\t\t\tbox-shadow: 0 8px 32px rgba(23, 194, 255, 0.3);\n\t\t}\n\t\t.hero-icon wa-icon {\n\t\t\tfont-size: 40px;\n\t\t\tcolor: white;\n\t\t}\n\t\t.feature-item {\n\t\t\tdisplay: flex;\n\t\t\talign-items: flex-start;\n\t\t\tgap: var(--wa-space-m);\n\t\t\tpadding: var(--wa-space-m);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-m);\n\t\t}\n\t\t.feature-icon {\n\t\t\twidth: 40px;\n\t\t\theight: 40px;\n\t\t\tborder-radius: var(--wa-radius-s);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tflex-shrink: 0;\n\t\t}\n\t\t.feature-icon.security {\n\t\t\tbackground: var(--wa-color-success-subtle);\n\t\t\tcolor: var(--wa-color-success);\n\t\t}\n\t\t.feature-icon.speed {\n\t\t\tbackground: var(--wa-color-primary-subtle);\n\t\t\tcolor: var(--wa-color-primary);\n\t\t}\n\t\t.feature-icon.privacy {\n\t\t\tbackground: var(--wa-color-warning-subtle);\n\t\t\tcolor: var(--wa-color-warning);\n\t\t}\n\t\t.action-card {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tgap: var(--wa-space-m);\n\t\t\tpadding: var(--wa-space-xl);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-l);\n\t\t\tborder: 2px solid transparent;\n\t\t\tcursor: pointer;\n\t\t\ttransition: all 0.2s;\n\t\t\ttext-align: center;\n\t\t\ttext-decoration: none;\n\t\t\tcolor: inherit;\n\t\t}\n\t\t.action-card:hover {\n\t\t\tborder-color: var(--wa-color-primary);\n\t\t\tbackground: var(--wa-color-primary-subtle);\n\t\t}\n\t\t.action-card wa-icon {\n\t\t\tfont-size: 2.5rem;\n\t\t\tcolor: var(--wa-color-primary);\n\t\t}\n\t\t.network-status {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tgap: var(--wa-space-xs);\n\t\t}\n\t\t.status-dot {\n\t\t\twidth: 8px;\n\t\t\theight: 8px;\n\t\t\tborder-radius: 50%;\n\t\t\tbackground: var(--wa-color-success);\n\t\t\tanimation: pulse 2s infinite;\n\t\t}\n\t\t@keyframes pulse {\n\t\t\t0%, 100% { opacity: 1; }\n\t\t\t50% { opacity: 0.5; }\n\t\t}\n\t\t/* Viewport constraints for popup/webview */\n\t\t@media (max-width: 400px) {\n\t\t\t.hero-icon {\n\t\t\t\twidth: 64px;\n\t\t\t\theight: 64px;\n\t\t\t}\n\t\t\t.hero-icon wa-icon {\n\t\t\t\tfont-size: 32px;\n\t\t\t}\n\t\t\t.feature-item {\n\t\t\t\tpadding: var(--wa-space-s);\n\t\t\t}\n\t\t\t.feature-icon {\n\t\t\t\twidth: 32px;\n\t\t\t\theight: 32px;\n\t\t\t}\n\t\t\t.action-card {\n\t\t\t\tpadding: var(--wa-space-m);\n\t\t\t}\n\t\t\t.action-card wa-icon {\n\t\t\t\tfont-size: 2rem;\n\t\t\t}\n\t\t}\n\t\t@media (max-height: 600px) {\n\t\t\t.hero-icon {\n\t\t\t\twidth: 56px;\n\t\t\t\theight: 56px;\n\t\t\t\tmargin-bottom: var(--wa-space-m);\n\t\t\t}\n\t\t\t.hero-icon wa-icon {\n\t\t\t\tfont-size: 28px;\n\t\t\t}\n\t\t}\n\t</style>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<style>\n\t\t/* HTMX 4 indicator and transition styles */\n\t\t.htmx-indicator {\n\t\t\tdisplay: none;\n\t\t\tposition: absolute;\n\t\t\ttop: 50%;\n\t\t\tleft: 50%;\n\t\t\ttransform: translate(-50%, -50%);\n\t\t\tz-index: 100;\n\t\t}\n\t\t.htmx-request .htmx-indicator {\n\t\t\tdisplay: flex;\n\t\t}\n\t\t.htmx-request.step-content {\n\t\t\topacity: 0.5;\n\t\t\tpointer-events: none;\n\t\t\ttransition: opacity 0.2s ease;\n\t\t}\n\t\t/* View transition support for HTMX 4 */\n\t\t@view-transition {\n\t\t\tnavigation: auto;\n\t\t}\n\t\t::view-transition-old(step-content),\n\t\t::view-transition-new(step-content) {\n\t\t\tanimation-duration: 0.25s;\n\t\t}\n\t\t.step-content {\n\t\t\tview-transition-name: step-content;\n\t\t\tposition: relative;\n\t\t}\n\t\t.hero-icon {\n\t\t\twidth: 80px;\n\t\t\theight: 80px;\n\t\t\tborder-radius: var(--wa-radius-l);\n\t\t\tbackground: linear-gradient(135deg, var(--wa-color-primary), #0090ff);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tmargin: 0 auto var(--wa-space-l);\n\t\t\tbox-shadow: 0 8px 32px rgba(23, 194, 255, 0.3);\n\t\t}\n\t\t.hero-icon wa-icon {\n\t\t\tfont-size: 40px;\n\t\t\tcolor: white;\n\t\t}\n\t\t.feature-item {\n\t\t\tdisplay: flex;\n\t\t\talign-items: flex-start;\n\t\t\tgap: var(--wa-space-m);\n\t\t\tpadding: var(--wa-space-m);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-m);\n\t\t}\n\t\t.feature-icon {\n\t\t\twidth: 40px;\n\t\t\theight: 40px;\n\t\t\tborder-radius: var(--wa-radius-s);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tflex-shrink: 0;\n\t\t}\n\t\t.feature-icon.security {\n\t\t\tbackground: var(--wa-color-success-subtle);\n\t\t\tcolor: var(--wa-color-success);\n\t\t}\n\t\t.feature-icon.speed {\n\t\t\tbackground: var(--wa-color-primary-subtle);\n\t\t\tcolor: var(--wa-color-primary);\n\t\t}\n\t\t.feature-icon.privacy {\n\t\t\tbackground: var(--wa-color-warning-subtle);\n\t\t\tcolor: var(--wa-color-warning);\n\t\t}\n\t\t.action-card {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tgap: var(--wa-space-m);\n\t\t\tpadding: var(--wa-space-xl);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-l);\n\t\t\tborder: 2px solid transparent;\n\t\t\tcursor: pointer;\n\t\t\ttransition: all 0.2s;\n\t\t\ttext-align: center;\n\t\t\ttext-decoration: none;\n\t\t\tcolor: inherit;\n\t\t}\n\t\t.action-card:hover {\n\t\t\tborder-color: var(--wa-color-primary);\n\t\t\tbackground: var(--wa-color-primary-subtle);\n\t\t}\n\t\t.action-card wa-icon {\n\t\t\tfont-size: 2.5rem;\n\t\t\tcolor: var(--wa-color-primary);\n\t\t}\n\t\t.network-status {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tgap: var(--wa-space-xs);\n\t\t}\n\t\t.status-dot {\n\t\t\twidth: 8px;\n\t\t\theight: 8px;\n\t\t\tborder-radius: 50%;\n\t\t\tbackground: var(--wa-color-success);\n\t\t\tanimation: pulse 2s infinite;\n\t\t}\n\t\t@keyframes pulse {\n\t\t\t0%, 100% { opacity: 1; }\n\t\t\t50% { opacity: 0.5; }\n\t\t}\n\t\t/* Viewport constraints for popup/webview */\n\t\t@media (max-width: 400px) {\n\t\t\t.hero-icon {\n\t\t\t\twidth: 64px;\n\t\t\t\theight: 64px;\n\t\t\t}\n\t\t\t.hero-icon wa-icon {\n\t\t\t\tfont-size: 32px;\n\t\t\t}\n\t\t\t.feature-item {\n\t\t\t\tpadding: var(--wa-space-s);\n\t\t\t}\n\t\t\t.feature-icon {\n\t\t\t\twidth: 32px;\n\t\t\t\theight: 32px;\n\t\t\t}\n\t\t\t.action-card {\n\t\t\t\tpadding: var(--wa-space-m);\n\t\t\t}\n\t\t\t.action-card wa-icon {\n\t\t\t\tfont-size: 2rem;\n\t\t\t}\n\t\t}\n\t\t@media (max-height: 600px) {\n\t\t\t.hero-icon {\n\t\t\t\twidth: 56px;\n\t\t\t\theight: 56px;\n\t\t\t\tmargin-bottom: var(--wa-space-m);\n\t\t\t}\n\t\t\t.hero-icon wa-icon {\n\t\t\t\tfont-size: 28px;\n\t\t\t}\n\t\t}\n\t</style>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }