Compare commits

...

2 Commits

Author SHA1 Message Date
5de9bdf18c
Merge pull request #32 from mrteye/ui-redesign-sessions-drawer-17867567827419767189
Redesign the Sessions management page header and drawer
2026-08-25 15:53:32 -07:00
google-labs-jules[bot]
64a9b2cd1d refactor(ui): Redesign and consolidate sessions and passes drawer
- Removed the dual top buttons from SessionsPage.tsx.
- Removed the `#eventDrawer` and wrapped its fields, buttons, and modal under `<form id="eventForm">` inside `#tabWorkshopPass` of `#delegateDrawer`.
- Wrapped the 1:1 Direct pass form fields, buttons, and modal under `<form id="delegateForm">` inside `#tabDirectPass` of `#delegateDrawer`.
- Added a vanilla Javascript tab switching logic for UI interaction without client-side frameworks.
- Removed deprecated `openEventDrawer` and `closeEventDrawer`.
- Replaced the single main button to trigger `openDelegateDrawer`.

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
2026-08-25 22:53:20 +00:00
2 changed files with 636 additions and 623 deletions

View File

@ -145,15 +145,6 @@ export const SessionsPage = ({
</div> </div>
<div style="display: flex; gap: 0.75rem; flex-wrap: wrap;"> <div style="display: flex; gap: 0.75rem; flex-wrap: wrap;">
<button
type="button"
class="btn-outline"
style="min-height: 42px; display: inline-flex; align-items: center; gap: 0.4rem;"
onclick="openEventDrawer()"
>
<span style="font-size: 1.1rem;">🎟</span>
<span>+ Create Workshop Pass</span>
</button>
<button <button
type="button" type="button"
class="btn-primary" class="btn-primary"
@ -161,37 +152,374 @@ export const SessionsPage = ({
onclick="openDelegateDrawer()" onclick="openDelegateDrawer()"
> >
<span style="font-size: 1.1rem;">🔑</span> <span style="font-size: 1.1rem;">🔑</span>
<span>+ Delegate 1:1 Pass / Session</span> <span>+ Delegate Session</span>
</button> </button>
</div> </div>
</div> </div>
{/* Create Multi-Claim Event Pass Drawer */} {/* Delegate Session Drawer */}
<div <div
id="eventDrawer" id="delegateDrawer"
class="card" class="card"
style="display: none; border-left: 4px solid var(--success); margin-bottom: 1.5rem;" style="display: none; border-left: 4px solid var(--primary); margin-bottom: 1.5rem;"
> >
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.5rem;"> <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.5rem;">
<div> <div style="width: 100%;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h3 style="margin: 0 0 0.25rem 0; color: var(--text-primary);"> <h3 style="margin: 0 0 0.25rem 0; color: var(--text-primary);">
Create Multi-Claim Workshop / Event Pass Create New Pass / Session
</h3> </h3>
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0;">
Generate a shared event pass with a universal 6-digit PIN, vanity
link (<code style="font-size: 0.8rem;">/e/slug</code>), and CLI
1-liner for workshops, hackathons, and multi-user kiosks.
</p>
</div>
<button <button
type="button" type="button"
onclick="closeEventDrawer()" onclick="closeDelegateDrawer()"
style="background: none; border: none; font-size: 1.3rem; color: var(--text-muted); cursor: pointer;" style="background: none; border: none; font-size: 1.3rem; color: var(--text-muted); cursor: pointer;"
> >
&times; &times;
</button> </button>
</div> </div>
<div style="display: flex; background: var(--surface-muted); padding: 4px; border-radius: var(--radius-sm); border: 1px solid var(--border-subtle); gap: 4px; margin-top: 1rem; margin-bottom: 1rem;">
<button
type="button"
id="tabBtnDirectPass"
class="delegate-tab-btn active"
onclick="switchDelegateTab('tabDirectPass')"
>
🔑 1:1 Direct Pass
</button>
<button
type="button"
id="tabBtnWorkshopPass"
class="delegate-tab-btn"
onclick="switchDelegateTab('tabWorkshopPass')"
>
🎟 Multi-Claim Workshop Pass
</button>
</div>
</div>
</div>
<div id="tabDirectPass">
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0 0 1rem 0;">
Mint an isolated, scoped child session for 1-click magic links, CLI
tools, team members, or AI assistants without exposing your primary
passkeys.
</p>
<form
id="delegateForm"
onsubmit="handleDelegateSession(event)"
style="margin-top: 1rem;"
>
{/* Label Input */}
<div style="margin-bottom: 1.25rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.875rem; color: var(--text-secondary);">
Session Label / Purpose *
</label>
<input
type="text"
id="delegateLabel"
placeholder="e.g. Friend Demo Pass, Antigravity Assistant, CI/CD Runner, Terminal CLI"
required
style="width: 100%;"
/>
</div>
{/* Direct 1-Click Destination (App Selection) */}
<div style="margin-bottom: 1.25rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.875rem; color: var(--text-secondary);">
1-Click Destination App (Optional)
</label>
<select
id="delegateTargetApp"
onchange="handleTargetAppChange(this.value)"
style="width: 100%; padding: 0.5rem 0.75rem; background: var(--surface-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); color: var(--text-primary); font-size: 0.9rem;"
>
<option value="">Auth-Yes Central Dashboard (Default)</option>
{apps.map((app) => (
<option value={app.name}>
{app.name} ({app.domain || "Internal App"})
</option>
))}
</select>
<p style="font-size: 0.75rem; color: var(--text-muted); margin: 0.35rem 0 0 0;">
Selecting an app scopes the pass and automatically redirects the
recipient to that app upon 1-click redemption.
</p>
</div>
{/* Lifespan Presets */}
<div style="margin-bottom: 1.25rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.45rem; font-size: 0.875rem; color: var(--text-secondary);">
Lifespan & Auto-Expiration
</label>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
<button
type="button"
class="pill-btn lifespan-pill active"
data-hours="1"
onclick="selectLifespan(this, 1)"
>
1 Hour (Quick Task)
</button>
<button
type="button"
class="pill-btn lifespan-pill"
data-hours="12"
onclick="selectLifespan(this, 12)"
>
🛠 12 Hours (Work Session)
</button>
<button
type="button"
class="pill-btn lifespan-pill"
data-hours="24"
onclick="selectLifespan(this, 24)"
>
📅 24 Hours
</button>
<button
type="button"
class="pill-btn lifespan-pill"
data-hours="168"
onclick="selectLifespan(this, 168)"
>
🗓 7 Days
</button>
</div>
<input type="hidden" id="delegateHours" value="1" />
</div>
{/* Access Mode Presets */}
<div style="margin-bottom: 1.25rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.45rem; font-size: 0.875rem; color: var(--text-secondary);">
Access Mode & Permissions
</label>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
<button
type="button"
class="pill-btn mode-pill active"
data-mode="read_only"
onclick="selectMode(this, 'read_only')"
>
👁 Read-Only (Audit & Inspection)
</button>
<button
type="button"
class="pill-btn mode-pill"
data-mode="operator"
onclick="selectMode(this, 'operator')"
>
Operator (App Actions)
</button>
{isAdmin && (
<button
type="button"
class="pill-btn mode-pill"
data-mode="admin"
onclick="selectMode(this, 'admin')"
>
👑 Full Admin
</button>
)}
<button
type="button"
class="pill-btn mode-pill"
data-mode="custom"
onclick="selectMode(this, 'custom')"
>
🛠 Custom Scopes
</button>
</div>
<input type="hidden" id="delegateMode" value="read_only" />
</div>
{/* Progressive Disclosure: Specific App & Capability Scopes */}
<details
id="customScopesSection"
style="margin-bottom: 1.25rem; border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 0.75rem 1rem; background: var(--surface-muted);"
>
<summary style="cursor: pointer; font-weight: 600; font-size: 0.875rem; color: var(--text-primary);">
Customize Specific App Grants & Capabilities (Optional)
</summary>
<div style="margin-top: 0.75rem; display: flex; flex-direction: column; gap: 0.5rem;">
<p style="margin: 0 0 0.5rem 0; font-size: 0.8rem; color: var(--text-secondary);">
Select which subsidiary microservices and IAM capabilities
this delegated token is permitted to access:
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0.5rem;">
<label style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-primary); cursor: pointer;">
<input
type="checkbox"
class="scope-checkbox"
value="read:audit"
checked
/>
Read Audit Ledger
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-primary); cursor: pointer;">
<input
type="checkbox"
class="scope-checkbox"
value="read:users"
checked
/>
Read Users List
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-primary); cursor: pointer;">
<input
type="checkbox"
class="scope-checkbox"
value="read:apps"
checked
/>
Read Apps Registry
</label>
{apps.map((app) => (
<label
key={app.id}
style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-primary); cursor: pointer;"
>
<input
type="checkbox"
class="scope-checkbox"
value={`app:${app.name}`}
/>
{app.name} ({app.domain || "Internal"})
</label>
))}
</div>
</div>
</details>
<div style="display: flex; gap: 0.75rem;">
<button
type="submit"
id="submitDelegateBtn"
class="btn-primary"
style="min-height: 42px;"
>
Mint & Delegate Session
</button>
<button
type="button"
class="btn-outline"
onclick="closeDelegateDrawer()"
style="min-height: 42px;"
>
Cancel
</button>
</div>
</form>
{/* Hand-Off Card (Revealed upon generation) */}
<div
id="handoffModal"
style="display: none; margin-top: 1.5rem; padding: 1.25rem; background: var(--surface-card); border: 1px solid var(--primary); border-radius: var(--radius-md); box-shadow: var(--shadow-md);"
>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem;">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<span style="font-size: 1.2rem;">🔑</span>
<strong style="color: var(--text-primary); font-size: 1rem;">
Session Delegated Successfully!
</strong>
</div>
<span class="badge badge-success">Active Now</span>
</div>
<p style="font-size: 0.85rem; color: var(--text-secondary); margin: 0 0 1rem 0;">
Copy this token, 1-click magic link, or CLI export string. You can
revoke or extend this session anytime from the active session
cards below.
</p>
<div style="display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 1rem;">
{/* 1-Click Magic Link */}
<div>
<label style="display: block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.25rem;">
1-Click Magic Link (Web / Friend / Interview)
</label>
<div style="display: flex; gap: 0.5rem;">
<code
id="handoffMagicLinkText"
style="flex: 1; padding: 0.5rem 0.75rem; background: var(--surface-muted); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: monospace; font-size: 0.85rem; overflow-x: auto; white-space: nowrap; color: var(--success);"
>
</code>
<button
type="button"
class="btn-primary"
style="min-height: 36px; padding: 0 0.85rem; font-size: 0.8rem;"
onclick="copyHandoff('magic')"
>
Copy Link
</button>
</div>
</div>
{/* 1-Tap Copy CLI */}
<div>
<label style="display: block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.25rem;">
Terminal Environment Export (CLI)
</label>
<div style="display: flex; gap: 0.5rem;">
<code
id="handoffCliText"
style="flex: 1; padding: 0.5rem 0.75rem; background: var(--surface-muted); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: monospace; font-size: 0.85rem; overflow-x: auto; white-space: nowrap; color: var(--text-primary);"
>
</code>
<button
type="button"
class="btn-outline"
style="min-height: 36px; padding: 0 0.85rem; font-size: 0.8rem;"
onclick="copyHandoff('cli')"
>
Copy CLI
</button>
</div>
</div>
{/* 1-Tap Copy cURL Header */}
<div>
<label style="display: block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.25rem;">
HTTP Authorization Header
</label>
<div style="display: flex; gap: 0.5rem;">
<code
id="handoffCurlText"
style="flex: 1; padding: 0.5rem 0.75rem; background: var(--surface-muted); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: monospace; font-size: 0.85rem; overflow-x: auto; white-space: nowrap; color: var(--text-primary);"
>
</code>
<button
type="button"
class="btn-outline"
style="min-height: 36px; padding: 0 0.85rem; font-size: 0.8rem;"
onclick="copyHandoff('curl')"
>
Copy Header
</button>
</div>
</div>
</div>
<button
type="button"
class="btn-outline"
style="width: 100%; min-height: 38px; justify-content: center; font-size: 0.85rem;"
onclick="closeHandoffModal()"
>
Done (Session is Active)
</button>
</div>
</div>
<div id="tabWorkshopPass" style="display: none;">
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0 0 1rem 0;">
Generate a shared event pass with a universal 6-digit PIN, vanity
link (<code style="font-size: 0.8rem;">/e/slug</code>), and CLI
1-liner for workshops, hackathons, and multi-user kiosks.
</p>
<form <form
id="eventForm" id="eventForm"
onsubmit="handleCreateEvent(event)" onsubmit="handleCreateEvent(event)"
@ -373,7 +701,7 @@ export const SessionsPage = ({
<button <button
type="button" type="button"
class="btn-outline" class="btn-outline"
onclick="closeEventDrawer()" onclick="closeDelegateDrawer()"
style="min-height: 42px;" style="min-height: 42px;"
> >
Cancel Cancel
@ -400,9 +728,9 @@ export const SessionsPage = ({
</div> </div>
<p style="font-size: 0.85rem; color: var(--text-secondary); margin: 0 0 1rem 0;"> <p style="font-size: 0.85rem; color: var(--text-secondary); margin: 0 0 1rem 0;">
Share the 6-digit PIN, 1-click link, or CLI 1-liner with attendees. Share the 6-digit PIN, 1-click link, or CLI 1-liner with
You can monitor live claimed seats and trigger the master kill attendees. You can monitor live claimed seats and trigger the
switch from the Event Cockpit below. master kill switch from the Event Cockpit below.
</p> </p>
<div style="display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 1.25rem;"> <div style="display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 1.25rem;">
@ -483,338 +811,6 @@ export const SessionsPage = ({
</button> </button>
</div> </div>
</div> </div>
{/* Delegate Session Drawer */}
<div
id="delegateDrawer"
class="card"
style="display: none; border-left: 4px solid var(--primary); margin-bottom: 1.5rem;"
>
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.5rem;">
<div>
<h3 style="margin: 0 0 0.25rem 0; color: var(--text-primary);">
Delegate New 1:1 Pass / Session
</h3>
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0;">
Mint an isolated, scoped child session for 1-click magic links,
CLI tools, team members, or AI assistants without exposing your
primary passkeys.
</p>
</div>
<button
type="button"
onclick="closeDelegateDrawer()"
style="background: none; border: none; font-size: 1.3rem; color: var(--text-muted); cursor: pointer;"
>
&times;
</button>
</div>
<form
id="delegateForm"
onsubmit="handleDelegateSession(event)"
style="margin-top: 1rem;"
>
{/* Label Input */}
<div style="margin-bottom: 1.25rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.875rem; color: var(--text-secondary);">
Session Label / Purpose *
</label>
<input
type="text"
id="delegateLabel"
placeholder="e.g. Friend Demo Pass, Antigravity Assistant, CI/CD Runner, Terminal CLI"
required
style="width: 100%;"
/>
</div>
{/* Direct 1-Click Destination (App Selection) */}
<div style="margin-bottom: 1.25rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.875rem; color: var(--text-secondary);">
1-Click Destination App (Optional)
</label>
<select
id="delegateTargetApp"
onchange="handleTargetAppChange(this.value)"
style="width: 100%; padding: 0.5rem 0.75rem; background: var(--surface-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); color: var(--text-primary); font-size: 0.9rem;"
>
<option value="">Auth-Yes Central Dashboard (Default)</option>
{apps.map((app) => (
<option value={app.name}>
{app.name} ({app.domain || "Internal App"})
</option>
))}
</select>
<p style="font-size: 0.75rem; color: var(--text-muted); margin: 0.35rem 0 0 0;">
Selecting an app scopes the pass and automatically redirects the
recipient to that app upon 1-click redemption.
</p>
</div>
{/* Lifespan Presets */}
<div style="margin-bottom: 1.25rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.45rem; font-size: 0.875rem; color: var(--text-secondary);">
Lifespan & Auto-Expiration
</label>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
<button
type="button"
class="pill-btn lifespan-pill active"
data-hours="1"
onclick="selectLifespan(this, 1)"
>
1 Hour (Quick Task)
</button>
<button
type="button"
class="pill-btn lifespan-pill"
data-hours="12"
onclick="selectLifespan(this, 12)"
>
🛠 12 Hours (Work Session)
</button>
<button
type="button"
class="pill-btn lifespan-pill"
data-hours="24"
onclick="selectLifespan(this, 24)"
>
📅 24 Hours
</button>
<button
type="button"
class="pill-btn lifespan-pill"
data-hours="168"
onclick="selectLifespan(this, 168)"
>
🗓 7 Days
</button>
</div>
<input type="hidden" id="delegateHours" value="1" />
</div>
{/* Access Mode Presets */}
<div style="margin-bottom: 1.25rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.45rem; font-size: 0.875rem; color: var(--text-secondary);">
Access Mode & Permissions
</label>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
<button
type="button"
class="pill-btn mode-pill active"
data-mode="read_only"
onclick="selectMode(this, 'read_only')"
>
👁 Read-Only (Audit & Inspection)
</button>
<button
type="button"
class="pill-btn mode-pill"
data-mode="operator"
onclick="selectMode(this, 'operator')"
>
Operator (App Actions)
</button>
{isAdmin && (
<button
type="button"
class="pill-btn mode-pill"
data-mode="admin"
onclick="selectMode(this, 'admin')"
>
👑 Full Admin
</button>
)}
<button
type="button"
class="pill-btn mode-pill"
data-mode="custom"
onclick="selectMode(this, 'custom')"
>
🛠 Custom Scopes
</button>
</div>
<input type="hidden" id="delegateMode" value="read_only" />
</div>
{/* Progressive Disclosure: Specific App & Capability Scopes */}
<details
id="customScopesSection"
style="margin-bottom: 1.25rem; border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 0.75rem 1rem; background: var(--surface-muted);"
>
<summary style="cursor: pointer; font-weight: 600; font-size: 0.875rem; color: var(--text-primary);">
Customize Specific App Grants & Capabilities (Optional)
</summary>
<div style="margin-top: 0.75rem; display: flex; flex-direction: column; gap: 0.5rem;">
<p style="margin: 0 0 0.5rem 0; font-size: 0.8rem; color: var(--text-secondary);">
Select which subsidiary microservices and IAM capabilities this
delegated token is permitted to access:
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0.5rem;">
<label style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-primary); cursor: pointer;">
<input
type="checkbox"
class="scope-checkbox"
value="read:audit"
checked
/>
Read Audit Ledger
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-primary); cursor: pointer;">
<input
type="checkbox"
class="scope-checkbox"
value="read:users"
checked
/>
Read Users List
</label>
<label style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-primary); cursor: pointer;">
<input
type="checkbox"
class="scope-checkbox"
value="read:apps"
checked
/>
Read Apps Registry
</label>
{apps.map((app) => (
<label
key={app.id}
style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-primary); cursor: pointer;"
>
<input
type="checkbox"
class="scope-checkbox"
value={`app:${app.name}`}
/>
{app.name} ({app.domain || "Internal"})
</label>
))}
</div>
</div>
</details>
<div style="display: flex; gap: 0.75rem;">
<button
type="submit"
id="submitDelegateBtn"
class="btn-primary"
style="min-height: 42px;"
>
Mint & Delegate Session
</button>
<button
type="button"
class="btn-outline"
onclick="closeDelegateDrawer()"
style="min-height: 42px;"
>
Cancel
</button>
</div>
</form>
{/* Hand-Off Card (Revealed upon generation) */}
<div
id="handoffModal"
style="display: none; margin-top: 1.5rem; padding: 1.25rem; background: var(--surface-card); border: 1px solid var(--primary); border-radius: var(--radius-md); box-shadow: var(--shadow-md);"
>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem;">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<span style="font-size: 1.2rem;">🔑</span>
<strong style="color: var(--text-primary); font-size: 1rem;">
Session Delegated Successfully!
</strong>
</div>
<span class="badge badge-success">Active Now</span>
</div>
<p style="font-size: 0.85rem; color: var(--text-secondary); margin: 0 0 1rem 0;">
Copy this token, 1-click magic link, or CLI export string. You can
revoke or extend this session anytime from the active session cards
below.
</p>
<div style="display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 1rem;">
{/* 1-Click Magic Link */}
<div>
<label style="display: block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.25rem;">
1-Click Magic Link (Web / Friend / Interview)
</label>
<div style="display: flex; gap: 0.5rem;">
<code
id="handoffMagicLinkText"
style="flex: 1; padding: 0.5rem 0.75rem; background: var(--surface-muted); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: monospace; font-size: 0.85rem; overflow-x: auto; white-space: nowrap; color: var(--success);"
>
</code>
<button
type="button"
class="btn-primary"
style="min-height: 36px; padding: 0 0.85rem; font-size: 0.8rem;"
onclick="copyHandoff('magic')"
>
Copy Link
</button>
</div>
</div>
{/* 1-Tap Copy CLI */}
<div>
<label style="display: block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.25rem;">
Terminal Environment Export (CLI)
</label>
<div style="display: flex; gap: 0.5rem;">
<code
id="handoffCliText"
style="flex: 1; padding: 0.5rem 0.75rem; background: var(--surface-muted); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: monospace; font-size: 0.85rem; overflow-x: auto; white-space: nowrap; color: var(--text-primary);"
>
</code>
<button
type="button"
class="btn-outline"
style="min-height: 36px; padding: 0 0.85rem; font-size: 0.8rem;"
onclick="copyHandoff('cli')"
>
Copy CLI
</button>
</div>
</div>
{/* 1-Tap Copy cURL Header */}
<div>
<label style="display: block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.25rem;">
HTTP Authorization Header
</label>
<div style="display: flex; gap: 0.5rem;">
<code
id="handoffCurlText"
style="flex: 1; padding: 0.5rem 0.75rem; background: var(--surface-muted); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: monospace; font-size: 0.85rem; overflow-x: auto; white-space: nowrap; color: var(--text-primary);"
>
</code>
<button
type="button"
class="btn-outline"
style="min-height: 36px; padding: 0 0.85rem; font-size: 0.8rem;"
onclick="copyHandoff('curl')"
>
Copy Header
</button>
</div>
</div>
</div>
<button
type="button"
class="btn-outline"
style="width: 100%; min-height: 38px; justify-content: center; font-size: 0.85rem;"
onclick="closeHandoffModal()"
>
Done (Session is Active)
</button>
</div>
</div> </div>
{/* Edit Scopes Modal */} {/* Edit Scopes Modal */}
@ -1178,6 +1174,22 @@ export const SessionsPage = ({
border-color: var(--primary); border-color: var(--primary);
color: var(--primary); color: var(--primary);
} }
.delegate-tab-btn {
flex: 1;
padding: 0.5rem 0.75rem;
border-radius: var(--radius-sm);
border: none;
font-weight: 600;
cursor: pointer;
transition: all 0.15s;
background: transparent;
color: var(--text-secondary);
}
.delegate-tab-btn.active {
background: var(--surface-card);
color: var(--primary);
box-shadow: var(--shadow-xs);
}
`} `}
</style> </style>
@ -1197,8 +1209,6 @@ export const SessionsPage = ({
} }
function openDelegateDrawer() { function openDelegateDrawer() {
const eventDrawer = document.getElementById('eventDrawer');
if (eventDrawer) eventDrawer.style.display = 'none';
document.getElementById('delegateDrawer').style.display = 'block'; document.getElementById('delegateDrawer').style.display = 'block';
document.getElementById('delegateDrawer').scrollIntoView({ behavior: 'smooth' }); document.getElementById('delegateDrawer').scrollIntoView({ behavior: 'smooth' });
} }
@ -1207,15 +1217,18 @@ export const SessionsPage = ({
document.getElementById('delegateDrawer').style.display = 'none'; document.getElementById('delegateDrawer').style.display = 'none';
} }
function openEventDrawer() { function switchDelegateTab(tabId) {
const delegateDrawer = document.getElementById('delegateDrawer'); document.getElementById('tabDirectPass').style.display = 'none';
if (delegateDrawer) delegateDrawer.style.display = 'none'; document.getElementById('tabWorkshopPass').style.display = 'none';
document.getElementById('eventDrawer').style.display = 'block'; document.getElementById('tabBtnDirectPass').classList.remove('active');
document.getElementById('eventDrawer').scrollIntoView({ behavior: 'smooth' }); document.getElementById('tabBtnWorkshopPass').classList.remove('active');
}
function closeEventDrawer() { document.getElementById(tabId).style.display = 'block';
document.getElementById('eventDrawer').style.display = 'none'; if (tabId === 'tabDirectPass') {
document.getElementById('tabBtnDirectPass').classList.add('active');
} else {
document.getElementById('tabBtnWorkshopPass').classList.add('active');
}
} }
function selectSeats(btn, count) { function selectSeats(btn, count) {