Repository navigation
Fix Linear skill-install modal CTA hierarchy - #9643
Conversation
Remove the redundant 'Not now' button from the Linear agent-skill install modal and make Install the filled primary action, matching the other setup surfaces (filled primary + muted dismiss). 'Not now' was doing exactly what the dialog's × already does (session snooze via onOpenChange), but styled as an outline button in the footer-right CTA slot it read as the primary action over Install — which itself was only an outline button buried in the panel body. Users reflexively clicked the dismiss. - Drop 'Not now' + its unused onSnoozeForSession prop / i18n key (all locales) - Add opt-in installVariant to AgentSkillSetupPanel (defaults 'outline'; settings surfaces unchanged) and pass 'default' from the Linear modal - Update tests to drive the session snooze via the dialog ×
📝 WalkthroughWalkthroughThe installer setup panel now accepts an optional button variant, defaults to 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Electron QA — visible modal renderLaunched the dev build on this branch ( Dialog close-up: Full app window: What the render confirms
DOM assertion from the running app: {"buttons":[{"text":"Install CLI & Skill","variant":"default"},{"text":"Re-check","variant":"ghost"},{"text":"Don't show again","variant":"ghost"},{"text":"Close","variant":null}],"hasNotNow":false,"hasDontShow":true,"hasInstall":true}Honesty note on methodThis dev profile had no Linear-linked worktree, and the modal is gated on |
…ted link
This modal auto-opens unsolicited (first activation of a Linear-linked
worktree with the skill missing), so the reflexive dismiss must be
non-destructive. Removing 'Not now' left the permanent 'Don't show again'
as the only labeled footer button — the reflexive target — which would
nuke a useful prompt forever.
Restore 'Not now' (session snooze) as a quiet ghost — the easy, safe
dismiss, matching Neil's original ask ('not now is fine, just not the
CTA'). Demote 'Don't show again' to a muted text link so permanent
suppression takes intent. Install stays the filled primary.
Restores onSnoozeForSession wiring + notNow i18n key (all locales);
tests exercise the Not now snooze path again.
Design update — safe reflexive dismiss (revised from the first pass)Since this modal auto-opens unsolicited, the earlier "just remove Not now" pass had a side effect: it left permanent "Don't show again" as the only labeled footer button — i.e. the reflexive dismiss became the destructive one, nuking a useful prompt forever. Revised layout (verified live in the running app):
Live DOM assertion: {"buttons":[{"text":"Install CLI & Skill","variant":"default"},{"text":"Re-check","variant":"ghost"},{"text":"Don't show again","variant":"link"},{"text":"Not now","variant":"ghost"},{"text":"Close","variant":null}]}Method note: this dev profile had no Linear-linked worktree, so I mounted the real, unmodified |
…ismiss Per product call: the modal is typically reached by clicking 'Set up' on the reminder toast (an opt-in), and the dialog's × already dismisses for the session. Drop the redundant 'Not now' button. Footer keeps only the quiet muted 'Don't show again' (permanent opt-out); Install stays the filled primary. Removes onSnoozeForSession wiring + notNow i18n key (all locales); tests exercise the × (Close) session-snooze path.
Final: "Not now" removedLanded on removing "Not now" entirely — the modal is normally opened by clicking "Set up" on the reminder toast (opt-in), and the dialog × already covers session dismiss, so it was redundant. Live DOM from the running app: {"buttons":[{"text":"Install CLI & Skill","variant":"default"},{"text":"Re-check","variant":"ghost"},{"text":"Don't show again","variant":"link"},{"text":"Close","variant":null}],"hasNotNow":false}
Supersedes the earlier "keep Not now" comment. |
…GuideModal) Replaces the footer 'Don't show again' link with a quiet EyeOff icon button beside the dialog ×, matching the house pattern for hiding a setup/teaching surface (SetupGuideModal, StatusBarUsageEmptyCta, SetupGuideSidebarEntry). The footer is gone entirely, so Install is the only labeled action; the two 'make it go away' controls (× = for now, EyeOff = never) sit together and neither is a tempting text button. - Rendered last in the branch so initial dialog focus lands on Install, not the hide button (Enter-on-open must not permanently dismiss). - Wrapped in a local TooltipProvider so it works outside the app's global provider (isolated tests/mounts). - Reuses the existing dontShowAgain i18n key for aria-label + tooltip. - Tests locate the control by aria-label now that it has no text.
Update — permanent opt-out is now an EyeOff icon (no footer)Replaced the muted "Don't show again" footer link with a quiet EyeOff icon button next to the ×, matching the house pattern for hiding a setup surface (
Live-verified in the app: |
The icon-xs button is 24px tall vs the bare 16px × close, so its centered icon sat 2px low. top-3 lines the two icon centers up (measured pixel-exact in the running app).
* Fix Linear skill-install modal CTA hierarchy
Remove the redundant 'Not now' button from the Linear agent-skill
install modal and make Install the filled primary action, matching the
other setup surfaces (filled primary + muted dismiss).
'Not now' was doing exactly what the dialog's × already does (session
snooze via onOpenChange), but styled as an outline button in the
footer-right CTA slot it read as the primary action over Install —
which itself was only an outline button buried in the panel body. Users
reflexively clicked the dismiss.
- Drop 'Not now' + its unused onSnoozeForSession prop / i18n key (all locales)
- Add opt-in installVariant to AgentSkillSetupPanel (defaults 'outline';
settings surfaces unchanged) and pass 'default' from the Linear modal
- Update tests to drive the session snooze via the dialog ×
* Keep 'Not now' as the safe dismiss; demote 'Don't show again' to a muted link
This modal auto-opens unsolicited (first activation of a Linear-linked
worktree with the skill missing), so the reflexive dismiss must be
non-destructive. Removing 'Not now' left the permanent 'Don't show again'
as the only labeled footer button — the reflexive target — which would
nuke a useful prompt forever.
Restore 'Not now' (session snooze) as a quiet ghost — the easy, safe
dismiss, matching Neil's original ask ('not now is fine, just not the
CTA'). Demote 'Don't show again' to a muted text link so permanent
suppression takes intent. Install stays the filled primary.
Restores onSnoozeForSession wiring + notNow i18n key (all locales);
tests exercise the Not now snooze path again.
* Remove 'Not now' from Linear skill-install modal; × handles session dismiss
Per product call: the modal is typically reached by clicking 'Set up' on
the reminder toast (an opt-in), and the dialog's × already dismisses for
the session. Drop the redundant 'Not now' button. Footer keeps only the
quiet muted 'Don't show again' (permanent opt-out); Install stays the
filled primary.
Removes onSnoozeForSession wiring + notNow i18n key (all locales); tests
exercise the × (Close) session-snooze path.
* Move permanent opt-out to an EyeOff icon next to the × (matches SetupGuideModal)
Replaces the footer 'Don't show again' link with a quiet EyeOff icon
button beside the dialog ×, matching the house pattern for hiding a
setup/teaching surface (SetupGuideModal, StatusBarUsageEmptyCta,
SetupGuideSidebarEntry). The footer is gone entirely, so Install is the
only labeled action; the two 'make it go away' controls (× = for now,
EyeOff = never) sit together and neither is a tempting text button.
- Rendered last in the branch so initial dialog focus lands on Install,
not the hide button (Enter-on-open must not permanently dismiss).
- Wrapped in a local TooltipProvider so it works outside the app's global
provider (isolated tests/mounts).
- Reuses the existing dontShowAgain i18n key for aria-label + tooltip.
- Tests locate the control by aria-label now that it has no text.
* Align EyeOff hide icon with the × (top-3 not top-3.5)
The icon-xs button is 24px tall vs the bare 16px × close, so its centered
icon sat 2px low. top-3 lines the two icon centers up (measured pixel-exact
in the running app).







What
Fixes the action hierarchy of the Linear agent-skill install modal. Neil + Brennan flagged that the dismiss button read as the primary CTA and got reflexively clicked.
Changes
default). Added an opt-ininstallVariantprop toAgentSkillSetupPanel(defaults tooutline, so settings surfaces are untouched); the Linear modal passesdefault.EyeOfficon next to the ×, matching the house pattern for hiding a setup/teaching surface (SetupGuideModal,StatusBarUsageEmptyCta,SetupGuideSidebarEntry). The two "make it go away" controls now sit together — × = dismiss for the session, ⊘ EyeOff = don't show again — and neither is a tempting text button.TooltipProviderso it works in isolated mounts/tests.Testing
LinearAgentSkillSetupPrompt.test.tsx: 27/27 pass (× → session snooze; EyeOff → permanent dismiss, located by aria-label).default, EyeOff present (aria "Don't show again"), no footer, and initial focus = Install (not the hide button). Screenshot above.tsc(web) + oxlint clean; all 5 locale JSONs validate.Note: the modal also auto-opens on the first activation of a Linear-linked worktree with the skill missing (not only via the reminder toast's "Set up"). For that path the × (session) and the EyeOff (permanent) cover dismissal.