Files
hanzo.ai/e2e/pin.spec.ts
zeekayandHanzo Dev 9bbdc64ee0 One /dev: the builder is platform.hanzo.ai/dev
This export carried a second copy of the builder at /dev — the composer,
a run's transcript and a project pane (app/(app)/dev and
components/workspace/{Build,Session,project}.tsx), plus DevStudio.tsx and
enter.ts, which nothing else used. They are deleted, and dev.hanzo.ai is no
longer published from this export.

Every link to the builder is DEV or dev(ref) from lib/host.ts. dev() carries
a run id or a project slug into the path only when it is exactly one, so no
caller can make it name another address. The shell's Dev tile, Projects and
Runs open it there; /code forwards there. The edge's permanent redirects for
hanzo.ai/dev, /code, dev.hanzo.ai and hanzo.app live in universe.

Gates and specs that drove the old /dev drive /chat where they tested the
shared shell, and are removed where the builder itself was the subject.



Co-authored-by: Hanzo Dev <[email protected]>
2026-09-24 15:53:59 -07:00

56 lines
2.0 KiB
TypeScript

import { expect, test } from '@playwright/test'
import { signIn } from './session'
import { sidebar } from './app'
/**
* A PINNED SIDEBAR MAKES ROOM; A PEEKED ONE FLOATS.
*
* The sheet appears under the pointer and goes again, so peeking must not move
* the page. But a reader who PINS it means to keep it while they work, and
* floating it there hides the room's left edge behind it — measured on
* production at 1440 with both columns open, the composer read "Hanzo to build…"
* and the heading had lost its first word.
*
* So this asserts the room's own box, not a style: pinned, nothing overlaps it.
*/
async function room(page: import('@playwright/test').Page) {
return page.evaluate(() => {
const m = document.querySelector('main')!.getBoundingClientRect()
const sheet = document.querySelector('[data-slot="sidebar"]')?.getBoundingClientRect()
return {
roomLeft: Math.round(m.x),
sheetRight: sheet && sheet.width > 0 ? Math.round(sheet.right) : 0,
}
})
}
test('pinned, the sidebar sits beside the room rather than over it', async ({ page }) => {
await signIn(page)
await page.setViewportSize({ width: 1440, height: 900 })
await page.goto('/chat')
await page.waitForLoadState('networkidle')
await sidebar(page)
await expect
.poll(async () => (await room(page)).sheetRight, { timeout: 15_000 })
.toBeGreaterThan(0)
const { roomLeft, sheetRight } = await room(page)
expect(roomLeft, 'the room begins where the sheet ends').toBeGreaterThanOrEqual(sheetRight - 1)
})
test('a phone keeps the overlay, because there is no width to give a column', async ({ page }) => {
await signIn(page)
await page.setViewportSize({ width: 390, height: 844 })
await page.goto('/chat')
await page.waitForLoadState('networkidle')
await sidebar(page)
await expect
.poll(async () => (await room(page)).sheetRight, { timeout: 15_000 })
.toBeGreaterThan(0)
const { roomLeft, sheetRight } = await room(page)
expect(roomLeft, 'the room stays put and the sheet covers it').toBeLessThan(sheetRight)
})