Remove startup reveal animation
Build and Deploy Gateway / build-and-push (push) Successful in 22s
Build and Deploy Gateway / deploy (push) Successful in 7s

This commit is contained in:
2026-08-15 10:34:19 +03:00
parent 804e08727e
commit 8eccdd4050
6 changed files with 19 additions and 58 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
export const HARBOR_VERSIONS = Object.freeze({ export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.25.14', macClient: '0.25.15',
gatewayClient: '0.26.13', gatewayClient: '0.26.14',
gatewayBackend: '0.26.5', gatewayBackend: '0.26.5',
}); });
+1 -8
View File
@@ -442,7 +442,6 @@ export function ClientOverviewPage({
const desiredServer = desiredProfile?.servers.find(({ id }) => id === selectedServerId); const desiredServer = desiredProfile?.servers.find(({ id }) => id === selectedServerId);
const showPower = hasSubscription; const showPower = hasSubscription;
const [now, setNow] = useState(Date.now()); const [now, setNow] = useState(Date.now());
const [showIntro, setShowIntro] = useState(true);
const [copyFeedback, setCopyFeedback] = useState<CopyFeedbackMap>({}); const [copyFeedback, setCopyFeedback] = useState<CopyFeedbackMap>({});
const [copyAnnouncement, setCopyAnnouncement] = useState<CopyAnnouncement>({ text: '', cycle: 0 }); const [copyAnnouncement, setCopyAnnouncement] = useState<CopyAnnouncement>({ text: '', cycle: 0 });
const [drawerSwitchTarget, setDrawerSwitchTarget] = useState<DrawerKey | null>(null); const [drawerSwitchTarget, setDrawerSwitchTarget] = useState<DrawerKey | null>(null);
@@ -580,12 +579,6 @@ export function ClientOverviewPage({
return () => clearInterval(timer); return () => clearInterval(timer);
}, [isGateway, connected, state?.connection?.startedAt]); }, [isGateway, connected, state?.connection?.startedAt]);
useEffect(() => {
if (!showIntro) return undefined;
const timer = setTimeout(() => setShowIntro(false), 1200);
return () => clearTimeout(timer);
}, [showIntro]);
useEffect(() => { useEffect(() => {
if (!hasSubscription) { if (!hasSubscription) {
routingFeature.forceClose(); routingFeature.forceClose();
@@ -741,7 +734,7 @@ export function ClientOverviewPage({
return ( return (
<div <div
className={`client-shell${!hasSubscription ? ' is-first-run' : ''}${showIntro ? ' is-intro' : ''}`} className={`client-shell${!hasSubscription ? ' is-first-run' : ''}`}
> >
<VersionDisplay isGateway={isGateway} versionInfo={versionInfo} /> <VersionDisplay isGateway={isGateway} versionInfo={versionInfo} />
<div className="client-live-region" role="status" aria-live="polite" aria-atomic="true"> <div className="client-live-region" role="status" aria-live="polite" aria-atomic="true">
-18
View File
@@ -233,24 +233,6 @@
text-shadow: 0 0 12px color-mix(in oklch, var(--client-accent) 34%, transparent); text-shadow: 0 0 12px color-mix(in oklch, var(--client-accent) 34%, transparent);
} }
.client-shell.is-intro .harbor-brand-content {
animation: harbor-startup-brand 760ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-shell.is-intro .client-panel > :not(.client-drawer),
.client-shell.is-intro .client-secondary-menu,
.client-shell.is-intro .harbor-versions {
animation: harbor-startup-content 720ms 120ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes harbor-startup-brand {
from { opacity: 0.35; filter: blur(5px); }
}
@keyframes harbor-startup-content {
from { opacity: 0; filter: blur(6px); }
}
.harbor-brand svg { .harbor-brand svg {
width: 24px; width: 24px;
height: 24px; height: 24px;
-6
View File
@@ -156,12 +156,6 @@
transition: none; transition: none;
} }
.client-shell.is-intro .client-panel > :not(.client-drawer),
.client-shell.is-intro .client-secondary-menu,
.client-shell.is-intro .harbor-versions {
animation: none;
}
.harbor-mode-stack em, .harbor-mode-stack em,
.harbor-mode-gateway > span, .harbor-mode-gateway > span,
.harbor-mode-swap g, .harbor-mode-swap g,
+3 -11
View File
@@ -57,17 +57,9 @@ test('desktop layout keeps the power control on a symmetric center axis beside t
assert.match(component, /\$\{isGateway && hasSubscription \? ' is-gateway-home' : ''\}/); assert.match(component, /\$\{isGateway && hasSubscription \? ' is-gateway-home' : ''\}/);
}); });
test('page reload plays one stable startup sequence', () => { test('page reload renders the accepted snapshot without a second startup reveal', () => {
assert.match(component, /const \[showIntro, setShowIntro\] = useState\(true\)/); assert.doesNotMatch(component, /showIntro|is-intro/);
assert.match(component, /\$\{showIntro \? ' is-intro' : ''\}/); assert.doesNotMatch(styles, /is-intro|harbor-startup-(?:brand|content)/);
assert.doesNotMatch(component, /showIntro && !hasSubscription/);
assert.match(styles, /\.client-shell\.is-intro \.harbor-brand-content \{[\s\S]*harbor-startup-brand 760ms/);
assert.match(styles, /@keyframes harbor-startup-brand[\s\S]*opacity: 0\.35;[\s\S]*filter: blur\(5px\)/);
assert.doesNotMatch(/@keyframes harbor-startup-brand \{([\s\S]*?)\n\}/.exec(styles)?.[1] || '', /translate|scale/);
assert.match(styles, /\.client-shell\.is-intro \.client-panel > :not\(\.client-drawer\),[\s\S]*\.client-secondary-menu,[\s\S]*\.harbor-versions \{[\s\S]*harbor-startup-content 720ms 120ms/);
assert.doesNotMatch(styles, /\.client-shell\.is-intro \.client-panel\s*,/);
assert.match(styles, /@keyframes harbor-startup-content[\s\S]*opacity: 0;[\s\S]*filter: blur\(6px\)/);
assert.match(styles, /@media \(prefers-reduced-motion: reduce\)[\s\S]*\.client-shell\.is-intro \.client-panel > :not\(\.client-drawer\),[\s\S]*animation: none/);
}); });
test('server rows scroll inside the subscription drawer without moving the main layout', () => { test('server rows scroll inside the subscription drawer without moving the main layout', () => {
+13 -13
View File
@@ -37,26 +37,26 @@ const expectedImports = [
const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex'); const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex');
const acceptedLedger = { const acceptedLedger = {
counts: { counts: {
cascadeEdges: 823, cascadeEdges: 820,
customProperties: 105, customProperties: 105,
declarations: 3314, declarations: 3307,
important: 0, important: 0,
keyframes: 51, keyframes: 49,
media: 12, media: 12,
rules: 946, rules: 941,
variableReferences: 814, variableReferences: 814,
}, },
hashes: { hashes: {
cascadeEdges: '42851684f732dd5e7f04959d009648dd4a8fab85904759da16786411ab199d1a', cascadeEdges: 'a90bd0c489fe893ba65d6b4be6a56fdde8b9c8ba5a9ce606190c6eb95f7a25b1',
customProperties: 'dede9598ee62929fb29ff470897cd475ca34eb5af5dda342b09e306f3eaee887', customProperties: 'dede9598ee62929fb29ff470897cd475ca34eb5af5dda342b09e306f3eaee887',
declarations: 'fac169725174c1303de5673abfd42b7316c2180830d66173dca8a2e449b1db5e', declarations: '782a758059d22fe094c2a972f315f5b7273987d5240a5d4c652959fe646f96aa',
duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945', duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945',
duplicateSelectors: '0fa66e370695261a2a5c6a189752c07d518d80d68d3e79a3ba4bb34407893c0e', duplicateSelectors: '0fa66e370695261a2a5c6a189752c07d518d80d68d3e79a3ba4bb34407893c0e',
keyframes: 'b2b826683e11dfa95cf2d01996d43a02b3a2b33672a14353ebfbee7701d4532e', keyframes: '9e78309512ed82b1e9f87c58aeff505dfcdb694fc30c8f54570d01dce13eb51a',
ruleDeclarationSequences: '7750298af4530d2b0f7d7b0feee21c6bd00a5f9a7ed1d6cfa368e2d9bcace26c', ruleDeclarationSequences: '04efee8e9ff18e320bdb1c7c6981097a8d5bdf9fab5a461403425053cba4d172',
selectors: 'aa487c004e9960e60982e983fcbdb3becef76b0007d3ef85a96f45d4c3d7933e', selectors: '59d230e56506322b783962fbd2b86a1a0a3d08a3eb6ecba57f23ba20733a33c8',
variableReferences: '67fbd84828e97d10d7fe163ebea6504e8c48355e99616ede2498307d836fa616', variableReferences: '67fbd84828e97d10d7fe163ebea6504e8c48355e99616ede2498307d836fa616',
witnesses: '5ae376bbd8884cc14a6907fc94302e0cd34974bf0b5beeb66dda1978a23d1959', witnesses: '2f49f8c981cb3e6dbcdffd6bf3a436c8d66492546c2a1984853f9c9108e8d0ec',
}, },
}; };
@@ -405,8 +405,8 @@ test('main owns one public stylesheet and the regrouped production CSS is determ
assert.equal((main.match(/import ['"][^'"]+\.css['"]/g) || []).length, 1); assert.equal((main.match(/import ['"][^'"]+\.css['"]/g) || []).length, 1);
const assets = fs.readdirSync(path.join(root, 'dist/assets')).filter((file) => file.endsWith('.css')); const assets = fs.readdirSync(path.join(root, 'dist/assets')).filter((file) => file.endsWith('.css'));
assert.deepEqual(assets, ['index-DUedU53N.css']); assert.deepEqual(assets, ['index-CE2JQAR2.css']);
const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0])); const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0]));
assert.equal(built.byteLength, 128983); assert.equal(built.byteLength, 128371);
assert.equal(sha256(built), 'eeeb9cc0bebc7afc37c2d6e60808aa4980f91c564762e47d240c654dd1b00fcf'); assert.equal(sha256(built), '0254a99e188554e0a2ae34dc9ac60109d86f9f1b6c2c96d2f8dcadb6641afe6c');
}); });