From 8eccdd40509c522cdd80e361b6efee6be6d4f718 Mon Sep 17 00:00:00 2001 From: Dmitriy Petrov Date: Sat, 15 Aug 2026 10:34:19 +0300 Subject: [PATCH] Remove startup reveal animation --- src/shared/versions.ts | 4 ++-- src/web/components/ClientOverviewPage.tsx | 9 +------ src/web/styles/layout.css | 18 -------------- src/web/styles/themes.css | 6 ----- test/web/responsive-layout-contract.test.js | 14 +++-------- test/web/style-boundaries.test.js | 26 ++++++++++----------- 6 files changed, 19 insertions(+), 58 deletions(-) diff --git a/src/shared/versions.ts b/src/shared/versions.ts index 01161f0..d2c005b 100644 --- a/src/shared/versions.ts +++ b/src/shared/versions.ts @@ -1,6 +1,6 @@ export const HARBOR_VERSIONS = Object.freeze({ - macClient: '0.25.14', - gatewayClient: '0.26.13', + macClient: '0.25.15', + gatewayClient: '0.26.14', gatewayBackend: '0.26.5', }); diff --git a/src/web/components/ClientOverviewPage.tsx b/src/web/components/ClientOverviewPage.tsx index 75c5057..836f8f6 100644 --- a/src/web/components/ClientOverviewPage.tsx +++ b/src/web/components/ClientOverviewPage.tsx @@ -442,7 +442,6 @@ export function ClientOverviewPage({ const desiredServer = desiredProfile?.servers.find(({ id }) => id === selectedServerId); const showPower = hasSubscription; const [now, setNow] = useState(Date.now()); - const [showIntro, setShowIntro] = useState(true); const [copyFeedback, setCopyFeedback] = useState({}); const [copyAnnouncement, setCopyAnnouncement] = useState({ text: '', cycle: 0 }); const [drawerSwitchTarget, setDrawerSwitchTarget] = useState(null); @@ -580,12 +579,6 @@ export function ClientOverviewPage({ return () => clearInterval(timer); }, [isGateway, connected, state?.connection?.startedAt]); - useEffect(() => { - if (!showIntro) return undefined; - const timer = setTimeout(() => setShowIntro(false), 1200); - return () => clearTimeout(timer); - }, [showIntro]); - useEffect(() => { if (!hasSubscription) { routingFeature.forceClose(); @@ -741,7 +734,7 @@ export function ClientOverviewPage({ return (
diff --git a/src/web/styles/layout.css b/src/web/styles/layout.css index 763a686..be9cc4d 100644 --- a/src/web/styles/layout.css +++ b/src/web/styles/layout.css @@ -233,24 +233,6 @@ 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 { width: 24px; height: 24px; diff --git a/src/web/styles/themes.css b/src/web/styles/themes.css index 8fa3d12..7be52c7 100644 --- a/src/web/styles/themes.css +++ b/src/web/styles/themes.css @@ -156,12 +156,6 @@ 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-gateway > span, .harbor-mode-swap g, diff --git a/test/web/responsive-layout-contract.test.js b/test/web/responsive-layout-contract.test.js index d0b7c8c..5eca4d7 100644 --- a/test/web/responsive-layout-contract.test.js +++ b/test/web/responsive-layout-contract.test.js @@ -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' : ''\}/); }); -test('page reload plays one stable startup sequence', () => { - assert.match(component, /const \[showIntro, setShowIntro\] = useState\(true\)/); - assert.match(component, /\$\{showIntro \? ' is-intro' : ''\}/); - 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('page reload renders the accepted snapshot without a second startup reveal', () => { + assert.doesNotMatch(component, /showIntro|is-intro/); + assert.doesNotMatch(styles, /is-intro|harbor-startup-(?:brand|content)/); }); test('server rows scroll inside the subscription drawer without moving the main layout', () => { diff --git a/test/web/style-boundaries.test.js b/test/web/style-boundaries.test.js index 976c741..e1c2d1d 100644 --- a/test/web/style-boundaries.test.js +++ b/test/web/style-boundaries.test.js @@ -37,26 +37,26 @@ const expectedImports = [ const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex'); const acceptedLedger = { counts: { - cascadeEdges: 823, + cascadeEdges: 820, customProperties: 105, - declarations: 3314, + declarations: 3307, important: 0, - keyframes: 51, + keyframes: 49, media: 12, - rules: 946, + rules: 941, variableReferences: 814, }, hashes: { - cascadeEdges: '42851684f732dd5e7f04959d009648dd4a8fab85904759da16786411ab199d1a', + cascadeEdges: 'a90bd0c489fe893ba65d6b4be6a56fdde8b9c8ba5a9ce606190c6eb95f7a25b1', customProperties: 'dede9598ee62929fb29ff470897cd475ca34eb5af5dda342b09e306f3eaee887', - declarations: 'fac169725174c1303de5673abfd42b7316c2180830d66173dca8a2e449b1db5e', + declarations: '782a758059d22fe094c2a972f315f5b7273987d5240a5d4c652959fe646f96aa', duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945', duplicateSelectors: '0fa66e370695261a2a5c6a189752c07d518d80d68d3e79a3ba4bb34407893c0e', - keyframes: 'b2b826683e11dfa95cf2d01996d43a02b3a2b33672a14353ebfbee7701d4532e', - ruleDeclarationSequences: '7750298af4530d2b0f7d7b0feee21c6bd00a5f9a7ed1d6cfa368e2d9bcace26c', - selectors: 'aa487c004e9960e60982e983fcbdb3becef76b0007d3ef85a96f45d4c3d7933e', + keyframes: '9e78309512ed82b1e9f87c58aeff505dfcdb694fc30c8f54570d01dce13eb51a', + ruleDeclarationSequences: '04efee8e9ff18e320bdb1c7c6981097a8d5bdf9fab5a461403425053cba4d172', + selectors: '59d230e56506322b783962fbd2b86a1a0a3d08a3eb6ecba57f23ba20733a33c8', 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); 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])); - assert.equal(built.byteLength, 128983); - assert.equal(sha256(built), 'eeeb9cc0bebc7afc37c2d6e60808aa4980f91c564762e47d240c654dd1b00fcf'); + assert.equal(built.byteLength, 128371); + assert.equal(sha256(built), '0254a99e188554e0a2ae34dc9ac60109d86f9f1b6c2c96d2f8dcadb6641afe6c'); });