Polish CLI feature tip terminal transition (#4344)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
1f1aaee78c
commit
eaae2dd4ce
|
|
@ -0,0 +1,103 @@
|
|||
import { useEffect, useState, type JSX } from 'react'
|
||||
import { AgentsOrchestrationVisual } from '@/components/feature-wall/AgentsOrchestrationVisual'
|
||||
import {
|
||||
ORCHESTRATION_CLI_COMMAND_LOOP_MS,
|
||||
ORCHESTRATION_CLI_COMMAND_TIMINGS_MS
|
||||
} from '@/components/feature-wall/agents-orchestration/orchestration-types'
|
||||
import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers'
|
||||
|
||||
const CLI_AGENT_COMMANDS = [
|
||||
'orca worktree create --name auth-pr-1',
|
||||
'orca worktree create --name auth-pr-2',
|
||||
'orca orchestration dispatch --task pr1 --to w1',
|
||||
'orca orchestration dispatch --task pr2 --to w2'
|
||||
]
|
||||
|
||||
export function CliFeatureTipVisual(): JSX.Element {
|
||||
const reducedMotion = usePrefersReducedMotion()
|
||||
const [animatedVisibleCommandCount, setAnimatedVisibleCommandCount] = useState(0)
|
||||
// Why: reduced-motion users should see the static completed state without a
|
||||
// post-render state repair; only the animated path needs timer-backed state.
|
||||
const visibleCommandCount = reducedMotion
|
||||
? CLI_AGENT_COMMANDS.length
|
||||
: animatedVisibleCommandCount
|
||||
|
||||
useEffect(() => {
|
||||
if (reducedMotion) {
|
||||
return
|
||||
}
|
||||
|
||||
let cancelled = false
|
||||
const timeouts: number[] = []
|
||||
const later = (fn: () => void, ms: number): void => {
|
||||
timeouts.push(window.setTimeout(() => !cancelled && fn(), ms))
|
||||
}
|
||||
|
||||
// Why: terminal lines mirror the orchestration tour beat timings so the
|
||||
// shell shows each command as the parent agent runs it.
|
||||
const runOnce = (): void => {
|
||||
setAnimatedVisibleCommandCount(0)
|
||||
ORCHESTRATION_CLI_COMMAND_TIMINGS_MS.forEach((ms, index) => {
|
||||
later(() => setAnimatedVisibleCommandCount(index + 1), ms)
|
||||
})
|
||||
later(runOnce, ORCHESTRATION_CLI_COMMAND_LOOP_MS)
|
||||
}
|
||||
|
||||
runOnce()
|
||||
return () => {
|
||||
cancelled = true
|
||||
timeouts.forEach((id) => window.clearTimeout(id))
|
||||
}
|
||||
}, [reducedMotion])
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative flex min-h-[27rem] flex-col overflow-hidden bg-muted/60 px-6 py-7"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="absolute inset-x-0 top-0 h-16 bg-gradient-to-b from-background/55 to-transparent" />
|
||||
<div className="relative rounded-lg border border-border/70 bg-card/95 shadow-xs">
|
||||
<div className="flex items-center gap-2 border-b border-border/70 px-3 py-2">
|
||||
<span className="size-2 rounded-full bg-muted-foreground/35" />
|
||||
<span className="size-2 rounded-full bg-muted-foreground/25" />
|
||||
<span className="size-2 rounded-full bg-muted-foreground/20" />
|
||||
</div>
|
||||
<div className="space-y-1.5 px-3 py-3 font-mono text-[10.5px] leading-[1.35] text-foreground">
|
||||
<div className="truncate text-muted-foreground">
|
||||
<span className="mr-1.5 text-foreground">●</span>Claude Code session started
|
||||
</div>
|
||||
{CLI_AGENT_COMMANDS.map((command, index) => {
|
||||
const isVisible = index < visibleCommandCount
|
||||
const isCurrentLine = isVisible && index === visibleCommandCount - 1
|
||||
return (
|
||||
<div
|
||||
key={command}
|
||||
className={`truncate ${isVisible ? 'animate-cli-tip-command-line' : 'invisible'}`}
|
||||
>
|
||||
<span className="text-foreground">> </span>
|
||||
<span>{command}</span>
|
||||
{isCurrentLine ? (
|
||||
<span className="animate-cli-tip-caret ml-0.5 inline-block h-3 w-1 translate-y-0.5 rounded-sm bg-foreground/70" />
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="cli-tip-orchestration-frame relative mt-5 flex h-[17rem] items-center justify-center overflow-hidden rounded-lg border border-border/70 bg-background/80 px-5 shadow-xs">
|
||||
<div className="origin-center">
|
||||
<AgentsOrchestrationVisual
|
||||
activeStepId="orchestration"
|
||||
reducedMotion={reducedMotion}
|
||||
widthPx={350}
|
||||
heightPx={252}
|
||||
orchestrationCreatedChildCount={Math.min(visibleCommandCount, 2)}
|
||||
orchestrationLoopMs={ORCHESTRATION_CLI_COMMAND_LOOP_MS}
|
||||
orchestrationShowResponseBeats={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import { Copy } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { OnboardingInlineCommandTerminal } from '@/components/onboarding/OnboardingInlineCommandTerminal'
|
||||
import { ORCA_CLI_ORCHESTRATION_SKILL_INSTALL_COMMAND } from '@/lib/agent-feature-install-commands'
|
||||
|
||||
export function CliSkillSetupTerminal(): React.JSX.Element {
|
||||
const handleCopySkillCommand = async (): Promise<void> => {
|
||||
try {
|
||||
await window.api.ui.writeClipboardText(ORCA_CLI_ORCHESTRATION_SKILL_INSTALL_COMMAND)
|
||||
toast.success('Copied the skill install command.')
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : 'Failed to copy skill command.')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-w-0">
|
||||
<div className="flex min-w-0 items-center gap-2 rounded-md border border-border bg-muted/35 px-3 py-2">
|
||||
<code className="scrollbar-sleek min-w-0 flex-1 overflow-x-auto whitespace-nowrap font-mono text-xs text-muted-foreground">
|
||||
{ORCA_CLI_ORCHESTRATION_SKILL_INSTALL_COMMAND}
|
||||
</code>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="shrink-0"
|
||||
onClick={() => void handleCopySkillCommand()}
|
||||
aria-label="Copy skill install command"
|
||||
>
|
||||
<Copy className="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" sideOffset={4}>
|
||||
Copy command
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<OnboardingInlineCommandTerminal
|
||||
command={ORCA_CLI_ORCHESTRATION_SKILL_INSTALL_COMMAND}
|
||||
title="Skill setup"
|
||||
ariaLabel="Orca CLI and orchestration skill install terminal"
|
||||
description="Press Enter to install the Orca CLI orchestration skill for your agents."
|
||||
terminalHeightPx={280}
|
||||
terminalTopMarginPx={8}
|
||||
descriptionPaddingClassName="px-4 py-2"
|
||||
autoScrollIntoView={false}
|
||||
worktreeId="feature-tip-cli-skills-terminal"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,17 +1,9 @@
|
|||
import { useEffect, useState, type JSX } from 'react'
|
||||
import { useEffect, useRef, useState, type JSX } from 'react'
|
||||
import { Loader2, Mic } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { getDefaultVoiceSettings } from '../../../../shared/constants'
|
||||
import type { FeatureTip } from '../../../../shared/feature-tips'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { AgentsOrchestrationVisual } from '@/components/feature-wall/AgentsOrchestrationVisual'
|
||||
import {
|
||||
ORCHESTRATION_CLI_COMMAND_LOOP_MS,
|
||||
ORCHESTRATION_CLI_COMMAND_TIMINGS_MS
|
||||
} from '@/components/feature-wall/agents-orchestration/orchestration-types'
|
||||
import { usePrefersReducedMotion } from '@/components/feature-wall/feature-wall-modal-helpers'
|
||||
import { OnboardingInlineCommandTerminal } from '@/components/onboarding/OnboardingInlineCommandTerminal'
|
||||
import { ORCA_CLI_ORCHESTRATION_SKILL_INSTALL_COMMAND } from '@/lib/agent-feature-install-commands'
|
||||
import {
|
||||
ORCHESTRATION_ENABLED_STORAGE_KEY,
|
||||
ORCHESTRATION_SETUP_DISMISSED_STORAGE_KEY,
|
||||
|
|
@ -26,6 +18,8 @@ import {
|
|||
DialogTitle
|
||||
} from '@/components/ui/dialog'
|
||||
import { useAppStore } from '@/store'
|
||||
import { CliFeatureTipVisual } from './CliFeatureTipVisual'
|
||||
import { CliSkillSetupTerminal } from './CliSkillSetupTerminal'
|
||||
import { installCliFromFeatureTip } from './feature-tip-cli-install-action'
|
||||
import { getFeatureTipForModal } from './feature-tip-modal-state'
|
||||
import {
|
||||
|
|
@ -36,12 +30,6 @@ import {
|
|||
import { useMountedRef } from '@/hooks/useMountedRef'
|
||||
|
||||
const WAVEFORM_BAR_HEIGHTS = [30, 60, 90, 70, 100, 50, 80, 35, 65]
|
||||
const CLI_AGENT_COMMANDS = [
|
||||
'orca worktree create --name auth-pr-1',
|
||||
'orca worktree create --name auth-pr-2',
|
||||
'orca orchestration dispatch --task pr1 --to w1',
|
||||
'orca orchestration dispatch --task pr2 --to w2'
|
||||
]
|
||||
|
||||
function WorktreePromptTerm({ children }: { children: string }): JSX.Element {
|
||||
return (
|
||||
|
|
@ -51,95 +39,6 @@ function WorktreePromptTerm({ children }: { children: string }): JSX.Element {
|
|||
)
|
||||
}
|
||||
|
||||
function CliFeatureTipVisual(): JSX.Element {
|
||||
const reducedMotion = usePrefersReducedMotion()
|
||||
const [animatedVisibleCommandCount, setAnimatedVisibleCommandCount] = useState(0)
|
||||
// Why: reduced-motion users should see the static completed state without a
|
||||
// post-render state repair; only the animated path needs timer-backed state.
|
||||
const visibleCommandCount = reducedMotion
|
||||
? CLI_AGENT_COMMANDS.length
|
||||
: animatedVisibleCommandCount
|
||||
|
||||
useEffect(() => {
|
||||
if (reducedMotion) {
|
||||
return
|
||||
}
|
||||
|
||||
let cancelled = false
|
||||
const timeouts: number[] = []
|
||||
const later = (fn: () => void, ms: number): void => {
|
||||
timeouts.push(window.setTimeout(() => !cancelled && fn(), ms))
|
||||
}
|
||||
|
||||
// Why: terminal lines mirror the orchestration tour beat timings so the
|
||||
// shell shows each command as the parent agent runs it.
|
||||
const runOnce = (): void => {
|
||||
setAnimatedVisibleCommandCount(0)
|
||||
ORCHESTRATION_CLI_COMMAND_TIMINGS_MS.forEach((ms, index) => {
|
||||
later(() => setAnimatedVisibleCommandCount(index + 1), ms)
|
||||
})
|
||||
later(runOnce, ORCHESTRATION_CLI_COMMAND_LOOP_MS)
|
||||
}
|
||||
|
||||
runOnce()
|
||||
return () => {
|
||||
cancelled = true
|
||||
timeouts.forEach((id) => window.clearTimeout(id))
|
||||
}
|
||||
}, [reducedMotion])
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative flex min-h-[27rem] flex-col overflow-hidden bg-muted/60 px-6 py-7"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="absolute inset-x-0 top-0 h-16 bg-gradient-to-b from-background/55 to-transparent" />
|
||||
<div className="relative rounded-lg border border-border/70 bg-card/95 shadow-xs">
|
||||
<div className="flex items-center gap-2 border-b border-border/70 px-3 py-2">
|
||||
<span className="size-2 rounded-full bg-muted-foreground/35" />
|
||||
<span className="size-2 rounded-full bg-muted-foreground/25" />
|
||||
<span className="size-2 rounded-full bg-muted-foreground/20" />
|
||||
</div>
|
||||
<div className="space-y-1.5 px-3 py-3 font-mono text-[10.5px] leading-[1.35] text-foreground">
|
||||
<div className="truncate text-muted-foreground">
|
||||
<span className="mr-1.5 text-foreground">●</span>Claude Code session started
|
||||
</div>
|
||||
{CLI_AGENT_COMMANDS.map((command, index) => {
|
||||
const isVisible = index < visibleCommandCount
|
||||
const isCurrentLine = isVisible && index === visibleCommandCount - 1
|
||||
return (
|
||||
<div
|
||||
key={command}
|
||||
className={`truncate ${isVisible ? 'animate-cli-tip-command-line' : 'invisible'}`}
|
||||
>
|
||||
<span className="text-amber-600">> </span>
|
||||
<span>{command}</span>
|
||||
{isCurrentLine ? (
|
||||
<span className="animate-cli-tip-caret ml-0.5 inline-block h-3 w-1 translate-y-0.5 rounded-sm bg-foreground/70" />
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="cli-tip-orchestration-frame relative mt-5 flex h-[17rem] items-center justify-center overflow-hidden rounded-lg border border-border/70 bg-background/80 px-5 shadow-xs">
|
||||
<div className="origin-center">
|
||||
<AgentsOrchestrationVisual
|
||||
activeStepId="orchestration"
|
||||
reducedMotion={reducedMotion}
|
||||
widthPx={350}
|
||||
heightPx={252}
|
||||
orchestrationCreatedChildCount={Math.min(visibleCommandCount, 2)}
|
||||
orchestrationLoopMs={ORCHESTRATION_CLI_COMMAND_LOOP_MS}
|
||||
orchestrationShowResponseBeats={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FeatureTipVisual({ tip }: { tip: FeatureTip }): JSX.Element {
|
||||
if (tip.action === 'setup-cli') {
|
||||
return <CliFeatureTipVisual />
|
||||
|
|
@ -219,6 +118,8 @@ export default function FeatureTipsModal(): JSX.Element | null {
|
|||
const markFeatureTipsSeen = useAppStore((s) => s.markFeatureTipsSeen)
|
||||
const modalData = useAppStore((s) => s.modalData)
|
||||
const mountedRef = useMountedRef()
|
||||
const activeModalRef = useRef(activeModal)
|
||||
const setupRequestIdRef = useRef(0)
|
||||
const [primaryBusy, setPrimaryBusy] = useState(false)
|
||||
const [skillTerminalOpen, setSkillTerminalOpen] = useState(false)
|
||||
const isOpen = activeModal === 'feature-tips'
|
||||
|
|
@ -230,6 +131,10 @@ export default function FeatureTipsModal(): JSX.Element | null {
|
|||
settings
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
activeModalRef.current = activeModal
|
||||
}, [activeModal])
|
||||
|
||||
const markCurrentTipSeen = (): void => {
|
||||
if (currentTip) {
|
||||
markFeatureTipsSeen([currentTip.id])
|
||||
|
|
@ -238,14 +143,19 @@ export default function FeatureTipsModal(): JSX.Element | null {
|
|||
|
||||
const handleOpenChange = (open: boolean): void => {
|
||||
if (!open) {
|
||||
setupRequestIdRef.current += 1
|
||||
markCurrentTipSeen()
|
||||
setSkillTerminalOpen(false)
|
||||
setPrimaryBusy(false)
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
|
||||
const handleSkip = (): void => {
|
||||
setupRequestIdRef.current += 1
|
||||
markCurrentTipSeen()
|
||||
setSkillTerminalOpen(false)
|
||||
setPrimaryBusy(false)
|
||||
closeModal()
|
||||
}
|
||||
|
||||
|
|
@ -281,6 +191,14 @@ export default function FeatureTipsModal(): JSX.Element | null {
|
|||
break
|
||||
}
|
||||
case 'setup-cli': {
|
||||
const setupRequestId = setupRequestIdRef.current + 1
|
||||
setupRequestIdRef.current = setupRequestId
|
||||
// Why: this modal is lazily mounted; closing it does not unmount the
|
||||
// component, so async install results must not reopen UI after dismissal.
|
||||
const canApplySetupResult = (): boolean =>
|
||||
mountedRef.current &&
|
||||
activeModalRef.current === 'feature-tips' &&
|
||||
setupRequestIdRef.current === setupRequestId
|
||||
const telemetrySource = getOrcaCliFeatureTipTelemetrySource(modalData.source)
|
||||
trackOrcaCliFeatureTipSetupClicked(telemetrySource)
|
||||
setPrimaryBusy(true)
|
||||
|
|
@ -288,17 +206,17 @@ export default function FeatureTipsModal(): JSX.Element | null {
|
|||
const result = await installCliFromFeatureTip(() => window.api.cli.install())
|
||||
if (result.kind === 'installed') {
|
||||
trackOrcaCliFeatureTipSetupResult(telemetrySource, 'installed')
|
||||
enableOrchestrationSkillSetup()
|
||||
if (!mountedRef.current) {
|
||||
if (!canApplySetupResult()) {
|
||||
return
|
||||
}
|
||||
enableOrchestrationSkillSetup()
|
||||
toast.success('Registered `orca` in PATH.')
|
||||
setSkillTerminalOpen(true)
|
||||
return
|
||||
}
|
||||
|
||||
trackOrcaCliFeatureTipSetupResult(telemetrySource, 'needs_attention')
|
||||
if (!mountedRef.current) {
|
||||
if (!canApplySetupResult()) {
|
||||
return
|
||||
}
|
||||
toast.warning('Orca CLI needs attention', {
|
||||
|
|
@ -313,21 +231,21 @@ export default function FeatureTipsModal(): JSX.Element | null {
|
|||
message.includes('Development mode uses a generated launcher for validation only')
|
||||
) {
|
||||
trackOrcaCliFeatureTipSetupResult(telemetrySource, 'dev_preview')
|
||||
enableOrchestrationSkillSetup()
|
||||
if (!mountedRef.current) {
|
||||
if (!canApplySetupResult()) {
|
||||
return
|
||||
}
|
||||
enableOrchestrationSkillSetup()
|
||||
toast.info('Development preview: opening skills setup terminal.')
|
||||
setSkillTerminalOpen(true)
|
||||
return
|
||||
}
|
||||
|
||||
trackOrcaCliFeatureTipSetupResult(telemetrySource, 'failed')
|
||||
if (mountedRef.current) {
|
||||
if (canApplySetupResult()) {
|
||||
toast.error(message)
|
||||
}
|
||||
} finally {
|
||||
if (mountedRef.current) {
|
||||
if (canApplySetupResult()) {
|
||||
setPrimaryBusy(false)
|
||||
}
|
||||
}
|
||||
|
|
@ -342,43 +260,47 @@ export default function FeatureTipsModal(): JSX.Element | null {
|
|||
if (currentTip.action === 'setup-cli') {
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
|
||||
<DialogContent className="grid gap-0 overflow-hidden p-0 sm:max-w-4xl md:grid-cols-[minmax(22rem,0.95fr)_minmax(26rem,1.05fr)]">
|
||||
<div className="flex min-h-[27rem] flex-col justify-between px-8 py-9">
|
||||
<DialogHeader className="gap-4 text-left">
|
||||
<div className="space-y-3">
|
||||
<DialogTitle className="max-w-[22rem] text-3xl font-semibold leading-tight tracking-tight">
|
||||
<DialogContent
|
||||
className="!flex max-h-[calc(100vh-2rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-4xl md:!h-[min(31rem,calc(100vh-2rem))] md:!flex-row"
|
||||
showCloseButton={!skillTerminalOpen}
|
||||
>
|
||||
<div
|
||||
className={`scrollbar-sleek flex min-h-0 min-w-0 flex-1 flex-col justify-between overflow-y-auto px-8 py-9 transition-[flex-basis] duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none md:shrink-0 ${
|
||||
skillTerminalOpen ? 'basis-auto md:basis-full' : 'basis-auto md:basis-[47.5%]'
|
||||
}`}
|
||||
>
|
||||
<DialogHeader className={`${skillTerminalOpen ? 'gap-2' : 'gap-4'} text-left`}>
|
||||
<div>
|
||||
<DialogTitle
|
||||
className={`text-3xl font-semibold leading-tight tracking-tight ${
|
||||
skillTerminalOpen ? 'max-w-2xl' : 'max-w-[22rem]'
|
||||
}`}
|
||||
>
|
||||
{currentTip.title}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="max-w-sm text-sm leading-relaxed">
|
||||
<DialogDescription className="mt-3 max-w-2xl text-sm leading-relaxed">
|
||||
{currentTip.description}
|
||||
</DialogDescription>
|
||||
{skillTerminalOpen ? null : (
|
||||
<div className="max-w-sm space-y-2 rounded-md border border-border/70 bg-muted/35 p-3 text-sm leading-relaxed text-muted-foreground">
|
||||
<p className="font-medium text-foreground">Try asking:</p>
|
||||
<p>
|
||||
“Split this PR into two <WorktreePromptTerm>worktrees</WorktreePromptTerm> and
|
||||
create PRs for each.”
|
||||
</p>
|
||||
<p>
|
||||
“When the agent in <WorktreePromptTerm>worktree</WorktreePromptTerm> X
|
||||
finishes, send it the review task.”
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
aria-hidden={skillTerminalOpen}
|
||||
className={`max-w-sm space-y-2 overflow-hidden rounded-md border text-sm leading-relaxed text-muted-foreground transition-[max-height,opacity,transform,margin,padding,border-color] duration-300 ease-out motion-reduce:transition-none ${
|
||||
skillTerminalOpen
|
||||
? 'pointer-events-none mt-0 max-h-0 -translate-y-2 border-transparent p-0 opacity-0'
|
||||
: 'mt-3 max-h-64 translate-y-0 border-border/70 bg-muted/35 p-3 opacity-100'
|
||||
}`}
|
||||
>
|
||||
<p className="font-medium text-foreground">Try asking:</p>
|
||||
<p>
|
||||
“Split this PR into two <WorktreePromptTerm>worktrees</WorktreePromptTerm> and
|
||||
create PRs for each.”
|
||||
</p>
|
||||
<p>
|
||||
“When the agent in <WorktreePromptTerm>worktree</WorktreePromptTerm> X finishes,
|
||||
send it the review task.”
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{skillTerminalOpen ? (
|
||||
<OnboardingInlineCommandTerminal
|
||||
command={ORCA_CLI_ORCHESTRATION_SKILL_INSTALL_COMMAND}
|
||||
title="Skill setup"
|
||||
ariaLabel="Orca CLI and orchestration skill install terminal"
|
||||
description="Press Enter to install the Orca CLI and orchestration skills for your agents."
|
||||
terminalHeightPx={150}
|
||||
terminalTopMarginPx={4}
|
||||
descriptionPaddingClassName="px-4 py-2"
|
||||
autoScrollIntoView={false}
|
||||
worktreeId="feature-tip-cli-skills-terminal"
|
||||
/>
|
||||
) : null}
|
||||
{skillTerminalOpen ? <CliSkillSetupTerminal /> : null}
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter className="mt-8 flex sm:justify-stretch">
|
||||
|
|
@ -398,7 +320,21 @@ export default function FeatureTipsModal(): JSX.Element | null {
|
|||
)}
|
||||
</DialogFooter>
|
||||
</div>
|
||||
<FeatureTipVisual tip={currentTip} />
|
||||
<div
|
||||
className={`min-h-0 min-w-0 shrink-0 overflow-hidden transition-[flex-basis,max-height] duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none ${
|
||||
skillTerminalOpen
|
||||
? 'pointer-events-none max-h-0 basis-0 md:max-h-none md:basis-0'
|
||||
: 'max-h-[40rem] basis-auto md:basis-[52.5%]'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`h-full transition-[transform,opacity] duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none md:w-[29.4rem] ${
|
||||
skillTerminalOpen ? 'translate-x-full opacity-0' : 'translate-x-0 opacity-100'
|
||||
}`}
|
||||
>
|
||||
{skillTerminalOpen ? null : <FeatureTipVisual tip={currentTip} />}
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue