branded app loading screen (0.78.0)

- the Suspense fallback (shown on every cold load while the app chunk loads,
  longer on slow/mobile connections) was a plain monospace 'Loading amerc…'
- replace with a branded screen: a pulsing cyan gem over 'amerc' in gold
  Georgia on a dark radial background, matching the logo + error-boundary look
- respects prefers-reduced-motion
- also audited the public mobile surfaces (agent modal, status, changelog) —
  all mobile-safe (modal is width:min(640px,96vw) + scroll)
- verified the loading screen renders (blocked the app chunk to capture it)
This commit is contained in:
artheru 2026-06-10 11:20:37 +08:00
parent f063e9ae80
commit dbe3c383e3
5 changed files with 18 additions and 4 deletions

View File

@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#0e0a14" /> <meta name="theme-color" content="#0e0a14" />
<meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." /> <meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." />
<meta name="version" content="0.77.0-docsmobile" /> <meta name="version" content="0.78.0-loadscreen" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" /> <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" /> <link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" /> <link rel="apple-touch-icon" href="/apple-touch-icon.png" />

View File

@ -1,6 +1,6 @@
{ {
"name": "amerc-site", "name": "amerc-site",
"version": "0.77.0-docsmobile", "version": "0.78.0-loadscreen",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@ -10,7 +10,7 @@ import { api } from './api.js';
const Menu = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /></svg>); const Menu = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /></svg>);
const X = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>); const X = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>);
const RELEASE = '0.77.0-docsmobile'; const RELEASE = '0.78.0-loadscreen';
const NAV = [ const NAV = [
{ id: 'home', label: 'Tavern' }, { id: 'home', label: 'Tavern' },

View File

@ -45,10 +45,17 @@ class ErrorBoundary extends React.Component {
} }
} }
const Loading = () => (
<div className="app-loading">
<span className="app-loading-gem" aria-hidden="true" />
<span className="app-loading-word">amerc</span>
</div>
);
createRoot(document.getElementById('root')).render( createRoot(document.getElementById('root')).render(
<React.StrictMode> <React.StrictMode>
<ErrorBoundary> <ErrorBoundary>
<Suspense fallback={<div style={{ color: '#9fb0d0', fontFamily: 'monospace', padding: 24 }}>Loading amerc</div>}> <Suspense fallback={<Loading />}>
<App /> <App />
</Suspense> </Suspense>
</ErrorBoundary> </ErrorBoundary>

View File

@ -1487,3 +1487,10 @@ button {
.docs-side { width: 100%; max-height: 210px; overflow-y: auto; border-right: none; border-bottom: 2px solid #1b2536; } .docs-side { width: 100%; max-height: 210px; overflow-y: auto; border-right: none; border-bottom: 2px solid #1b2536; }
.docs-main { padding: 20px 16px; } .docs-main { padding: 20px 16px; }
} }
/* branded app loading screen (Suspense fallback) (0.78) */
.app-loading { position: fixed; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; background: radial-gradient(circle at 50% 38%, #160d22, #07060c 70%); }
.app-loading-gem { width: 28px; height: 28px; background: linear-gradient(135deg, #5fe6ff, #1f86c4); box-shadow: 0 0 0 3px #07273a, 0 0 22px rgba(39,216,255,0.5); animation: app-pulse 1.3s ease-in-out infinite; }
.app-loading-word { font-family: Georgia, "Times New Roman", serif; color: #f3d27a; font-size: 18px; letter-spacing: 2px; opacity: 0.85; }
@keyframes app-pulse { 0%, 100% { transform: rotate(45deg) scale(1); opacity: 0.7; } 50% { transform: rotate(45deg) scale(1.16); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .app-loading-gem { animation: none; transform: rotate(45deg); } }