Skip to content

Commit dd6ef5e

Browse files
committed
fix(auth): preserve navigation and desktop layout in redesign
1 parent 1b6dd29 commit dd6ef5e

5 files changed

Lines changed: 50 additions & 25 deletions

File tree

‎apps/desktop/e2e/smoke.spec.ts‎

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -178,23 +178,20 @@ test.describe('desktop shell smoke', () => {
178178
const window = await app.firstWindow()
179179
await window.waitForSelector('#retry', { timeout: 30_000 })
180180
expect(window.url()).toMatch(/^sim-shell:\/\/pages\/offline\.html\?/)
181-
await expect(window.getByRole('img', { name: 'Sim', exact: true })).toBeVisible()
182-
await expect(window.getByRole('img', { name: 'Sim', exact: true })).toHaveAttribute(
183-
'aria-label',
184-
'Sim'
185-
)
181+
const home = window.getByRole('link', { name: 'Home', exact: true })
182+
await expect(home).toBeVisible()
183+
await expect(home).toHaveAttribute('href', 'https://sim.ai')
186184
await expect(window.locator('#title')).toHaveText('Can’t connect to Sim')
187-
// The recovery path for a self-hosted shell pointed at a server it cannot
188-
// reach. Exercised end to end here because it is the only coverage of the
189-
// `server:` local-page IPC gate: the bundled page reads the configuration
190-
// over the real preload bridge, and status.sim.ai is withheld because this
191-
// origin is not one of Sim's own. `toBeHidden` is load-bearing — the page's
192-
// own `button { display: inline-flex }` outranks the UA `[hidden]` rule, so
193-
// the attribute alone does not hide it.
185+
// The real preload bridge must retain server recovery while hiding cloud-only status.
194186
await expect(window.locator('#server')).toBeVisible()
195187
await expect(window.locator('#status')).toBeHidden()
196188
await expect
197-
.poll(() => window.evaluate(() => document.fonts.check('16px "Season Sans"')))
189+
.poll(() =>
190+
window.evaluate(async () => {
191+
const fonts = await document.fonts.load('14px "Sim Auth Geist"')
192+
return fonts.length > 0 && fonts.every((font) => font.status === 'loaded')
193+
})
194+
)
198195
.toBe(true)
199196
await expect(window.locator('#detail')).toHaveAttribute('role', 'status')
200197
})
@@ -205,18 +202,25 @@ test.describe('desktop shell smoke', () => {
205202
const window = await app.firstWindow()
206203
await expect(window.locator('#server')).toBeVisible()
207204
await expect(window.locator('html')).toHaveAttribute('data-sim-desktop-title-bar', 'inset')
205+
const titlebar = window.locator('[data-auth-titlebar]')
206+
await expect
207+
.poll(() => titlebar.evaluate((element) => element.getBoundingClientRect().height))
208+
.toBeGreaterThanOrEqual(38)
208209
await app.evaluate(({ BrowserWindow }) => {
209210
BrowserWindow.getAllWindows()[0].setFullScreen(true)
210211
})
211212
await expect(window.locator('html')).toHaveAttribute('data-sim-desktop-title-bar', 'fullscreen')
212213
await window.reload()
213214
await expect(window.locator('#server')).toBeVisible()
214215
await expect(window.locator('html')).toHaveAttribute('data-sim-desktop-title-bar', 'fullscreen')
215-
await expect(window.locator('.desktop-title-bar-page')).toHaveCSS('padding-top', '0px')
216+
await expect(titlebar).toHaveCSS('height', '0px')
216217
await app.evaluate(({ BrowserWindow }) => {
217218
BrowserWindow.getAllWindows()[0].setFullScreen(false)
218219
})
219220
await expect(window.locator('html')).toHaveAttribute('data-sim-desktop-title-bar', 'inset')
221+
await expect
222+
.poll(() => titlebar.evaluate((element) => element.getBoundingClientRect().height))
223+
.toBeGreaterThanOrEqual(38)
220224
})
221225

