From 889329c52e2219d7c904b020d9800c21d19ba81d Mon Sep 17 00:00:00 2001 From: cclohmar Date: Sun, 14 Jun 2026 18:04:58 +0000 Subject: [PATCH] fix(ui): user dropdown with logout, fix script injection, pink outlines --- src/core/ui/launcher.templ | 36 +++++++++++++++++++-- src/core/ui/launcher_templ.go | 59 ++++++++++++++++++++++++----------- 2 files changed, 74 insertions(+), 21 deletions(-) diff --git a/src/core/ui/launcher.templ b/src/core/ui/launcher.templ index a7eed3c..2805b05 100644 --- a/src/core/ui/launcher.templ +++ b/src/core/ui/launcher.templ @@ -41,7 +41,7 @@ templ LauncherPage(userName string, role string, apps []AppTile) { - + @launcherJS() } @@ -66,8 +66,15 @@ templ topBar(userName string, role string) { -
+
{ initials(userName) }
+
@@ -115,6 +122,22 @@ templ workspaceCSS() { font-weight:600;font-size:15px;color:#fff;flex-shrink:0 } .avatar.sm{width:32px;height:32px;font-size:13px} + + /* User Dropdown */ + .user-menu{position:relative;cursor:pointer} + .dropdown{ + display:none;position:absolute;right:0;top:44px; + background:var(--surface);border:1px solid var(--border);border-radius:12px; + min-width:180px;padding:6px;z-index:200;box-shadow:0 8px 24px rgba(0,0,0,0.4) + } + .dropdown.show{display:block} + .dropdown-header{padding:10px 12px;font-size:0.85rem;font-weight:500;border-bottom:1px solid var(--border);margin-bottom:4px} + .dropdown-item{ + display:flex;align-items:center;gap:10px;padding:10px 12px; + border-radius:8px;font-size:0.85rem;color:var(--text);cursor:pointer;transition:background 0.1s + } + .dropdown-item:hover{background:var(--surface2)} + .dropdown-item svg{flex-shrink:0} .content{flex:1;padding:24px 16px;max-width:900px;margin:0 auto;width:100%} .greeting{display:flex;align-items:center;gap:12px;margin-bottom:24px} @media(min-width:600px){.greeting{margin-bottom:32px}} @@ -228,6 +251,15 @@ templ launcherJS() { document.getElementById('pwa-overlay').style.display = 'none'; } + function toggleUserMenu(e) { + e.stopPropagation(); + document.getElementById('userDropdown').classList.toggle('show'); + } + document.addEventListener('click', function() { + var d = document.getElementById('userDropdown'); + if (d) d.classList.remove('show'); + }); + // PWA install handler let deferredPrompt = null; window.addEventListener('beforeinstallprompt', function(e) { diff --git a/src/core/ui/launcher_templ.go b/src/core/ui/launcher_templ.go index 0d0072b..6a0bcbe 100644 --- a/src/core/ui/launcher_templ.go +++ b/src/core/ui/launcher_templ.go @@ -80,7 +80,15 @@ func LauncherPage(userName string, role string, apps []AppTile) templ.Component if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = launcherJS().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -109,7 +117,7 @@ func greetingSection(userName string) templ.Component { templ_7745c5c3_Var3 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -122,7 +130,7 @@ func greetingSection(userName string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "

Good ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "

Good ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -135,7 +143,7 @@ func greetingSection(userName string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, ", ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, ", ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -148,7 +156,7 @@ func greetingSection(userName string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "

Your workspace is ready

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "

Your workspace is ready

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -177,7 +185,7 @@ func topBar(userName string, role string) templ.Component { templ_7745c5c3_Var7 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
N
NextWks
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
N
NextWks
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -190,7 +198,20 @@ func topBar(userName string, role string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(userName) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/ui/launcher.templ`, Line: 72, Col: 44} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
Logout
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -214,12 +235,12 @@ func workspaceCSS() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var9 := templ.GetChildren(ctx) - if templ_7745c5c3_Var9 == nil { - templ_7745c5c3_Var9 = templ.NopComponent + templ_7745c5c3_Var10 := templ.GetChildren(ctx) + if templ_7745c5c3_Var10 == nil { + templ_7745c5c3_Var10 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -265,12 +286,12 @@ func pwaInstructions() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var10 := templ.GetChildren(ctx) - if templ_7745c5c3_Var10 == nil { - templ_7745c5c3_Var10 = templ.NopComponent + templ_7745c5c3_Var11 := templ.GetChildren(ctx) + if templ_7745c5c3_Var11 == nil { + templ_7745c5c3_Var11 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "

Desktop Chrome/Edge

  1. Click the icon in the address bar
  2. Click Install

iOS Safari

  1. Tap Share
  2. Tap Add to Home Screen

Android Chrome

  1. Tap menu
  2. Tap Install app
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "

Desktop Chrome/Edge

  1. Click the icon in the address bar
  2. Click Install

iOS Safari

  1. Tap Share
  2. Tap Add to Home Screen

Android Chrome

  1. Tap menu
  2. Tap Install app
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -295,12 +316,12 @@ func launcherJS() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var11 := templ.GetChildren(ctx) - if templ_7745c5c3_Var11 == nil { - templ_7745c5c3_Var11 = templ.NopComponent + templ_7745c5c3_Var12 := templ.GetChildren(ctx) + if templ_7745c5c3_Var12 == nil { + templ_7745c5c3_Var12 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }