- {visibleCommands.length === 0 ? (
-
- {commands.length === 0
- ? translate(
- 'auto.components.settings.QuickCommandsPane.38d61927e6',
- 'No quick commands saved.'
- )
- : translate(
- 'auto.components.settings.QuickCommandsPane.3eb9897ab0',
- 'No commands in the selected scopes.'
- )}
-
- ) : (
-
- {visibleCommands.map((command) => (
-
- ))}
-
- )}
+
+ {visibleCommands.map((command) => (
+
+ ))}
)
}
diff --git a/src/renderer/src/components/settings/QuickCommandsPane.tsx b/src/renderer/src/components/settings/QuickCommandsPane.tsx
index f7f703e7ac1..a4f1ea85fe4 100644
--- a/src/renderer/src/components/settings/QuickCommandsPane.tsx
+++ b/src/renderer/src/components/settings/QuickCommandsPane.tsx
@@ -1,26 +1,25 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
-import { Plus } from 'lucide-react'
import type { GlobalSettings, TerminalQuickCommand } from '../../../../shared/types'
import { getTerminalQuickCommandScope } from '../../../../shared/terminal-quick-commands'
import {
createTerminalQuickCommandDraft,
TerminalQuickCommandDialog
} from '@/components/terminal-quick-commands/TerminalQuickCommandDialog'
+import { searchTerminalQuickCommands } from '@/lib/terminal-quick-command-search'
import { useAppStore } from '../../store'
import { Button } from '../ui/button'
-import { Label } from '../ui/label'
import { useConfirmationDialog } from '@/components/confirmation-dialog-context'
import { getSettingOwnershipSummary } from './setting-ownership'
import { translate } from '@/i18n/i18n'
import { QuickCommandsList } from './QuickCommandsList'
-import { GLOBAL_SCOPE_KEY, QuickCommandsScopeFilter } from './QuickCommandsScopeFilter'
+import { QuickCommandsToolbar } from './QuickCommandsToolbar'
+import { GLOBAL_SCOPE_KEY } from './QuickCommandsScopeFilter'
import {
getRepoExecutionHostId,
LOCAL_EXECUTION_HOST_ID,
parseExecutionHostId,
type ExecutionHostId
} from '../../../../shared/execution-host'
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
import {
getTerminalQuickCommandHostOptions,
shouldShowTerminalQuickCommandHostOwnership
@@ -130,6 +129,7 @@ export function QuickCommandsPane({
// automatically rather than being silently excluded.
const [scopeSelection, setScopeSelection] = useState
| null>(null)
const [scopePopoverOpen, setScopePopoverOpen] = useState(false)
+ const [query, setQuery] = useState('')
const availableHostId = getAvailableQuickCommandHostId(selectedHostId, hostOptions)
const editorHostIsCurrent =
@@ -164,7 +164,7 @@ export function QuickCommandsPane({
const effectiveSelection: ReadonlySet = scopeSelection ?? allScopeKeys
const showAll = scopeSelection === null
- const visibleCommands = commands.filter((command) => {
+ const scopedCommands = commands.filter((command) => {
const scope = getTerminalQuickCommandScope(command)
if (showAll) {
return true
@@ -174,6 +174,7 @@ export function QuickCommandsPane({
}
return effectiveSelection.has(scope.repoId)
})
+ const visibleCommands = searchTerminalQuickCommands(scopedCommands, query)
const createDraftForCurrentFilter = useCallback((): TerminalQuickCommand => {
// Why: when the user has narrowed to a single repo scope, the natural
@@ -273,68 +274,32 @@ export function QuickCommandsPane({
void useAppStore.getState().deleteTerminalQuickCommand(selectedHostId, command.id)
}
- return (
-
-
-
-
- {translate('auto.components.settings.QuickCommandsPane.f91b649324', 'Saved Commands')}
-
-
- {shouldShowTerminalQuickCommandHostOwnership(hostOptions)
- ? ownership.description
- : translate(
- 'auto.components.settings.settingOwnership.terminalQuickCommands',
- 'Commands are saved on this client, then scoped globally or to a project setup so they run from the selected terminal context.'
- )}
-
-
-
- setEditor({
- mode: 'add',
- command: createDraftForCurrentFilter(),
- connectionGeneration: selectedRuntimeConnectionGeneration,
- hostId: selectedHostId
- })
- }
- >
-
- {translate('auto.components.settings.QuickCommandsPane.5aacc8f7dc', 'Add Command')}
-
-
+ const openAddDialog = (): void =>
+ setEditor({
+ mode: 'add',
+ command: createDraftForCurrentFilter(),
+ connectionGeneration: selectedRuntimeConnectionGeneration,
+ hostId: selectedHostId
+ })
+ return (
+
{shouldShowTerminalQuickCommandHostOwnership(hostOptions) ? (
-
-
- {translate('auto.components.settings.QuickCommandsPane.89f7e57fcc', 'Saved on')}
-
- {
- setSelectedHostId(value as ExecutionHostId)
- setScopeSelection(null)
- }}
- >
-
-
-
-
- {hostOptions.map((host) => (
-
- {host.label}
-
- ))}
-
-
-
+
{ownership.description}
) : null}
-
{
+ setSelectedHostId(hostId)
+ setScopeSelection(null)
+ }}
+ canAdd={canManageSelectedHost}
+ onAdd={openAddDialog}
repos={hostRepos}
effectiveSelection={effectiveSelection}
showAll={showAll}
@@ -408,6 +373,7 @@ export function QuickCommandsPane({
0}
repoById={repoById}
onEdit={(command) =>
setEditor({
@@ -428,6 +394,7 @@ export function QuickCommandsPane({
mode={editor.mode}
command={editor.command}
repos={hostRepos}
+ defaultAdvancedOpen
onOpenChange={(open) => !open && setEditor(null)}
onSave={saveCommand}
/>
diff --git a/src/renderer/src/components/settings/QuickCommandsToolbar.tsx b/src/renderer/src/components/settings/QuickCommandsToolbar.tsx
new file mode 100644
index 00000000000..65e034eeb3f
--- /dev/null
+++ b/src/renderer/src/components/settings/QuickCommandsToolbar.tsx
@@ -0,0 +1,116 @@
+import type { Dispatch, SetStateAction } from 'react'
+import { Plus, Search } from 'lucide-react'
+import type { Repo } from '../../../../shared/types'
+import type { ExecutionHostId } from '../../../../shared/execution-host'
+import { Button } from '../ui/button'
+import { Input } from '../ui/input'
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
+import { translate } from '@/i18n/i18n'
+import { QuickCommandsScopeFilter } from './QuickCommandsScopeFilter'
+
+type QuickCommandsToolbarProps = {
+ query: string
+ setQuery: (query: string) => void
+ hostOptions: readonly { id: ExecutionHostId; label: string }[]
+ showHostSelect: boolean
+ selectedHostId: ExecutionHostId
+ onHostChange: (hostId: ExecutionHostId) => void
+ canAdd: boolean
+ onAdd: () => void
+ repos: readonly Repo[]
+ effectiveSelection: ReadonlySet
+ showAll: boolean
+ scopePopoverOpen: boolean
+ setScopePopoverOpen: Dispatch>
+ handleSelectAll: () => void
+ toggleScope: (key: string) => void
+}
+
+export function QuickCommandsToolbar({
+ query,
+ setQuery,
+ hostOptions,
+ showHostSelect,
+ selectedHostId,
+ onHostChange,
+ canAdd,
+ onAdd,
+ repos,
+ effectiveSelection,
+ showAll,
+ scopePopoverOpen,
+ setScopePopoverOpen,
+ handleSelectAll,
+ toggleScope
+}: QuickCommandsToolbarProps): React.JSX.Element {
+ const searchLabel = translate(
+ 'auto.components.settings.QuickCommandsToolbar.searchLabel',
+ 'Search commands'
+ )
+
+ return (
+
+
+
+ setQuery(event.target.value)}
+ placeholder={searchLabel}
+ aria-label={searchLabel}
+ className="h-8 pl-8 text-xs"
+ />
+
+
+
+
+ {showHostSelect ? (
+
onHostChange(value as ExecutionHostId)}
+ >
+
+
+ {translate('auto.components.settings.QuickCommandsPane.89f7e57fcc', 'Saved on')}
+
+
+
+
+ {hostOptions.map((host) => (
+
+ {host.label}
+
+ ))}
+
+
+ ) : null}
+
+
+
+ {translate('auto.components.settings.QuickCommandsPane.5aacc8f7dc', 'Add Command')}
+
+
+ )
+}
diff --git a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandAdvancedSection.tsx b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandAdvancedSection.tsx
index d49be9c9616..af196b2c628 100644
--- a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandAdvancedSection.tsx
+++ b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandAdvancedSection.tsx
@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'
import { translate } from '@/i18n/i18n'
import { TerminalQuickCommandAppendEnterSwitch } from './TerminalQuickCommandAppendEnterSwitch'
+import { TerminalQuickCommandCollapsibleRow } from './TerminalQuickCommandCollapsibleRow'
import { TerminalQuickCommandScopeField } from './TerminalQuickCommandScopeField'
type TerminalQuickCommandAdvancedSectionProps = {
@@ -50,42 +51,26 @@ export function TerminalQuickCommandAdvancedSection({
-
-
-
- {!isTerminalAgentQuickCommand(draft) ? (
-
- ) : null}
- {
- lastRepoScopeIdRef.current = repoId
- }}
- setDraft={setDraft}
- />
-
-
-
+
+ {
+ lastRepoScopeIdRef.current = repoId
+ }}
+ setDraft={setDraft}
+ />
+ {!isTerminalAgentQuickCommand(draft) ? (
+
+ ) : null}
+
)
}
diff --git a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandAppendEnterSwitch.tsx b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandAppendEnterSwitch.tsx
index 4450562729e..73f69ebf5d8 100644
--- a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandAppendEnterSwitch.tsx
+++ b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandAppendEnterSwitch.tsx
@@ -3,11 +3,13 @@ import { Switch } from '@/components/ui/switch'
type TerminalQuickCommandAppendEnterSwitchProps = {
appendEnter: boolean
onToggle: () => void
+ disabled?: boolean
}
export function TerminalQuickCommandAppendEnterSwitch({
appendEnter,
- onToggle
+ onToggle,
+ disabled = false
}: TerminalQuickCommandAppendEnterSwitchProps): React.JSX.Element {
return (
@@ -27,6 +29,7 @@ export function TerminalQuickCommandAppendEnterSwitch({
+
+
+ )
+}
diff --git a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandContentSection.tsx b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandContentSection.tsx
index 34035a6233f..19967b0daba 100644
--- a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandContentSection.tsx
+++ b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandContentSection.tsx
@@ -12,9 +12,11 @@ import {
SelectTrigger,
SelectValue
} from '@/components/ui/select'
+import { Textarea } from '@/components/ui/textarea'
import { AgentIcon } from '@/lib/agent-catalog'
import { cn } from '@/lib/utils'
import { translate } from '@/i18n/i18n'
+import { TerminalQuickCommandCollapsibleRow } from './TerminalQuickCommandCollapsibleRow'
import { getTerminalQuickCommandAgentOptions } from './terminal-quick-command-agent-options'
import type { TerminalQuickCommandDialogDraftMemory } from './terminal-quick-command-dialog-draft'
@@ -37,85 +39,66 @@ export function TerminalQuickCommandContentSection({
}: TerminalQuickCommandContentSectionProps): React.JSX.Element {
return (
- {/* Why: action changes add/remove agent-only fields; animating rows here
- keeps the fixed dialog from snapping between content heights. */}
-
-
-
-
- {translate(
- 'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.0adba8fa0c',
- 'Agent'
+
+
+ {translate(
+ 'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.0adba8fa0c',
+ 'Agent'
+ )}
+
+ {
+ const nextAgent = agent as TuiAgent
+ draftMemoryRef.current = {
+ ...draftMemoryRef.current,
+ agent: nextAgent
+ }
+ setDraft((current) =>
+ isTerminalAgentQuickCommand(current) ? { ...current, agent: nextAgent } : current
+ )
+ }}
+ >
+
+
- {
- const nextAgent = agent as TuiAgent
- draftMemoryRef.current = {
- ...draftMemoryRef.current,
- agent: nextAgent
- }
- setDraft((current) =>
- isTerminalAgentQuickCommand(current) ? { ...current, agent: nextAgent } : current
- )
- }}
- >
-
-
-
-
- {QUICK_COMMAND_AGENT_OPTIONS.map((entry) => {
- const supported = supportsTerminalAgentQuickCommand(entry.id)
- return (
-
-
-
-
- {entry.label}
- {!supported ? (
-
- {translate(
- 'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.026cfb232a',
- 'Does not support prompt commands'
- )}
-
- ) : null}
+ />
+
+
+ {QUICK_COMMAND_AGENT_OPTIONS.map((entry) => {
+ const supported = supportsTerminalAgentQuickCommand(entry.id)
+ return (
+
+
+
+
+ {entry.label}
+ {!supported ? (
+
+ {translate(
+ 'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.026cfb232a',
+ 'Does not support prompt commands'
+ )}
-
-
- )
- })}
-
-
-
-
-
+ ) : null}
+
+
+
+ )
+ })}
+
+
+
@@ -129,7 +112,7 @@ export function TerminalQuickCommandContentSection({
'Command Text'
)}
-
-
-
-
- {translate(
- 'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.e604bd40d6',
- 'Supports skills, file paths, and built-in commands like'
- )}{' '}
-
- {translate(
- 'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.97e96cc027',
- '/goal'
- )}
-
- .
-
-
-
+ {translate(
+ 'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.e604bd40d6',
+ 'Supports skills, file paths, and built-in commands like'
+ )}{' '}
+
+ {translate(
+ 'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.97e96cc027',
+ '/goal'
+ )}
+
+ .
+
)
}
diff --git a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialog.tsx b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialog.tsx
index 493eb755bd6..1c9c615d72d 100644
--- a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialog.tsx
+++ b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialog.tsx
@@ -40,6 +40,9 @@ type TerminalQuickCommandDialogProps = {
mode: TerminalQuickCommandDialogMode
command: TerminalQuickCommand
repos?: readonly Pick[]
+ /** Settings has no ambient workspace to imply scope from, so it opens the
+ * Advanced section up front. In-workspace entry points leave it collapsed. */
+ defaultAdvancedOpen?: boolean
onOpenChange: (open: boolean) => void
onSave: (command: TerminalQuickCommand) => void
}
@@ -63,6 +66,7 @@ export function TerminalQuickCommandDialog({
mode,
command,
repos = EMPTY_REPOS,
+ defaultAdvancedOpen = false,
onOpenChange,
onSave
}: TerminalQuickCommandDialogProps): React.JSX.Element {
@@ -76,7 +80,7 @@ export function TerminalQuickCommandDialog({
const lastRepoScopeIdRef = useRef(
initialScope.type === 'repo' ? initialScope.repoId : null
)
- const [advancedOpen, setAdvancedOpen] = useState(false)
+ const [advancedOpen, setAdvancedOpen] = useState(defaultAdvancedOpen)
const selectedAction = getTerminalQuickCommandAction(draft)
const selectedScope = getTerminalQuickCommandScope(draft)
const isAgentAction = isTerminalAgentQuickCommand(draft)
@@ -95,7 +99,7 @@ export function TerminalQuickCommandDialog({
draftMemoryRef.current = createTerminalQuickCommandDialogDraftMemory(command, fallbackAgent)
const commandScope = getTerminalQuickCommandScope(command)
lastRepoScopeIdRef.current = commandScope.type === 'repo' ? commandScope.repoId : null
- setAdvancedOpen(false)
+ setAdvancedOpen(defaultAdvancedOpen)
setDraft({ ...command })
}
@@ -164,9 +168,12 @@ export function TerminalQuickCommandDialog({
return (
-
+
-
+
{mode === 'edit'
? translate(
'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.f9b184fc16',
@@ -177,7 +184,7 @@ export function TerminalQuickCommandDialog({
'Add Quick Command'
)}
-
+
{translate(
'auto.components.terminal.quick.commands.TerminalQuickCommandDialog.ed04233b3e',
'Save terminal commands or agent prompts for quick access.'
@@ -185,8 +192,11 @@ export function TerminalQuickCommandDialog({
+ {/* Why -mx-3/px-3: overflow clips at the padding box, so the padding has
+ to cover the widest negative margin inside (Advanced's -ml-2) plus a
+ focus ring. The matching negative margin keeps children aligned. */}
{
if (isScreenSubmitShortcut(event) && canSave) {
event.preventDefault()
diff --git a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialogFooter.tsx b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialogFooter.tsx
index 6bcd65fd7b7..d2f98bef2c7 100644
--- a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialogFooter.tsx
+++ b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialogFooter.tsx
@@ -1,5 +1,7 @@
import { Button } from '@/components/ui/button'
import { DialogFooter } from '@/components/ui/dialog'
+import { ShortcutKeyCombo } from '@/components/ShortcutKeyCombo'
+import { getScreenSubmitModifierLabel } from '@/lib/screen-submit-shortcut'
import { translate } from '@/i18n/i18n'
type TerminalQuickCommandDialogFooterProps = {
@@ -16,7 +18,7 @@ export function TerminalQuickCommandDialogFooter({
onSave
}: TerminalQuickCommandDialogFooterProps): React.JSX.Element {
return (
-
+
{translate(
'auto.components.terminal.quick.commands.TerminalQuickCommandDialogFooter.28370f16b9',
@@ -37,7 +39,11 @@ export function TerminalQuickCommandDialogFooter({
'auto.components.terminal.quick.commands.TerminalQuickCommandDialogFooter.2e2b958dfc',
'Save'
)}
- {submitShortcutLabel}
+
)
diff --git a/src/renderer/src/components/ui/textarea.tsx b/src/renderer/src/components/ui/textarea.tsx
new file mode 100644
index 00000000000..5e78f5555ef
--- /dev/null
+++ b/src/renderer/src/components/ui/textarea.tsx
@@ -0,0 +1,28 @@
+import * as React from 'react'
+
+import { cn } from '@/lib/utils'
+
+const Textarea = React.forwardRef>(
+ ({ className, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+
+Textarea.displayName = 'Textarea'
+
+export { Textarea }
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json
index a1361ab527b..c10727a620d 100644
--- a/src/renderer/src/i18n/locales/en.json
+++ b/src/renderer/src/i18n/locales/en.json
@@ -6781,6 +6781,12 @@
"8bfdd23a88": "Help us figure out what to build next. Orca sends anonymous counts of which features you use and where things break.",
"afec8b03be": "ci"
},
+ "QuickCommandsList": {
+ "noSearchMatches": "No commands match this search."
+ },
+ "QuickCommandsToolbar": {
+ "searchLabel": "Search commands"
+ },
"QuickCommandsPane": {
"8764c6e9e4": "Remove {{value0}}",
"7d90fd5299": "Edit {{value0}}",