Pulse create worktree tour target

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
brennanb2025
2026-05-31 21:45:13 -07:00
co-authored by Orca
parent 37ffeef6bb
commit cbe924d4fe
3 changed files with 73 additions and 0 deletions
+43
View File
@@ -1735,6 +1735,49 @@ html.onboarding-tour-start-transition::view-transition-new(root) {
}
}
.orca-contextual-tour-target-rings {
border-radius: 9999px;
pointer-events: none;
}
.orca-contextual-tour-target-rings::before,
.orca-contextual-tour-target-rings::after {
content: '';
position: absolute;
inset: -2px;
border: 1px solid color-mix(in srgb, var(--ring) 72%, transparent);
border-radius: inherit;
box-shadow: 0 0 0 1px color-mix(in srgb, var(--ring) 18%, transparent);
opacity: 0;
transform-origin: center;
animation: contextual-tour-target-ring 1.8s ease-out infinite;
}
.orca-contextual-tour-target-rings::after {
animation-delay: 0.45s;
}
@keyframes contextual-tour-target-ring {
0% {
opacity: 0.72;
transform: scale(1);
}
72%,
100% {
opacity: 0;
transform: scale(2.4);
}
}
@media (prefers-reduced-motion: reduce) {
.orca-contextual-tour-target-rings::before,
.orca-contextual-tour-target-rings::after {
animation: none;
opacity: 0.72;
transform: scale(1.28);
}
}
.feature-tour-copy-slide {
position: absolute;
inset: 0;
@@ -245,6 +245,19 @@ describe('ContextualTourOverlaySurface', () => {
expect(onStepAction).toHaveBeenCalledWith(secondaryAction)
})
it('renders target rings for the create-worktree step only', () => {
const createWorktreeMarkup = renderToStaticMarkup(
renderSurface({
primaryAction: { kind: 'create-worktree', label: 'Create worktree' },
title: 'Run another task in parallel'
})
)
const defaultMarkup = renderToStaticMarkup(renderSurface())
expect(createWorktreeMarkup).toContain('data-contextual-tour-target-rings')
expect(defaultMarkup).not.toContain('data-contextual-tour-target-rings')
})
it('handles Escape by clicking Skip before page-level handlers see it', () => {
const click = vi.fn()
const preventDefault = vi.fn()
@@ -101,6 +101,15 @@ export function ContextualTourOverlaySurface({
kind: renderState.isLastStep ? 'complete' : 'next',
label: renderState.isLastStep ? 'Done' : 'Next'
} satisfies ContextualTourStepAction)
const showTargetRings = primaryAction.kind === 'create-worktree'
const targetRingStyle = showTargetRings
? ({
left: renderState.rect.left,
top: renderState.rect.top,
width: renderState.rect.width,
height: renderState.rect.height
} satisfies CSSProperties)
: undefined
const panel = (
<section
@@ -183,6 +192,14 @@ export function ContextualTourOverlaySurface({
role="presentation"
onKeyDownCapture={onOverlayKeyDownCapture}
>
{showTargetRings ? (
<div
aria-hidden="true"
className="orca-contextual-tour-target-rings fixed z-[75]"
data-contextual-tour-target-rings=""
style={targetRingStyle}
/>
) : null}
<div className="pointer-events-auto">
{panelHost ? createPortal(panel, panelHost) : panel}
</div>