import React, { useState } from 'react' import { type OptionWithDescription, Select, } from '../../components/CustomSelect/select.js' import { Dialog } from '@anthropic/ink' import { Box, Text } from '@anthropic/ink' import { useAppState } from '../../state/AppState.js' import { isClaudeAISubscriber } from '../../utils/auth.js' import { openBrowser } from '../../utils/browser.js' import { CLAUDE_IN_CHROME_MCP_SERVER_NAME, openInChrome, } from '../../utils/claudeInChrome/common.js' import { isChromeExtensionInstalled } from '../../utils/claudeInChrome/setup.js' import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' import { env } from '../../utils/env.js' import { isRunningOnHomespace } from '../../utils/envUtils.js' const CHROME_EXTENSION_URL = 'https://costrict.ai/chrome' const CHROME_PERMISSIONS_URL = 'https://clau.de/chrome/permissions' const CHROME_RECONNECT_URL = 'https://clau.de/chrome/reconnect' type MenuAction = | 'install-extension' | 'reconnect' | 'manage-permissions' | 'toggle-default' type Props = { onDone: (result?: string) => void isExtensionInstalled: boolean configEnabled: boolean | undefined isClaudeAISubscriber: boolean isWSL: boolean } function ClaudeInChromeMenu({ onDone, isExtensionInstalled: installed, configEnabled, isClaudeAISubscriber, isWSL, }: Props): React.ReactNode { const mcpClients = useAppState(s => s.mcp.clients) const [selectKey, setSelectKey] = useState(0) const [enabledByDefault, setEnabledByDefault] = useState( configEnabled ?? false, ) const [showInstallHint, setShowInstallHint] = useState(false) const [isExtensionInstalled, setIsExtensionInstalled] = useState(installed) const isHomespace = process.env.USER_TYPE === 'ant' && isRunningOnHomespace() const chromeClient = mcpClients.find( c => c.name === CLAUDE_IN_CHROME_MCP_SERVER_NAME, ) const isConnected = chromeClient?.type === 'connected' function openUrl(url: string): void { if (isHomespace) { void openBrowser(url) } else { void openInChrome(url) } } function handleAction(action: MenuAction): void { switch (action) { case 'install-extension': setSelectKey(k => k + 1) setShowInstallHint(true) openUrl(CHROME_EXTENSION_URL) break case 'reconnect': setSelectKey(k => k + 1) void isChromeExtensionInstalled().then(installed => { setIsExtensionInstalled(installed) if (installed) { setShowInstallHint(false) } }) openUrl(CHROME_RECONNECT_URL) break case 'manage-permissions': setSelectKey(k => k + 1) openUrl(CHROME_PERMISSIONS_URL) break case 'toggle-default': { const newValue = !enabledByDefault saveGlobalConfig(current => ({ ...current, claudeInChromeDefaultEnabled: newValue, })) setEnabledByDefault(newValue) break } } } const options: OptionWithDescription[] = [] const requiresExtensionSuffix = isExtensionInstalled ? '' : ' (requires extension)' if (!isExtensionInstalled && !isHomespace) { options.push({ label: 'Install Chrome extension', value: 'install-extension', }) } options.push( { label: ( <> Manage permissions {requiresExtensionSuffix} ), value: 'manage-permissions', }, { label: ( <> Reconnect extension {requiresExtensionSuffix} ), value: 'reconnect', }, { label: `Enabled by default: ${enabledByDefault ? 'Yes' : 'No'}`, value: 'toggle-default', }, ) const isDisabled = isWSL || ((process.env.USER_TYPE as string) !== 'ant' && !isClaudeAISubscriber) return ( onDone()} color="chromeYellow" > Claude in Chrome works with the Chrome extension to let you control your browser directly from CoStrict. Navigate websites, fill forms, capture screenshots, record GIFs, and debug with console logs and network requests. {isWSL && ( Claude in Chrome is not supported in WSL at this time. )} {(process.env.USER_TYPE as string) !== 'ant' && !isClaudeAISubscriber && ( Claude in Chrome requires a costrict.ai subscription. )} {!isDisabled && ( <> {!isHomespace && ( Status:{' '} {isConnected ? ( Enabled ) : ( Disabled )} Extension:{' '} {isExtensionInstalled ? ( Installed ) : ( Not detected )} )}