import { hasFeatureInteraction, type FeatureInteractionId, type FeatureInteractionState } from './feature-interactions' export type FeatureTipId = 'voice-dictation' | 'orca-cli' | 'cmd-j-palette' export type FeatureTipPriority = 'new' | 'unseen' export type FeatureTipAction = 'enable-voice' | 'setup-cli' | 'learn-cmd-j-palette' export type FeatureTip = { id: FeatureTipId priority: FeatureTipPriority eyebrow: string title: string description: string action: FeatureTipAction ctaLabel: string /** Feature interactions that mean this tip is no longer useful to show. */ completedByFeatureInteractions?: readonly FeatureInteractionId[] } export type CompletedFeatureTipState = { cliInstalled: boolean voiceDictationEnabled: boolean featureInteractions?: FeatureInteractionState } export const FEATURE_TIPS = [ { id: 'orca-cli', priority: 'new', eyebrow: 'Tip', title: 'Let agents drive Orca with the Orca CLI', description: 'Enable agents to coordinate child worktrees and communicate between worktrees.', action: 'setup-cli', ctaLabel: 'Install CLI & Skills', completedByFeatureInteractions: [] }, { id: 'cmd-j-palette', priority: 'new', eyebrow: 'Tip', // Why: "" is a placeholder token; the cmd-j dialog splits the // title on it and inlines the live, platform-correct keybinding as a . title: 'Jump to a worktree with ', description: 'Search worktrees, switch tabs, tweak settings, or spin up a new worktree, all without leaving the keyboard.', action: 'learn-cmd-j-palette', ctaLabel: 'Got it', completedByFeatureInteractions: [] }, { id: 'voice-dictation', priority: 'unseen', eyebrow: 'Tip', title: 'Voice Dictation is here', description: 'Speak into any focused pane and Orca will transcribe it. Press the dictation shortcut to start and stop.', action: 'enable-voice', ctaLabel: 'Set Up Voice', completedByFeatureInteractions: ['voice-dictation'] } ] as const satisfies readonly FeatureTip[] export const FEATURE_TIP_IDS = FEATURE_TIPS.map((tip) => tip.id) export function isFeatureTipId(value: unknown): value is FeatureTipId { return typeof value === 'string' && FEATURE_TIP_IDS.includes(value as FeatureTipId) } export function normalizeFeatureTipIds(value: unknown): FeatureTipId[] { if (!Array.isArray(value)) { return [] } const seen = new Set() for (const item of value) { if (isFeatureTipId(item)) { seen.add(item) } } return [...seen] } export function getCompletedFeatureTipIds(state: CompletedFeatureTipState): Set { const completedIds = new Set() if (state.cliInstalled) { completedIds.add('orca-cli') } if (state.voiceDictationEnabled) { completedIds.add('voice-dictation') } for (const tip of FEATURE_TIPS) { if ( tip.completedByFeatureInteractions?.some((id) => hasFeatureInteraction(state.featureInteractions, id) ) ) { completedIds.add(tip.id) } } return completedIds } export function getOrderedUnseenFeatureTips(args: { seenTipIds: ReadonlySet completedTipIds?: ReadonlySet }): FeatureTip[] { const completedTipIds = args.completedTipIds ?? new Set() const unseenTips = FEATURE_TIPS.filter( (tip) => !args.seenTipIds.has(tip.id) && !completedTipIds.has(tip.id) ) return [ ...unseenTips.filter((tip) => tip.priority === 'new'), ...unseenTips.filter((tip) => tip.priority !== 'new') ] }