Skip to content
Open
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions frontend/src/app/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe('AppRoutes authentication boundary', () => {
)
})

it('redirects a guest before rendering a protected product page and preserves its return path', async () => {
it('redirects a guest before rendering Quick Start and preserves its return path', async () => {
render(
<GuestAuthSession>
<MemoryRouter initialEntries={['/quick-start?draft=1#setup']}>
Expand All @@ -104,7 +104,7 @@ describe('AppRoutes authentication boundary', () => {
'/?account=login&returnTo=%2Fquick-start%3Fdraft%3D1%23setup',
),
)
expect(screen.queryByRole('heading', { name: /开始一条可追踪的制作流程/ })).toBeNull()
expect(screen.queryByRole('heading', { name: '想做一个什么角色?' })).toBeNull()
})

it('redirects a guest from the PlayTest entry and preserves that return path', async () => {
Expand Down Expand Up @@ -161,13 +161,13 @@ describe('AppRoutes authentication boundary', () => {
</AuthSessionProvider>,
)

expect(screen.queryByRole('heading', { name: /开始一条可追踪的制作流程/ })).toBeNull()
expect(screen.queryByRole('heading', { name: '想做一个什么角色?' })).toBeNull()
expect(screen.getByTestId('location').textContent).toBe('/quick-start')

const restoredTokens = await baseApis.refresh('stored-refresh-token')
await act(async () => resolveRefresh(restoredTokens))

expect(await screen.findByRole('heading', { name: /开始一条可追踪的制作流程/ })).toBeTruthy()
expect(await screen.findByRole('heading', { name: '想做一个什么角色?' })).toBeTruthy()
})

it('renders protected product pages for an authenticated session', async () => {
Expand All @@ -179,7 +179,7 @@ describe('AppRoutes authentication boundary', () => {
</AuthenticatedAuthSession>,
)

expect(await screen.findByRole('heading', { name: /开始一条可追踪的制作流程/ })).toBeTruthy()
expect(await screen.findByRole('heading', { name: '想做一个什么角色?' })).toBeTruthy()
})

it('tells the user when restoring the session fails instead of becoming a silent guest', async () => {
Expand Down
702 changes: 661 additions & 41 deletions frontend/src/pages/quick-start/index.test.tsx

Large diffs are not rendered by default.

1,268 changes: 808 additions & 460 deletions frontend/src/pages/quick-start/index.tsx

Large diffs are not rendered by default.

176 changes: 176 additions & 0 deletions frontend/src/pages/quick-start/kinetic-copy-cycle.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
.kinetic-copy-cycle {
display: grid;
place-items: center;
}

.quick-start-agent-copy {
place-items: start;
text-align: left;
}

.quick-start-agent-copy .kinetic-copy-line-inner-prefixed {
justify-content: flex-start;
}

.kinetic-copy-line {
display: block;
overflow: hidden;
min-height: 1.55em;
}

.kinetic-copy-line-inner {
display: block;
opacity: 0;
letter-spacing: 0.075em;
transform: translate3d(0, 120%, 0);
animation: kinetic-copy-line-enter 620ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
animation-delay: calc(210ms + var(--kinetic-copy-line-index) * 90ms);
backface-visibility: hidden;
will-change: opacity, transform, letter-spacing;
}

.kinetic-copy-line-inner-prefixed {
display: flex;
align-items: center;
justify-content: center;
}

.kinetic-copy-cycle-resting .kinetic-copy-line-inner {
opacity: 1;
letter-spacing: -0.01em;
transform: translate3d(0, 0, 0);
animation: none;
}

.kinetic-copy-cycle-exiting .kinetic-copy-line-inner {
opacity: 1;
letter-spacing: -0.01em;
transform: translate3d(0, 0, 0);
animation: kinetic-copy-line-exit 400ms cubic-bezier(0.55, 0, 1, 0.45) forwards;
animation-delay: calc(var(--kinetic-copy-line-reverse-index) * 55ms);
}

.kinetic-copy-prefix {
display: inline-block;
}

.kinetic-copy-character {
display: inline-block;
}

.kinetic-copy-cycle-entering[data-copy-motion-mode='characters'] .kinetic-copy-line-inner {
opacity: 1;
letter-spacing: -0.01em;
transform: translate3d(0, 0, 0);
animation: none;
}

.kinetic-copy-cycle-entering[data-copy-motion-mode='characters'] .kinetic-copy-prefix {
opacity: 0;
transform: translate3d(0, 0.38em, 0);
filter: blur(4px);
animation: kinetic-copy-character-enter 520ms cubic-bezier(0.16, 1, 0.3, 1) 90ms forwards;
}

.kinetic-copy-cycle-entering[data-copy-motion-mode='characters'] .kinetic-copy-character {
opacity: 0;
transform: translate3d(0, 0.42em, 0);
filter: blur(4px);
animation: kinetic-copy-character-enter 520ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
animation-delay: calc(100ms + min(var(--kinetic-copy-character-index) * 24ms, 340ms));
will-change: opacity, transform, filter;
}

.kinetic-copy-cycle-exiting[data-copy-motion-mode='characters'] .kinetic-copy-line-inner {
opacity: 1;
letter-spacing: -0.01em;
transform: translate3d(0, -0.16em, 0);
animation: none;
transition: transform 420ms cubic-bezier(0.55, 0, 1, 0.45);
}

.kinetic-copy-cycle-exiting[data-copy-motion-mode='characters'] .kinetic-copy-prefix {
opacity: 1;
animation: kinetic-copy-character-exit 300ms cubic-bezier(0.55, 0, 1, 0.45) 140ms forwards;
}

.kinetic-copy-cycle-exiting[data-copy-motion-mode='characters'] .kinetic-copy-character {
opacity: 1;
transform: translate3d(0, 0, 0);
filter: blur(0);
animation: kinetic-copy-character-exit 300ms cubic-bezier(0.55, 0, 1, 0.45) forwards;
animation-delay: calc(min(var(--kinetic-copy-character-reverse-index) * 10ms, 140ms));
will-change: opacity, transform, filter;
}

@keyframes kinetic-copy-line-enter {
0% {
opacity: 0;
letter-spacing: 0.075em;
transform: translate3d(0, 120%, 0);
}
100% {
opacity: 1;
letter-spacing: -0.01em;
transform: translate3d(0, 0, 0);
}
}

@keyframes kinetic-copy-line-exit {
0% {
opacity: 1;
letter-spacing: -0.01em;
transform: translate3d(0, 0, 0);
}
46% {
opacity: 1;
}
100% {
opacity: 0;
letter-spacing: 0.045em;
transform: translate3d(0, -118%, 0);
}
}

@keyframes kinetic-copy-character-enter {
0% {
opacity: 0;
transform: translate3d(0, 0.42em, 0);
filter: blur(4px);
}
100% {
opacity: 1;
transform: translate3d(0, 0, 0);
filter: blur(0);
}
}

@keyframes kinetic-copy-character-exit {
0% {
opacity: 1;
transform: translate3d(0, 0, 0);
filter: blur(0);
}
100% {
opacity: 0;
transform: translate3d(0, -0.42em, 0);
filter: blur(4px);
}
}

@media (prefers-reduced-motion: reduce) {
.kinetic-copy-line-inner {
opacity: 1;
letter-spacing: -0.01em;
transform: none;
animation: none;
}

.kinetic-copy-prefix,
.kinetic-copy-character {
opacity: 1;
transform: none;
filter: none;
animation: none;
}
}
Loading
Loading