diff --git a/src/web-ui/src/app/global-search/interactiveCapabilityActivator.test.ts b/src/web-ui/src/app/global-search/interactiveCapabilityActivator.test.ts index ff3db7b970..444d99d185 100644 --- a/src/web-ui/src/app/global-search/interactiveCapabilityActivator.test.ts +++ b/src/web-ui/src/app/global-search/interactiveCapabilityActivator.test.ts @@ -80,4 +80,12 @@ describe('activateInteractiveCapability', () => { })).rejects.toThrow('Unknown documented item'); expect(mocks.openDestination).not.toHaveBeenCalled(); }); + + it('rejects Git panel requests, including documented items, without opening a scene', async () => { + for (const itemId of [undefined, 'commit', 'status-diff']) { + await expect(activateInteractiveCapability('feature.git', { itemId })) + .rejects.toThrow('The Git panel is no longer available'); + } + expect(mocks.openScene).not.toHaveBeenCalled(); + }); }); diff --git a/src/web-ui/src/app/global-search/interactiveCapabilityActivator.ts b/src/web-ui/src/app/global-search/interactiveCapabilityActivator.ts index 840543ac6b..0d2f6daba6 100644 --- a/src/web-ui/src/app/global-search/interactiveCapabilityActivator.ts +++ b/src/web-ui/src/app/global-search/interactiveCapabilityActivator.ts @@ -1,6 +1,9 @@ import { useSettingsStore } from '@/app/scenes/settings/settingsStore'; import { useSceneStore } from '@/app/stores/sceneStore'; -import { getInteractiveCapability } from './interactiveCapabilityCatalog'; +import { + getInteractiveCapability, + isInteractiveCapabilityDestinationAvailable, +} from './interactiveCapabilityCatalog'; import { activateProductAction } from './productActionActivator'; export interface InteractiveCapabilityActivationOptions { @@ -21,6 +24,9 @@ export async function activateInteractiveCapability( throw new Error(`Unknown documented item for ${capabilityId}: ${options.itemId}`); } const destination = item?.destination ?? capability.destination; + if (!isInteractiveCapabilityDestinationAvailable(destination)) { + throw new Error('The Git panel is no longer available'); + } switch (destination.kind) { case 'settings': diff --git a/src/web-ui/src/app/global-search/interactiveCapabilityCatalog.ts b/src/web-ui/src/app/global-search/interactiveCapabilityCatalog.ts index 6d4633c377..89ab922d10 100644 --- a/src/web-ui/src/app/global-search/interactiveCapabilityCatalog.ts +++ b/src/web-ui/src/app/global-search/interactiveCapabilityCatalog.ts @@ -14,6 +14,13 @@ export type InteractiveCapabilityDestination = | { kind: 'scene'; sceneId: SceneTabId } | { kind: 'event'; eventName: string; detail?: Record }; +/** Git operations remain host capabilities, but their standalone panel has no entry point. */ +export function isInteractiveCapabilityDestinationAvailable( + destination: InteractiveCapabilityDestination, +): boolean { + return destination.kind !== 'scene' || destination.sceneId !== 'git'; +} + export interface InteractiveCapabilityValueSchema { type: 'boolean' | 'string' | 'integer' | 'number' | 'object' | 'array'; nullable?: boolean; diff --git a/src/web-ui/src/app/global-search/providers/interactiveCapabilitySearchProvider.test.ts b/src/web-ui/src/app/global-search/providers/interactiveCapabilitySearchProvider.test.ts index c8517b9b62..bee2f55c32 100644 --- a/src/web-ui/src/app/global-search/providers/interactiveCapabilitySearchProvider.test.ts +++ b/src/web-ui/src/app/global-search/providers/interactiveCapabilitySearchProvider.test.ts @@ -128,6 +128,12 @@ describe('interactiveCapabilitySearchProvider', () => { request(acceptance.query), new AbortController().signal, ); + // Git remains in the host discovery contract, but no longer has a Web UI panel entry. + if (acceptance.expectedFirstCapabilityId === 'feature.git') { + expect(result.items.some(item => item.target.kind === 'capability' + && item.target.capabilityId === 'feature.git')).toBe(false); + continue; + } const ranked = [...result.items].sort((left, right) => right.score - left.score); expect( ranked[0]?.target.kind === 'capability' ? ranked[0].target.capabilityId : undefined, diff --git a/src/web-ui/src/app/global-search/providers/interactiveCapabilitySearchProvider.ts b/src/web-ui/src/app/global-search/providers/interactiveCapabilitySearchProvider.ts index 0b2809201e..0a6069b07f 100644 --- a/src/web-ui/src/app/global-search/providers/interactiveCapabilitySearchProvider.ts +++ b/src/web-ui/src/app/global-search/providers/interactiveCapabilitySearchProvider.ts @@ -1,6 +1,7 @@ import { INTERACTIVE_CAPABILITY_CATALOG, getInteractiveCapabilityCatalog, + isInteractiveCapabilityDestinationAvailable, type InteractiveCapability, } from '../interactiveCapabilityCatalog'; import { scoreTextMatch } from '../searchMatching'; @@ -51,8 +52,11 @@ export const interactiveCapabilitySearchProvider: GlobalSearchProvider = { ? getInteractiveCapabilityCatalog(false) : INTERACTIVE_CAPABILITY_CATALOG; const items = catalog.capabilities - .map((capability) => { + .flatMap((capability) => { const item = matchingItem(capability, request.query); + if (!isInteractiveCapabilityDestinationAvailable(item?.destination ?? capability.destination)) { + return []; + } const textScore = scoreTextMatch(request.query, [ capability.id, capability.titleZh, diff --git a/src/web-ui/src/app/hooks/useGlobalSceneShortcuts.ts b/src/web-ui/src/app/hooks/useGlobalSceneShortcuts.ts index a75d7ca802..9ed9ec1d66 100644 --- a/src/web-ui/src/app/hooks/useGlobalSceneShortcuts.ts +++ b/src/web-ui/src/app/hooks/useGlobalSceneShortcuts.ts @@ -6,7 +6,6 @@ * * Scene quick-open (allowInInput: true — fires from anywhere): * Mod+Shift+A — open Agent/Session scene - * Mod+Shift+G — open Git scene * Mod+, — open Settings scene * Mod+Shift+` — open Terminal scene */ @@ -51,13 +50,6 @@ export function useGlobalSceneShortcuts(): void { { priority: 10, description: 'keyboard.shortcuts.scene.openSession' } ); - useShortcut( - 'scene.openGit', - { key: 'G', ctrl: true, shift: true, scope: 'app', allowInInput: true }, - () => openSceneById('git'), - { priority: 10, description: 'keyboard.shortcuts.scene.openGit' } - ); - useShortcut( 'scene.openSettings', { key: ',', ctrl: true, scope: 'app', allowInInput: true }, diff --git a/src/web-ui/src/app/scenes/git/views/WorkingCopyView.tsx b/src/web-ui/src/app/scenes/git/views/WorkingCopyView.tsx index 9d30e2f189..cef47b8ae1 100644 --- a/src/web-ui/src/app/scenes/git/views/WorkingCopyView.tsx +++ b/src/web-ui/src/app/scenes/git/views/WorkingCopyView.tsx @@ -4,7 +4,6 @@ import { OverflowText, Button, Icon, IconButton, SearchField, Textarea, Tooltip } from '@openbitfun/ui'; import React, { useCallback, useState, useMemo, useEffect, useRef } from 'react'; -import { useShortcut } from '@/infrastructure/hooks/useShortcut'; import { useTranslation } from 'react-i18next'; import { Minus, RotateCcw, Square, FileCode2 } from 'lucide-react'; import { useI18n } from '@/infrastructure/i18n/hooks/useI18n'; @@ -86,7 +85,7 @@ const WorkingCopyView: React.FC = ({ [currentBranch, staged, unstaged, untracked, ahead, behind] ); - const { isOperating, addFiles, commit, push, pull, resetFiles } = useGitOperations({ + const { isOperating, addFiles, commit, push, pull } = useGitOperations({ repositoryPath: { workspaceId: workspaceId ?? '', repositoryPath: workspacePath }, autoRefresh: false, }); @@ -306,76 +305,6 @@ const WorkingCopyView: React.FC = ({ document.addEventListener('mouseup', onMouseUp); }, [fileListWidth]); - const handleGitStageAll = useCallback(async () => { - const files = getAllUnstagedFiles(); - if (files.length === 0) return; - const result = await addFiles({ files, all: false }); - if (result.success) { - setSelectedFiles(new Set()); - await handleRefresh(); - notification.success(t('notifications.stageSuccess', { count: files.length })); - } else if (result.error) notification.error(t('notifications.stageFailed', { error: result.error })); - }, [getAllUnstagedFiles, addFiles, handleRefresh, notification, t]); - - const handleGitUnstageAll = useCallback(async () => { - if (!status?.staged?.length) return; - const paths = status.staged.map((f: { path: string }) => f.path); - const result = await resetFiles(paths, true); - if (result.success) await handleRefresh(); - else if (result.error) notification.error(result.error); - }, [status, resetFiles, handleRefresh, notification]); - - const gitShortcutsEnabled = Boolean(workspacePath) && isActive; - - useShortcut( - 'git.refresh', - { key: 'F5', scope: 'git' }, - () => { - void handleRefresh(); - }, - { enabled: gitShortcutsEnabled, description: 'keyboard.shortcuts.git.refresh' } - ); - useShortcut( - 'git.commit', - { key: 'Enter', ctrl: true, scope: 'git', allowInInput: true }, - () => { - void handleQuickCommit(); - }, - { enabled: gitShortcutsEnabled, description: 'keyboard.shortcuts.git.commit' } - ); - useShortcut( - 'git.push', - { key: 'P', ctrl: true, shift: true, scope: 'git' }, - () => { - void handlePush(); - }, - { enabled: gitShortcutsEnabled, description: 'keyboard.shortcuts.git.push' } - ); - useShortcut( - 'git.pull', - { key: 'L', ctrl: true, shift: true, scope: 'git' }, - () => { - void handlePull(); - }, - { enabled: gitShortcutsEnabled, description: 'keyboard.shortcuts.git.pull' } - ); - useShortcut( - 'git.stageAll', - { key: 'A', ctrl: true, shift: true, scope: 'git' }, - () => { - void handleGitStageAll(); - }, - { enabled: gitShortcutsEnabled, description: 'keyboard.shortcuts.git.stageAll' } - ); - useShortcut( - 'git.unstageAll', - { key: 'U', ctrl: true, shift: true, scope: 'git' }, - () => { - void handleGitUnstageAll(); - }, - { enabled: gitShortcutsEnabled, description: 'keyboard.shortcuts.git.unstageAll' } - ); - if (!workspacePath) { return (
@@ -431,12 +360,6 @@ const WorkingCopyView: React.FC = ({ placeholder={status?.staged?.length ? t('commit.inputPlaceholder') : t('commit.inputPlaceholderNoStaged')} value={quickCommitMessage} onChange={e => setQuickCommitMessage(e.target.value)} - onKeyDown={e => { - if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { - e.preventDefault(); - handleQuickCommit(); - } - }} disabled={isOperating || isGeneratingCommit} /> {isGeneratingCommit ? ( diff --git a/src/web-ui/src/shared/constants/shortcuts.ts b/src/web-ui/src/shared/constants/shortcuts.ts index 8471f3201a..6fae6462c0 100644 --- a/src/web-ui/src/shared/constants/shortcuts.ts +++ b/src/web-ui/src/shared/constants/shortcuts.ts @@ -48,11 +48,6 @@ export const APP_SHORTCUTS: ShortcutDef[] = [ config: mod('A', { shift: true, scope: 'app', allowInInput: true }), descriptionKey: 'keyboard.shortcuts.scene.openSession', }, - { - id: 'scene.openGit', - config: mod('G', { shift: true, scope: 'app', allowInInput: true }), - descriptionKey: 'keyboard.shortcuts.scene.openGit', - }, { id: 'scene.openSettings', config: mod(',', { scope: 'app', allowInInput: true }), @@ -250,41 +245,6 @@ export const FILETREE_SHORTCUTS: ShortcutDef[] = [ }, ]; -// ─── Git shortcuts (scope: 'git') ───────────────────────────────────────── - -export const GIT_SHORTCUTS: ShortcutDef[] = [ - { - id: 'git.commit', - config: mod('Enter', { scope: 'git', allowInInput: true }), - descriptionKey: 'keyboard.shortcuts.git.commit', - }, - { - id: 'git.refresh', - config: { key: 'F5', scope: 'git' }, - descriptionKey: 'keyboard.shortcuts.git.refresh', - }, - { - id: 'git.stageAll', - config: mod('A', { shift: true, scope: 'git' }), - descriptionKey: 'keyboard.shortcuts.git.stageAll', - }, - { - id: 'git.unstageAll', - config: mod('U', { shift: true, scope: 'git' }), - descriptionKey: 'keyboard.shortcuts.git.unstageAll', - }, - { - id: 'git.push', - config: mod('P', { shift: true, scope: 'git' }), - descriptionKey: 'keyboard.shortcuts.git.push', - }, - { - id: 'git.pull', - config: mod('L', { shift: true, scope: 'git' }), - descriptionKey: 'keyboard.shortcuts.git.pull', - }, -]; - // ─── All shortcuts combined ──────────────────────────────────────────────── export const ALL_SHORTCUTS: ShortcutDef[] = [ @@ -294,7 +254,6 @@ export const ALL_SHORTCUTS: ShortcutDef[] = [ ...EDITOR_SHORTCUTS, ...CHAT_SHORTCUTS, ...FILETREE_SHORTCUTS, - ...GIT_SHORTCUTS, ]; /** Shortcuts registered in code but not listed in ALL_SHORTCUTS (e.g. legacy ids). */ diff --git a/src/web-ui/src/shared/services/ide-control/api.ts b/src/web-ui/src/shared/services/ide-control/api.ts index 4efb88411c..8b5371c9c6 100644 --- a/src/web-ui/src/shared/services/ide-control/api.ts +++ b/src/web-ui/src/shared/services/ide-control/api.ts @@ -239,10 +239,6 @@ export const ideControl = { export const quickActions = { - openGitPanel: () => window.dispatchEvent(new CustomEvent('scene:open', { detail: { sceneId: 'git' } })), - - - openSettings: (destination?: SettingsDestination | string) => { if (isLegacyEcosystemCompatibilityDestination(destination)) { void import('@/app/scenes/ecosystem-compatibility/ecosystemCompatibilityStore')