Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 53 additions & 34 deletions apps/www/public/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3100,14 +3100,15 @@ interface AnimatedThemeTogglerProps extends React.ComponentPropsWithoutRef<"butt
onThemeChange?: (theme: "light" | "dark") => void
}

function polygonCollapsed(cx: number, cy: number, vertexCount: number): string {
const pairs = Array.from(
{ length: vertexCount },
() => `${cx}px ${cy}px`
).join(", ")
function polygonCollapsed(point: string, vertexCount: number): string {
const pairs = Array.from({ length: vertexCount }, () => point).join(", ")
return `polygon(${pairs})`
}

// All coordinates are percentages of the snapshot reference box: Chrome 150
// renders absolute px clip-path coordinates on ::view-transition-new(root)
// unscaled on fractional display scales (e.g. Windows 150%) for the first
// transition after load, so px values land at the wrong position (#989).
function getThemeTransitionClipPaths(
variant: TransitionVariant,
cx: number,
Expand All @@ -3116,64 +3117,74 @@ function getThemeTransitionClipPaths(
viewportWidth: number,
viewportHeight: number
): [string, string] {
const toX = (x: number) => `${(x / viewportWidth) * 100}%`
const toY = (y: number) => `${(y / viewportHeight) * 100}%`
const point = (x: number, y: number) => `${toX(x)} ${toY(y)}`
// circle() percentage radii resolve against hypot(w, h) / sqrt(2) of the reference box.
const toRadius = (r: number) =>
`${(r / (Math.hypot(viewportWidth, viewportHeight) / Math.SQRT2)) * 100}%`

switch (variant) {
case "circle":
return [
`circle(0px at ${cx}px ${cy}px)`,
`circle(${maxRadius}px at ${cx}px ${cy}px)`,
`circle(0% at ${point(cx, cy)})`,
`circle(${toRadius(maxRadius)} at ${point(cx, cy)})`,
]
case "square": {
const halfW = Math.max(cx, viewportWidth - cx)
const halfH = Math.max(cy, viewportHeight - cy)
const halfSide = Math.max(halfW, halfH) * 1.05
const end = [
`${cx - halfSide}px ${cy - halfSide}px`,
`${cx + halfSide}px ${cy - halfSide}px`,
`${cx + halfSide}px ${cy + halfSide}px`,
`${cx - halfSide}px ${cy + halfSide}px`,
point(cx - halfSide, cy - halfSide),
point(cx + halfSide, cy - halfSide),
point(cx + halfSide, cy + halfSide),
point(cx - halfSide, cy + halfSide),
].join(", ")
return [polygonCollapsed(cx, cy, 4), `polygon(${end})`]
return [polygonCollapsed(point(cx, cy), 4), `polygon(${end})`]
}
case "triangle": {
const scale = maxRadius * 2.2
const dx = (Math.sqrt(3) / 2) * scale
const verts = [
`${cx}px ${cy - scale}px`,
`${cx + dx}px ${cy + 0.5 * scale}px`,
`${cx - dx}px ${cy + 0.5 * scale}px`,
point(cx, cy - scale),
point(cx + dx, cy + 0.5 * scale),
point(cx - dx, cy + 0.5 * scale),
].join(", ")
return [polygonCollapsed(cx, cy, 3), `polygon(${verts})`]
return [polygonCollapsed(point(cx, cy), 3), `polygon(${verts})`]
}
case "diamond": {
// Slightly larger than the view-transition circle radius so axis-aligned coverage matches the circle reveal.
const R = maxRadius * Math.SQRT2
const end = [
`${cx}px ${cy - R}px`,
`${cx + R}px ${cy}px`,
`${cx}px ${cy + R}px`,
`${cx - R}px ${cy}px`,
point(cx, cy - R),
point(cx + R, cy),
point(cx, cy + R),
point(cx - R, cy),
].join(", ")
return [polygonCollapsed(cx, cy, 4), `polygon(${end})`]
return [polygonCollapsed(point(cx, cy), 4), `polygon(${end})`]
}
case "hexagon": {
const R = maxRadius * Math.SQRT2
const verts: string[] = []
for (let i = 0; i < 6; i++) {
const a = -Math.PI / 2 + (i * Math.PI) / 3
verts.push(`${cx + R * Math.cos(a)}px ${cy + R * Math.sin(a)}px`)
verts.push(point(cx + R * Math.cos(a), cy + R * Math.sin(a)))
}
return [polygonCollapsed(cx, cy, 6), `polygon(${verts.join(", ")})`]
return [
polygonCollapsed(point(cx, cy), 6),
`polygon(${verts.join(", ")})`,
]
}
case "rectangle": {
const halfW = Math.max(cx, viewportWidth - cx)
const halfH = Math.max(cy, viewportHeight - cy)
const end = [
`${cx - halfW}px ${cy - halfH}px`,
`${cx + halfW}px ${cy - halfH}px`,
`${cx + halfW}px ${cy + halfH}px`,
`${cx - halfW}px ${cy + halfH}px`,
point(cx - halfW, cy - halfH),
point(cx + halfW, cy - halfH),
point(cx + halfW, cy + halfH),
point(cx - halfW, cy + halfH),
].join(", ")
return [polygonCollapsed(cx, cy, 4), `polygon(${end})`]
return [polygonCollapsed(point(cx, cy), 4), `polygon(${end})`]
}
case "star": {
// Small overscan so the last frames never leave a 1px seam before the transition group ends.
Expand All @@ -3184,11 +3195,17 @@ function getThemeTransitionClipPaths(
for (let i = 0; i < 5; i++) {
const outerA = -Math.PI / 2 + (i * 2 * Math.PI) / 5
verts.push(
`${cx + radius * Math.cos(outerA)}px ${cy + radius * Math.sin(outerA)}px`
point(
cx + radius * Math.cos(outerA),
cy + radius * Math.sin(outerA)
)
)
const innerA = outerA + Math.PI / 5
verts.push(
`${cx + radius * innerRatio * Math.cos(innerA)}px ${cy + radius * innerRatio * Math.sin(innerA)}px`
point(
cx + radius * innerRatio * Math.cos(innerA),
cy + radius * innerRatio * Math.sin(innerA)
)
)
}
return `polygon(${verts.join(", ")})`
Expand All @@ -3198,8 +3215,8 @@ function getThemeTransitionClipPaths(
}
default:
return [
`circle(0px at ${cx}px ${cy}px)`,
`circle(${maxRadius}px at ${cx}px ${cy}px)`,
`circle(0% at ${point(cx, cy)})`,
`circle(${toRadius(maxRadius)} at ${point(cx, cy)})`,
]
}
}
Expand Down Expand Up @@ -3247,8 +3264,10 @@ export const AnimatedThemeToggler = ({
)
return

const viewportWidth = window.visualViewport?.width ?? window.innerWidth
const viewportHeight = window.visualViewport?.height ?? window.innerHeight
// innerWidth/innerHeight (not visualViewport): percentages must resolve
// against the snapshot reference box, which includes classic scrollbars.
const viewportWidth = window.innerWidth
const viewportHeight = window.innerHeight

let x: number
let y: number
Expand Down
Loading
Loading