Gate authenticated startup behind a branded connecting screen until webview handoff completes
Hop-State: A_06FPH83PG5HPAWAAD6CMP0G Hop-Proposal: R_06FPH837DB89SK1KPK2QGA8 Hop-Task: T_06FPH7MPR7BHXM62H21SEER Hop-Attempt: AT_06FPH7MPR5TCA3S36T5CMXG
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#070808" />
|
||||
<link rel="preload" href="/logotext.svg" as="image" type="image/svg+xml" />
|
||||
<title>Synapsis</title>
|
||||
</head>
|
||||
|
||||
|
||||
+15
-6
@@ -12,7 +12,7 @@ input:focus-visible { outline: 0; }
|
||||
.signal-panel { position: relative; min-width: 0; overflow: hidden; border-right: 1px solid rgba(255,255,255,.1); }
|
||||
.signal-field { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||||
.signal-shade { position: absolute; inset: 0; background: radial-gradient(circle at 55% 45%, transparent 0, rgba(0,0,0,.08) 38%, rgba(0,0,0,.74) 100%), linear-gradient(180deg, rgba(0,0,0,.12), rgba(0,0,0,.4)); pointer-events: none; }
|
||||
.panel-brand, .sidebar-brand, .launch-mark { position: absolute; top: 52px; left: 48px; z-index: 2; display: flex; align-items: center; }
|
||||
.panel-brand, .sidebar-brand { position: absolute; top: 52px; left: 48px; z-index: 2; display: flex; align-items: center; }
|
||||
.wordmark { display: block; width: auto; object-fit: contain; }
|
||||
.panel-wordmark { width: 146px; height: 28px; }
|
||||
.signal-copy { position: absolute; z-index: 2; left: 48px; right: 36px; bottom: 104px; }
|
||||
@@ -65,11 +65,20 @@ label { display: block; margin: 0 0 9px; color: rgba(255,255,255,.75); font-size
|
||||
.spinner.dark { width: 15px; height: 15px; border-color: rgba(0,0,0,.18); border-top-color: #111; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
.launch-screen { position: relative; width: 100%; height: 100%; display: grid; place-items: center; background: #060707; }
|
||||
.launch-screen::after { content: ""; position: absolute; inset: 0; background: radial-gradient(circle, transparent, rgba(0,0,0,.6)); }
|
||||
.launch-mark { left: 50%; top: 50%; transform: translate(-50%, -50%); }
|
||||
.launch-wordmark { width: 176px; height: 33px; }
|
||||
.launch-screen > .spinner { position: absolute; z-index: 3; bottom: 12%; }
|
||||
.launch-screen { position: relative; width: 100%; height: 100%; display: grid; place-items: center; overflow: hidden; background: #060707; }
|
||||
.launch-screen::after { content: ""; position: absolute; inset: 0; background: radial-gradient(circle at 50% 48%, rgba(20,22,22,.18), rgba(0,0,0,.72) 72%); pointer-events: none; }
|
||||
.launch-content { position: relative; z-index: 3; width: 260px; display: flex; flex-direction: column; align-items: center; transform: translateY(-3px); }
|
||||
.launch-wordmark { width: 204px; height: 38px; }
|
||||
.connecting-dots { width: 168px; height: 34px; margin-top: 31px; overflow: visible; }
|
||||
.connecting-line { fill: none; stroke: rgba(255,255,255,.18); stroke-width: 1; stroke-dasharray: .18 .08; animation: connect-line 2.4s ease-in-out infinite; }
|
||||
.connecting-dot { fill: rgba(255,255,255,.28); transform-box: fill-box; transform-origin: center; animation: connect-dot 2.4s ease-in-out infinite; }
|
||||
.connecting-dot.dot-2 { animation-delay: .16s; }
|
||||
.connecting-dot.dot-3 { animation-delay: .32s; }
|
||||
.connecting-dot.dot-4 { animation-delay: .48s; }
|
||||
.connecting-dot.dot-5 { animation-delay: .64s; }
|
||||
.launch-content p { margin: 18px 0 0; color: rgba(255,255,255,.34); font: 500 8px/1 "Roboto Mono", monospace; letter-spacing: 2.2px; }
|
||||
@keyframes connect-line { 0%, 18% { stroke-dashoffset: 1; opacity: .24; } 58%, 78% { stroke-dashoffset: 0; opacity: .9; } 100% { stroke-dashoffset: -1; opacity: .24; } }
|
||||
@keyframes connect-dot { 0%, 16%, 100% { fill: rgba(255,255,255,.24); filter: none; transform: scale(.75); } 38%, 62% { fill: #fff; filter: drop-shadow(0 0 5px rgba(255,255,255,.9)); transform: scale(1.25); } 82% { fill: rgba(255,255,255,.42); transform: scale(.9); } }
|
||||
|
||||
.app-shell { width: 100%; height: 100%; display: grid; grid-template-columns: 238px 1fr; background: #0a0b0b; }
|
||||
.sidebar { position: relative; display: flex; flex-direction: column; padding: 104px 18px 18px; border-right: 1px solid rgba(255,255,255,.09); background: #080909; }
|
||||
|
||||
+29
-2
@@ -75,6 +75,30 @@ function Wordmark({ className = "" }: { className?: string }) {
|
||||
return <img className={`wordmark ${className}`} src="/logotext.svg" alt="Synapsis" />;
|
||||
}
|
||||
|
||||
function ConnectingDots() {
|
||||
return (
|
||||
<svg className="connecting-dots" viewBox="0 0 168 34" aria-hidden="true">
|
||||
<path className="connecting-line" d="M12 17H156" pathLength="1" />
|
||||
{[12, 48, 84, 120, 156].map((x, index) => (
|
||||
<circle key={x} className={`connecting-dot dot-${index + 1}`} cx={x} cy="17" r="3" />
|
||||
))}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function LoadingScreen() {
|
||||
return (
|
||||
<main className="launch-screen" aria-busy="true" aria-label="Connecting to Synapsis">
|
||||
<SignalField />
|
||||
<div className="launch-content">
|
||||
<Wordmark className="launch-wordmark" />
|
||||
<ConnectingDots />
|
||||
<p>CONNECTING TO YOUR NETWORK</p>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
function SignalField() {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
@@ -260,10 +284,12 @@ function App() {
|
||||
|
||||
const savedNodeUrl = savedNode;
|
||||
async function restore() {
|
||||
let handedOffToWebApp = false;
|
||||
try {
|
||||
const hasWebSession = await invoke<boolean>("has_web_session", { nodeUrl: savedNodeUrl });
|
||||
if (hasWebSession) {
|
||||
await openNodeWebApp(savedNodeUrl);
|
||||
handedOffToWebApp = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -273,13 +299,14 @@ function App() {
|
||||
setUser(connection.user);
|
||||
if (connection.user) {
|
||||
await openNodeWebApp(connection.nodeUrl);
|
||||
handedOffToWebApp = true;
|
||||
} else {
|
||||
setScreen("login");
|
||||
}
|
||||
} catch {
|
||||
setScreen("node");
|
||||
} finally {
|
||||
setBootstrapping(false);
|
||||
if (!handedOffToWebApp) setBootstrapping(false);
|
||||
}
|
||||
}
|
||||
void restore();
|
||||
@@ -369,7 +396,7 @@ function App() {
|
||||
}
|
||||
|
||||
if (bootstrapping) {
|
||||
return <main className="launch-screen"><SignalField/><div className="launch-mark"><Wordmark className="launch-wordmark"/></div><span className="spinner" aria-label="Loading Synapsis"/></main>;
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
if (screen === "home" && user) {
|
||||
|
||||
Reference in New Issue
Block a user