refactor(integrations): retire unavailable options
Remove retired Command Code, Discord, and Telegram integration entries, search targets, and documentation. Keep Command Code provider usage and logo support available through normalized provider ID aliases.
This commit is contained in:
@@ -1,74 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Icon } from '@/components/icon/Icon';
|
||||
import { SettingsSection } from '@/components/sections/shared/SettingsSection';
|
||||
import type { IconName } from '@/components/icon/icons';
|
||||
import { useI18n, type I18nKey } from '@/lib/i18n';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
type ComingSoonMessenger = {
|
||||
id: 'discord' | 'telegram';
|
||||
icon: IconName;
|
||||
brandClassName: string;
|
||||
nameKey: I18nKey;
|
||||
descriptionKey: I18nKey;
|
||||
};
|
||||
|
||||
const COMING_SOON_MESSENGERS: readonly ComingSoonMessenger[] = [
|
||||
{
|
||||
id: 'discord',
|
||||
icon: 'discord-fill',
|
||||
brandClassName: 'text-[#5865F2]',
|
||||
nameKey: 'settings.integrations.messengers.discord.name',
|
||||
descriptionKey: 'settings.integrations.messengers.discord.description',
|
||||
},
|
||||
{
|
||||
id: 'telegram',
|
||||
icon: 'telegram-fill',
|
||||
brandClassName: 'text-[#2AABEE]',
|
||||
nameKey: 'settings.integrations.messengers.telegram.name',
|
||||
descriptionKey: 'settings.integrations.messengers.telegram.description',
|
||||
},
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Non-interactive Discord/Telegram placeholders — same card chrome as live
|
||||
* integrations, greyed out, with a Coming soon badge and no expandable body.
|
||||
*/
|
||||
export const ComingSoonMessengersSection: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
title={t('settings.integrations.messengers.title')}
|
||||
info={t('settings.integrations.messengers.info')}
|
||||
divider={false}
|
||||
settingsItem="integrations.messengers"
|
||||
contentClassName="space-y-3"
|
||||
>
|
||||
{COMING_SOON_MESSENGERS.map((messenger) => (
|
||||
<div
|
||||
key={messenger.id}
|
||||
data-settings-item={`integrations.messengers.${messenger.id}`}
|
||||
aria-disabled="true"
|
||||
className={cn(
|
||||
'flex min-w-0 items-center gap-3 rounded-xl border border-[var(--interactive-border)] bg-[var(--surface-elevated)] px-4 py-3',
|
||||
'pointer-events-none opacity-60',
|
||||
)}
|
||||
>
|
||||
<div className="flex size-10 shrink-0 items-center justify-center rounded-[10px] bg-[var(--surface-muted)]">
|
||||
<Icon name={messenger.icon} className={cn('size-5', messenger.brandClassName)} />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-sm font-semibold text-foreground">{t(messenger.nameKey)}</div>
|
||||
<p className="mt-0.5 line-clamp-1 text-xs leading-snug text-muted-foreground">
|
||||
{t(messenger.descriptionKey)}
|
||||
</p>
|
||||
</div>
|
||||
<span className="max-w-36 shrink-0 truncate rounded-full bg-[var(--surface-muted)] px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
|
||||
{t('settings.common.state.comingSoon')}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</SettingsSection>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Icon } from '@/components/icon/Icon';
|
||||
import { SettingsPageLayout } from '@/components/sections/shared/SettingsPageLayout';
|
||||
import { SETTINGS_DESCRIPTION_CLASS } from '@/components/sections/shared/SettingsSection';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { ThirdPartyIntegrationsSection } from './ThirdPartyIntegrationsSection';
|
||||
|
||||
@@ -18,15 +19,19 @@ export const IntegrationsPage: React.FC<IntegrationsPageProps> = ({
|
||||
return (
|
||||
<SettingsPageLayout
|
||||
title={t('settings.page.integrations.title')}
|
||||
description={t('settings.page.integrations.description')}
|
||||
description={(
|
||||
<div className="space-y-3">
|
||||
<p className={SETTINGS_DESCRIPTION_CLASS}>{t('settings.page.integrations.description')}</p>
|
||||
<div role="alert" className="flex items-start gap-2 rounded-lg border border-[var(--status-warning-border)] bg-[var(--status-warning-background)] p-3">
|
||||
<Icon name="error-warning" className="mt-0.5 size-4 shrink-0 text-[var(--status-warning)]" />
|
||||
<p className="typography-meta text-[var(--status-warning)]">
|
||||
{t('settings.integrations.experimentalWarning')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
showSaveStatus={false}
|
||||
>
|
||||
<div className="flex items-start gap-2 rounded-lg border border-[var(--status-warning-border)] bg-[var(--status-warning-background)] p-3">
|
||||
<Icon name="error-warning" className="mt-0.5 size-4 shrink-0 text-[var(--status-warning)]" />
|
||||
<p className="typography-meta text-[var(--status-warning)]">
|
||||
{t('settings.integrations.experimentalWarning')}
|
||||
</p>
|
||||
</div>
|
||||
<ThirdPartyIntegrationsSection
|
||||
divider={false}
|
||||
onOpenProviderSetup={onOpenProviderSetup}
|
||||
|
||||
@@ -13,7 +13,6 @@ import { toast } from '@/components/ui';
|
||||
import { Icon } from '@/components/icon/Icon';
|
||||
import { SettingsSection } from '@/components/sections/shared/SettingsSection';
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
||||
import { ProviderLogo } from '@/components/ui/ProviderLogo';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { openExternalUrl } from '@/lib/url';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -330,11 +329,7 @@ export const ThirdPartyIntegrationsSection: React.FC<ThirdPartyIntegrationsSecti
|
||||
className="flex w-full min-w-0 items-center gap-3 px-4 py-3 text-left hover:bg-[var(--interactive-hover)]/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--interactive-focus-ring)]"
|
||||
>
|
||||
<div className="flex size-10 shrink-0 items-center justify-center rounded-[10px] bg-[var(--surface-muted)]">
|
||||
{plugin.providerId === 'command-code' ? (
|
||||
<ProviderLogo providerId={plugin.providerId} className="size-5" />
|
||||
) : (
|
||||
<Icon name={plugin.icon} className={cn('size-5', plugin.brandClassName)} />
|
||||
)}
|
||||
<Icon name={plugin.icon} className={cn('size-5', plugin.brandClassName)} />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-sm font-semibold text-foreground">{t(plugin.nameKey)}</div>
|
||||
|
||||
@@ -131,11 +131,6 @@ describe('third-party plugin catalog helpers', () => {
|
||||
packageName: '@openchamber/opencode-claude',
|
||||
homepage: 'https://github.com/openchamber/opencode-claude',
|
||||
},
|
||||
{
|
||||
id: 'opencode-commandcode',
|
||||
packageName: '@openchamber/opencode-commandcode',
|
||||
homepage: 'https://github.com/openchamber/opencode-commandcode',
|
||||
},
|
||||
{
|
||||
id: 'opencode-cursor-oauth',
|
||||
packageName: '@openchamber/opencode-cursor',
|
||||
|
||||
@@ -25,16 +25,6 @@ export const THIRD_PARTY_PLUGINS: readonly ThirdPartyPluginDefinition[] = [
|
||||
descriptionKey: 'settings.integrations.thirdParty.opencodeClaude.description',
|
||||
homepage: 'https://github.com/openchamber/opencode-claude',
|
||||
},
|
||||
{
|
||||
id: 'opencode-commandcode',
|
||||
packageName: '@openchamber/opencode-commandcode',
|
||||
providerId: 'command-code',
|
||||
icon: 'command-code',
|
||||
brandClassName: 'text-foreground',
|
||||
nameKey: 'settings.integrations.thirdParty.opencodeCommandcode.name',
|
||||
descriptionKey: 'settings.integrations.thirdParty.opencodeCommandcode.description',
|
||||
homepage: 'https://github.com/openchamber/opencode-commandcode',
|
||||
},
|
||||
{
|
||||
id: 'opencode-cursor-oauth',
|
||||
packageName: '@openchamber/opencode-cursor',
|
||||
|
||||
@@ -2,8 +2,10 @@ import { describe, expect, test } from 'bun:test';
|
||||
import { getProviderLogoFallbackIcon } from './providerLogoFallback';
|
||||
|
||||
describe('provider logo fallbacks', () => {
|
||||
test('uses a local terminal icon when Command Code has no resolved logo', () => {
|
||||
expect(getProviderLogoFallbackIcon('command-code')).toBe('terminal-box');
|
||||
test('uses a local terminal icon for Command Code provider ID variants', () => {
|
||||
for (const providerId of ['command-code', 'commandcode', 'command_code', 'command code']) {
|
||||
expect(getProviderLogoFallbackIcon(providerId)).toBe('terminal-box');
|
||||
}
|
||||
});
|
||||
|
||||
test('does not replace providers with their own logo assets', () => {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { IconName } from '@/components/icon/icons';
|
||||
|
||||
const COMMAND_CODE_PROVIDER_IDS = new Set(['command-code', 'commandcode', 'command_code', 'command code']);
|
||||
|
||||
export function getProviderLogoFallbackIcon(providerId: string | null | undefined): IconName | null {
|
||||
return providerId?.trim().toLowerCase() === 'command-code' ? 'terminal-box' : null;
|
||||
return providerId && COMMAND_CODE_PROVIDER_IDS.has(providerId.trim().toLowerCase())
|
||||
? 'terminal-box'
|
||||
: null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user