From cbe924d4fe3dd6ee8265e2f7da679555a4ea909c Mon Sep 17 00:00:00 2001 From: brennanb2025 Date: Sun, 31 May 2026 21:45:13 -0700 Subject: [PATCH] Pulse create worktree tour target Co-authored-by: Orca --- src/renderer/src/assets/main.css | 43 +++++++++++++++++++ .../ContextualTourOverlay.test.tsx | 13 ++++++ .../ContextualTourOverlaySurface.tsx | 17 ++++++++ 3 files changed, 73 insertions(+) diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 7f56cd220c5..3aeafbcbf12 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -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; diff --git a/src/renderer/src/components/contextual-tours/ContextualTourOverlay.test.tsx b/src/renderer/src/components/contextual-tours/ContextualTourOverlay.test.tsx index a2215864097..1cb81709724 100644 --- a/src/renderer/src/components/contextual-tours/ContextualTourOverlay.test.tsx +++ b/src/renderer/src/components/contextual-tours/ContextualTourOverlay.test.tsx @@ -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() diff --git a/src/renderer/src/components/contextual-tours/ContextualTourOverlaySurface.tsx b/src/renderer/src/components/contextual-tours/ContextualTourOverlaySurface.tsx index 8b2215994bc..c0e956f4ca0 100644 --- a/src/renderer/src/components/contextual-tours/ContextualTourOverlaySurface.tsx +++ b/src/renderer/src/components/contextual-tours/ContextualTourOverlaySurface.tsx @@ -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 = (
+ {showTargetRings ? ( +