222226
test('bundled dialogs follow the app theme independently of the system theme', async () => {
@@ -310,7 +314,7 @@ test.describe('desktop shell smoke', () => {
310314
// Its page, the pre-filled value (which crosses the local-page IPC gate) and
311315
// Escape are asserted together because the packaged build once opened it as
312316
// a blank sheet with no way out.
313-
test('the offline server picker renders EMCN controls and handles validation and dismissal', async () => {
317+
test('the offline server picker handles validation and dismissal', async () => {
314318
const testInfo = test.info()
315319
app = await launchApp('http://127.0.0.1:1')
316320
const window = await app.firstWindow()
@@ -333,17 +337,21 @@ test.describe('desktop shell smoke', () => {
333337
.toBe(true)
334338
await picker.getByLabel('Server URL').fill('http://example.com')
335339
await picker.getByLabel('Server URL').press('Enter')
336-
await expect(picker.getByRole('alert')).toBeVisible()
340+
await expect(picker.locator('#server-origin-errors')).toBeVisible()
341+
await expect(picker.getByLabel('Server URL')).toHaveAccessibleDescription(
342+
/Server URL must use HTTPS/
343+
)
337344
await expect(picker.getByLabel('Server URL')).toHaveAttribute('aria-invalid', 'true')
338345
await picker.getByLabel('Server URL').fill('http://127.0.0.1:1')
339-
await expect(picker.getByRole('alert')).toHaveCount(0)
346+
await expect(picker.locator('#server-origin-errors')).toHaveCount(0)
347+
await expect(picker.getByLabel('Server URL')).not.toHaveAttribute('aria-invalid', 'true')
340348
await picker.getByRole('button', { name: 'Connect', exact: true }).click()
341349
await expect(picker.getByRole('status')).toHaveText('Already connected to this server.')
342350
await expect
343351
.poll(() =>
344352
picker
345-
.locator('[data-chip-modal-body]')
346-
.evaluate((element) => element.scrollHeight <= element.clientHeight)
353+
.getByRole('dialog')
354+
.evaluate((element) => element.scrollHeight <= globalThis.innerHeight)
347355
)
348356
.toBe(true)
349357
await picker.emulateMedia({ colorScheme: 'light' })
@@ -352,7 +360,7 @@ test.describe('desktop shell smoke', () => {
352360
animations: 'disabled',
353361
})
354362
await picker.emulateMedia({ colorScheme: 'dark' })
355-
await expect(picker.locator('html')).toHaveClass('dark')
363+
await expect(picker.locator('[data-auth-root]')).toHaveCSS('color-scheme', 'light')
356364
await picker.screenshot({
357365
path: testInfo.outputPath('server-modal-dark.png'),
358366
animations: 'disabled',

‎apps/desktop/src/renderer/auth.css‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,11 @@ body {
55
background: white;
66
color-scheme: light;
77
}
8+
9+
html {
10+
--desktop-title-bar-height: 0px;
11+
}
12+
13+
html[data-sim-desktop-title-bar="inset"] {
14+
--desktop-title-bar-height: max(env(titlebar-area-height, 38px), 38px);
15+
}

‎apps/sim/app/(auth)/components/auth-navigation.tsx‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@ import { getEnv, isFalsy } from '@/lib/core/config/env'
66
import { validateCallbackUrl } from '@/lib/core/security/input-validation'
77
import { buildAuthCrossLink, resolveAuthRedirect } from '@/app/(auth)/auth-redirect'
88

9+
interface AuthNavigationProps {
10+
registrationDisabled: boolean
11+
}
12+
913
/** Preserves return and invitation context in the Auth header cross-link. */
10-
export function AuthNavigation({ registrationDisabled }: { registrationDisabled: boolean }) {
14+
export function AuthNavigation({ registrationDisabled }: AuthNavigationProps) {
1115
const pathname = usePathname()
1216
const params = useSearchParams()
1317
if (pathname !== '/login' && pathname !== '/signup') return null
14-
if (isFalsy(getEnv('NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED'))) return null
15-
if (pathname === '/login' && registrationDisabled) return null
18+
if (
19+
pathname === '/login' &&
20+
(registrationDisabled || isFalsy(getEnv('NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED')))
21+
)
22+
return null
1623
const { rawCallbackUrl: callback, isInviteFlow } = resolveAuthRedirect({
1724
redirect: params.get('redirect'),
1825
callbackUrl: params.get('callbackUrl'),

‎packages/auth-ui/src/controls.tsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,10 @@ export function AuthSelect({
372372
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
373373
event.preventDefault()
374374
move(active + (event.key === 'ArrowDown' ? 1 : -1))
375-
} else if (event.key === 'Home' || event.key === 'End') {
375+
} else if (
376+
(event.key === 'Home' || event.key === 'End') &&
377+
!(event.target instanceof HTMLInputElement)
378+
) {
376379
event.preventDefault()
377380
move(event.key === 'Home' ? 0 : available.length - 1)
378381
} else if (event.key === 'Enter' && activeValue) {

‎packages/auth-ui/src/styles.module.css‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,6 @@
663663
.root .accountTrigger:disabled {
664664
opacity: var(--auth-disabled);
665665
}
666-
/* Notifications share the menu material and remain within the Auth portal. */
667666
.root .notification {
668667
position: fixed;
669668
right: 20px;

0 commit comments

Comments
 (0)