Files
ComfyUI_frontend/src/components/dialog/content/setting/KeybindingPanel.vue
jaeone94 1ab9752af8 fix: keep Reka overlays above PrimeVue dialogs (#12038)
## Summary

Temporarily patch FE-569 by keeping the affected portaled Reka dropdowns
and menus above their containing PrimeVue dialogs when PrimeVue auto
z-index state has been elevated.

## Changes

- **What**: Added a small compatibility helper,
`usePrimeVueOverlayChildStyle`, that returns an anchor ref plus a
computed inline style for child popover content. The helper finds the
nearest PrimeVue dialog mask (`.p-dialog-mask` / `.p-overlay-mask`) from
the parent surface and, only when found, applies `parent z-index + 1` to
the affected Reka overlay content.
- **What**: Applied that helper at the exact PrimeVue parent surfaces
where the issue was found. This PR does not add a global overlay policy
and does not change every Reka select/dropdown in the app.
- **What**: Added optional `contentStyle`/`selectContentStyle` plumbing
only where needed so the style reaches the actual portaled Reka overlay
root.
- **What**: Added focused unit coverage for the helper contract: no
PrimeVue parent preserves existing stacking, PrimeVue dialog/overlay
masks render child content above the parent, low parent z-index values
respect the Reka floor, and invalid z-index values do not inject an
inline override.
- **Approach**: This is intentionally a minimal, parent-scoped band-aid.
It avoids a global PrimeVue overlay scanner because global sampling can
be polluted by unrelated persistent PrimeVue roots such as Toast and
would turn this fix into a broader layering policy.
- **Approach**: The patch targets the confirmed failure mode: a Reka
child overlay rendering below its owning PrimeVue dialog after PrimeVue
autoZIndex has been elevated. It does not attempt to solve PrimeVue
z-index globally.
- **Lifecycle**: This is temporary migration compatibility. PrimeVue
dialogs and controls are being incrementally migrated to Reka UI, so
`usePrimeVueOverlayChildStyle` and the optional style props added for
FE-569 should be removed once the affected parent surfaces move to Reka.
- **Breaking**: None. New props are optional and no public API contract
is changed.
- **Dependencies**: None.

## Patched Entry Points

This PR pinpoints the six affected user-facing surfaces below. Each
patch is applied from the PrimeVue dialog parent and passed only to the
Reka child overlay content that can render underneath that parent.


https://github.com/user-attachments/assets/d0d1522a-ffc7-4934-9e7a-06b83e20f809

1. **Workflow Template Library filters**
- **How to enter**: click the Templates button in the left sidebar, or
open the Comfy menu and choose **Browse Templates**.
- **Affected elements**: the template filter popovers in
`WorkflowTemplateSelectorDialog`: **Model**, **Use case**, **Runs on**,
and **Sort by**.
- **Patch point**: `WorkflowTemplateSelectorDialog.vue` anchors to the
template dialog content filter area and passes `selectContentStyle` to
the affected `MultiSelect` / `SingleSelect` controls.


https://github.com/user-attachments/assets/3641fa24-da51-4392-a904-9085f8a5a2f4

2. **Manager dialog header controls**
- **How to enter**: open Manager from the top/menu Manager entry when
the new Manager UI is available.
- **Affected elements**: the Manager header controls in `ManagerDialog`:
search mode `SingleSelect`, search autocomplete suggestions, and
**Sort** `SingleSelect`.
- **Patch point**: `ManagerDialog.vue` anchors to the dialog header and
passes `selectContentStyle` to those three Reka overlays.


https://github.com/user-attachments/assets/cf25cc06-f851-48ef-9d9c-9ec2da8afc06

3. **Asset Browser filter bar**
- **How to enter**: open the Asset Browser from an eligible model widget
browse action, the Model Library flow, or another
`useAssetBrowserDialog` caller.
- **Affected elements**: `AssetFilterBar` controls: **File formats**,
**Base models**, **Ownership**, and **Sort by**.
- **Patch point**: `AssetBrowserModal.vue` anchors to the PrimeVue
dialog header and passes the style through `AssetFilterBar` to its
`MultiSelect` / `SingleSelect` controls.


https://github.com/user-attachments/assets/e27bd805-10c0-4b3b-97f3-9e11faa47021

4. **Asset Browser model info panel**
- **How to enter**: open Asset Browser, select an asset, then use the
right-side model info panel.
- **Affected element**: the **Model type** select in `ModelInfoPanel`.
- **Patch point**: `AssetBrowserModal.vue` reuses the same parent-scoped
style and passes it to `ModelInfoPanel` as `selectContentStyle`.


https://github.com/user-attachments/assets/5e9f7ef0-ebd7-4987-ba1b-2137c034086f

5. **Upload Model confirmation step**
- **How to enter**: open Asset Browser, click **Upload**, enter/fetch
model metadata, then proceed to the confirmation step.
- **Affected element**: the **Model type** `SingleSelect` in
`UploadModelConfirmation`.
- **Patch point**: `UploadModelConfirmation.vue` anchors within the
upload dialog content and passes `selectContentStyle` to the model type
selector.



https://github.com/user-attachments/assets/ec145f26-8621-455b-915e-bedee47e1cbd

6. **Settings > Keybinding panel controls**
- **How to enter**: open Settings from the sidebar/menu, then select the
**Keybinding** panel.
- **Affected elements**: the keybinding preset select, the preset
overflow dropdown menu, and the row context menu inside
`KeybindingPanel`.
- **Patch point**: `KeybindingPanel.vue` anchors to the settings dialog
panel and passes `keybindingOverlayContentStyle` only to those Reka
overlay roots.

## Review Focus

- Confirm the patch stays narrowly scoped to the six known PrimeVue
parent + Reka child overlay surfaces above.
- Confirm `contentStyle` reaches the actual portaled Reka overlay
content in each patched path.
- Confirm the fallback behavior preserves existing stacking when no
PrimeVue parent overlay is found; in that case the helper returns an
empty style object and leaves existing Tailwind z-index classes alone.
- Please avoid expanding this into a larger overlay refactor. The goal
is a clean, backport-friendly compatibility patch while the Reka
migration continues.

Validation performed:

- `pnpm exec vitest run src/composables/usePopoverSizing.test.ts`
- `pnpm typecheck`
- `pnpm lint` (passes with existing unrelated warnings only)
- `pnpm format:check`
- commit hook lint-staged checks (`oxfmt`, `stylelint`, `oxlint`,
`eslint --fix`, `pnpm typecheck`)
- pre-push `pnpm knip`

Linear: FE-569

## Bug Screenshots 



https://github.com/user-attachments/assets/e73761af-9867-4c50-ab0d-4e32e59011e1



https://github.com/user-attachments/assets/145daf4d-3268-428b-9987-1e1afd0b866f


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12038-fix-keep-Reka-overlays-above-PrimeVue-dialogs-3596d73d365081e7af49dbc4d3905962)
by [Unito](https://www.unito.io)
2026-05-07 13:37:08 +09:00

644 lines
21 KiB
Vue

<template>
<div
:ref="primeVueOverlay.overlayScopeRef"
class="keybinding-panel flex flex-col gap-2"
>
<Teleport defer to="#keybinding-panel-header">
<SearchInput
v-model="filters['global'].value"
class="max-w-96"
size="lg"
:placeholder="
$t('g.searchPlaceholder', { subject: $t('g.keybindings') })
"
/>
</Teleport>
<Teleport defer to="#keybinding-panel-actions">
<div class="flex items-center gap-2">
<KeybindingPresetToolbar
:preset-names="presetNames"
:content-style="keybindingOverlayContentStyle"
@presets-changed="refreshPresetList"
/>
<DropdownMenu
:entries="menuEntries"
:style="keybindingOverlayContentStyle"
icon="icon-[lucide--ellipsis]"
item-class="text-sm gap-2"
button-size="unset"
button-class="size-10"
to="#keybinding-panel-actions"
align="end"
>
<template #button>
<Button
size="unset"
class="size-10"
data-testid="keybinding-preset-menu"
>
<i class="icon-[lucide--ellipsis]" />
</Button>
</template>
</DropdownMenu>
</div>
</Teleport>
<ContextMenuRoot>
<ContextMenuTrigger as-child>
<div @contextmenu.capture="clearContextMenuTarget">
<DataTable
v-model:selection="selectedCommandData"
v-model:expanded-rows="expandedRows"
:value="commandsData"
data-key="id"
:global-filter-fields="['id', 'label']"
:filters="filters"
:paginator="true"
:rows="50"
:rows-per-page-options="[25, 50, 100]"
selection-mode="single"
context-menu
striped-rows
:pt="{
header: 'px-0'
}"
@row-click="handleRowClick($event)"
@row-dblclick="handleRowDblClick($event.data)"
@row-contextmenu="handleRowContextMenu($event)"
>
<Column
field="id"
:header="$t('g.command')"
sortable
class="max-w-64 2xl:max-w-full"
:pt="{ bodyCell: 'p-1 min-h-8' }"
>
<template #body="slotProps">
<div
class="flex items-center gap-1 truncate"
:class="slotProps.data.keybindings.length < 2 && 'pl-5'"
:title="slotProps.data.id"
>
<i
v-if="slotProps.data.keybindings.length >= 2"
class="icon-[lucide--chevron-right] size-4 shrink-0 text-muted-foreground transition-transform"
:class="
expandedCommandIds.has(slotProps.data.id) && 'rotate-90'
"
/>
<i
v-if="
slotProps.data.keybindings.some(
(b: KeybindingImpl) => b.combo.isBrowserReserved
)
"
v-tooltip="$t('g.browserReservedKeybindingTooltip')"
class="icon-[lucide--triangle-alert] shrink-0 text-warning-background"
/>
{{ slotProps.data.label }}
</div>
</template>
</Column>
<Column
field="keybindings"
:header="$t('g.keybinding')"
:pt="{ bodyCell: 'p-1 min-h-8' }"
>
<template #body="slotProps">
<div
v-if="slotProps.data.keybindings.length > 0"
class="flex items-center gap-1"
>
<template
v-for="(binding, idx) in (
slotProps.data as ICommandData
).keybindings.slice(0, 2)"
:key="binding.combo.serialize()"
>
<span v-if="idx > 0" class="text-muted-foreground">,</span>
<KeyComboDisplay
:key-combo="binding.combo"
:is-modified="slotProps.data.isModified"
/>
</template>
<span
v-if="slotProps.data.keybindings.length > 2"
class="rounded-sm px-1.5 py-0.5 text-xs text-muted-foreground"
>
{{
$t('g.nMoreKeybindings', {
count: slotProps.data.keybindings.length - 2
})
}}
</span>
</div>
<span v-else>-</span>
</template>
</Column>
<Column
field="source"
:header="$t('g.source')"
:pt="{ bodyCell: 'p-1 min-h-8' }"
>
<template #body="slotProps">
<span class="overflow-hidden text-ellipsis">{{
slotProps.data.source || '-'
}}</span>
</template>
</Column>
<Column field="actions" header="" :pt="{ bodyCell: 'p-1 min-h-8' }">
<template #body="slotProps">
<div class="actions flex flex-row justify-end">
<Button
v-if="slotProps.data.keybindings.length === 1"
v-tooltip="$t('g.edit')"
variant="textonly"
size="icon"
:aria-label="$t('g.edit')"
@click="
editKeybinding(
slotProps.data,
slotProps.data.keybindings[0]
)
"
>
<i class="icon-[lucide--pencil]" />
</Button>
<Button
v-tooltip="$t('g.addNewKeybinding')"
variant="textonly"
size="icon"
:aria-label="$t('g.addNewKeybinding')"
@click="addKeybinding(slotProps.data)"
>
<i class="icon-[lucide--plus]" />
</Button>
<Button
v-tooltip="$t('g.reset')"
variant="textonly"
size="icon"
:aria-label="$t('g.reset')"
:disabled="!slotProps.data.isModified"
@click="resetKeybinding(slotProps.data)"
>
<i class="icon-[lucide--rotate-ccw]" />
</Button>
<Button
v-tooltip="$t('g.delete')"
variant="textonly"
size="icon"
:aria-label="$t('g.delete')"
:disabled="slotProps.data.keybindings.length === 0"
@click="handleRemoveKeybindingFromMenu(slotProps.data)"
>
<i class="icon-[lucide--trash-2]" />
</Button>
</div>
</template>
</Column>
<template #expansion="slotProps">
<div class="pl-4" data-testid="keybinding-expansion-content">
<div
v-for="(binding, idx) in (slotProps.data as ICommandData)
.keybindings"
:key="binding.combo.serialize()"
data-testid="keybinding-expansion-binding"
class="flex items-center justify-between border-b border-border-subtle py-1.5 last:border-b-0"
>
<div class="flex items-center gap-4">
<span class="text-muted-foreground">{{
slotProps.data.label
}}</span>
<KeyComboDisplay
:key-combo="binding.combo"
:is-modified="slotProps.data.isModified"
/>
</div>
<div class="flex flex-row">
<Button
v-tooltip="$t('g.edit')"
variant="textonly"
size="icon"
:aria-label="$t('g.edit')"
@click="editKeybinding(slotProps.data, binding)"
>
<i class="icon-[lucide--pencil]" />
</Button>
<Button
v-tooltip="$t('g.removeKeybinding')"
variant="textonly"
size="icon"
:aria-label="$t('g.removeKeybinding')"
@click="removeSingleKeybinding(slotProps.data, idx)"
>
<i class="icon-[lucide--trash-2]" />
</Button>
</div>
</div>
</div>
</template>
</DataTable>
</div>
</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent
:style="keybindingOverlayContentStyle"
class="z-1200 min-w-56 rounded-lg border border-border-subtle bg-base-background px-2 py-3 shadow-interface"
>
<ContextMenuItem
class="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 text-sm text-text-primary outline-none select-none hover:bg-node-component-surface-hovered focus:bg-node-component-surface-hovered data-disabled:cursor-default data-disabled:opacity-50"
:disabled="
!contextMenuTarget || contextMenuTarget.keybindings.length === 0
"
@select="ctxChangeKeybinding"
>
<i class="icon-[lucide--pencil] size-4" />
{{ $t('g.changeKeybinding') }}
</ContextMenuItem>
<ContextMenuItem
class="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 text-sm text-text-primary outline-none select-none hover:bg-node-component-surface-hovered focus:bg-node-component-surface-hovered"
@select="ctxAddKeybinding"
>
<i class="icon-[lucide--plus] size-4" />
{{ $t('g.addNewKeybinding') }}
</ContextMenuItem>
<ContextMenuSeparator class="my-1 h-px bg-border-subtle" />
<ContextMenuItem
class="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 text-sm text-text-primary outline-none select-none hover:bg-node-component-surface-hovered focus:bg-node-component-surface-hovered data-disabled:cursor-default data-disabled:opacity-50"
:disabled="!contextMenuTarget?.isModified"
@select="ctxResetToDefault"
>
<i class="icon-[lucide--rotate-ccw] size-4" />
{{ $t('g.resetToDefault') }}
</ContextMenuItem>
<ContextMenuItem
class="flex cursor-pointer items-center gap-2 rounded-sm px-3 py-2 text-sm text-text-primary outline-none select-none hover:bg-node-component-surface-hovered focus:bg-node-component-surface-hovered data-disabled:cursor-default data-disabled:opacity-50"
:disabled="
!contextMenuTarget || contextMenuTarget.keybindings.length === 0
"
@select="ctxRemoveKeybinding"
>
<i class="icon-[lucide--trash-2] size-4" />
{{ $t('g.removeKeybinding') }}
</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
<Button
v-tooltip="$t('g.resetAllKeybindingsTooltip')"
class="mt-4 w-full"
variant="destructive-textonly"
@click="resetAllKeybindings"
>
<i class="icon-[lucide--rotate-ccw]" />
{{ $t('g.resetAll') }}
</Button>
</div>
</template>
<script setup lang="ts">
import type { MenuItem } from 'primevue/menuitem'
import { FilterMatchMode } from '@primevue/core/api'
import Column from 'primevue/column'
import DataTable from 'primevue/datatable'
import { useToast } from 'primevue/usetoast'
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger
} from 'reka-ui'
import { computed, onMounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import DropdownMenu from '@/components/common/DropdownMenu.vue'
import { showConfirmDialog } from '@/components/dialog/confirm/confirmDialog'
import Button from '@/components/ui/button/Button.vue'
import SearchInput from '@/components/ui/search-input/SearchInput.vue'
import { useEditKeybindingDialog } from '@/composables/useEditKeybindingDialog'
import { usePrimeVueOverlayChildStyle } from '@/composables/usePopoverSizing'
import type { KeybindingImpl } from '@/platform/keybindings/keybinding'
import { useKeybindingService } from '@/platform/keybindings/keybindingService'
import { useKeybindingStore } from '@/platform/keybindings/keybindingStore'
import { useKeybindingPresetService } from '@/platform/keybindings/presetService'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useCommandStore } from '@/stores/commandStore'
import { useDialogStore } from '@/stores/dialogStore'
import { normalizeI18nKey } from '@/utils/formatUtil'
import KeybindingPresetToolbar from './keybinding/KeybindingPresetToolbar.vue'
import KeyComboDisplay from './keybinding/KeyComboDisplay.vue'
const filters = ref({
global: { value: '', matchMode: FilterMatchMode.CONTAINS }
})
const keybindingStore = useKeybindingStore()
const keybindingService = useKeybindingService()
const presetService = useKeybindingPresetService()
const settingStore = useSettingStore()
const commandStore = useCommandStore()
const dialogStore = useDialogStore()
const { t } = useI18n()
const primeVueOverlay = usePrimeVueOverlayChildStyle()
const keybindingOverlayContentStyle = primeVueOverlay.contentStyle
const presetNames = ref<string[]>([])
async function refreshPresetList() {
presetNames.value = (await presetService.listPresets()) ?? []
}
async function initPresets() {
await refreshPresetList()
const currentName = settingStore.get('Comfy.Keybinding.CurrentPreset')
if (currentName !== 'default') {
const preset = await presetService.loadPreset(currentName)
if (preset) {
keybindingStore.savedPresetData = preset
keybindingStore.currentPresetName = currentName
} else {
await presetService.switchToDefaultPreset()
}
}
}
onMounted(() => initPresets())
// "..." menu entries (teleported to header)
async function saveAsNewPreset() {
await presetService.promptAndSaveNewPreset()
refreshPresetList()
}
async function handleDeletePreset() {
await presetService.deletePreset(keybindingStore.currentPresetName)
refreshPresetList()
}
async function handleImportPreset() {
await presetService.importPreset()
refreshPresetList()
}
const showSaveAsNew = computed(
() =>
keybindingStore.currentPresetName !== 'default' ||
keybindingStore.isCurrentPresetModified
)
const menuEntries = computed<MenuItem[]>(() => [
...(showSaveAsNew.value
? [
{
label: t('g.keybindingPresets.saveAsNewPreset'),
icon: 'icon-[lucide--save]',
command: saveAsNewPreset
}
]
: []),
{
label: t('g.keybindingPresets.resetToDefault'),
icon: 'icon-[lucide--rotate-cw]',
command: () =>
presetService.switchPreset('default').then(() => refreshPresetList())
},
{
label: t('g.keybindingPresets.deletePreset'),
icon: 'icon-[lucide--trash-2]',
disabled: keybindingStore.currentPresetName === 'default',
command: handleDeletePreset
},
{
label: t('g.keybindingPresets.importPreset'),
icon: 'icon-[lucide--file-input]',
command: handleImportPreset
},
{
label: t('g.keybindingPresets.exportPreset'),
icon: 'icon-[lucide--file-output]',
command: () => presetService.exportPreset()
}
])
// Keybinding table logic
interface ICommandData {
id: string
keybindings: KeybindingImpl[]
label: string
source?: string
isModified: boolean
}
const commandsData = computed<ICommandData[]>(() => {
return Object.values(commandStore.commands).map((command) => ({
id: command.id,
label: t(
`commands.${normalizeI18nKey(command.id)}.label`,
command.label ?? ''
),
keybindings: keybindingStore.getKeybindingsByCommandId(command.id),
source: command.source,
isModified: keybindingStore.isCommandKeybindingModified(command.id)
}))
})
const expandedCommandIds = ref<Set<string>>(new Set())
const expandedRows = computed({
get() {
const result: Record<string, boolean> = {}
for (const id of expandedCommandIds.value) {
result[id] = true
}
return result
},
set(value: Record<string, boolean>) {
expandedCommandIds.value = new Set(Object.keys(value))
}
})
function toggleExpanded(commandId: string) {
if (expandedCommandIds.value.has(commandId)) {
expandedCommandIds.value.delete(commandId)
} else {
expandedCommandIds.value.add(commandId)
}
}
watch(filters, () => expandedCommandIds.value.clear(), { deep: true })
const selectedCommandData = ref<ICommandData | null>(null)
const editKeybindingDialog = useEditKeybindingDialog()
const contextMenuTarget = ref<ICommandData | null>(null)
function editKeybinding(commandData: ICommandData, binding: KeybindingImpl) {
editKeybindingDialog.show({
commandId: commandData.id,
commandLabel: commandData.label,
currentCombo: binding.combo,
mode: 'edit',
existingBinding: binding
})
}
function addKeybinding(commandData: ICommandData) {
editKeybindingDialog.show({
commandId: commandData.id,
commandLabel: commandData.label,
currentCombo: null,
mode: 'add'
})
}
function handleRowClick(event: { originalEvent: Event; data: ICommandData }) {
const target = event.originalEvent.target as HTMLElement
if (target.closest('.actions')) return
const commandData = event.data
if (
commandData.keybindings.length >= 2 ||
expandedCommandIds.value.has(commandData.id)
) {
toggleExpanded(commandData.id)
}
}
function handleRowDblClick(commandData: ICommandData) {
if (commandData.keybindings.length === 0) {
addKeybinding(commandData)
} else if (commandData.keybindings.length === 1) {
editKeybinding(commandData, commandData.keybindings[0])
}
}
function handleRowContextMenu(event: {
originalEvent: Event
data: ICommandData
}) {
contextMenuTarget.value = event.data
}
function clearContextMenuTarget() {
contextMenuTarget.value = null
}
async function removeSingleKeybinding(
commandData: ICommandData,
index: number
) {
const binding = commandData.keybindings[index]
if (binding) {
keybindingStore.unsetKeybinding(binding)
if (commandData.keybindings.length <= 2) {
expandedCommandIds.value.delete(commandData.id)
}
await keybindingService.persistUserKeybindings()
}
}
function handleRemoveAllKeybindings(commandData: ICommandData) {
const dialog = showConfirmDialog({
headerProps: { title: t('g.removeAllKeybindingsTitle') },
props: { promptText: t('g.removeAllKeybindingsMessage') },
footerProps: {
confirmText: t('g.removeAll'),
confirmVariant: 'destructive',
onCancel: () => dialogStore.closeDialog(dialog),
onConfirm: async () => {
keybindingStore.removeAllKeybindingsForCommand(commandData.id)
await keybindingService.persistUserKeybindings()
dialogStore.closeDialog(dialog)
}
}
})
}
function handleRemoveKeybindingFromMenu(commandData: ICommandData) {
if (commandData.keybindings.length >= 2) {
handleRemoveAllKeybindings(commandData)
} else {
removeSingleKeybinding(commandData, 0)
}
}
function ctxChangeKeybinding() {
if (!contextMenuTarget.value) return
const target = contextMenuTarget.value
if (target.keybindings.length === 1) {
editKeybinding(target, target.keybindings[0])
} else if (target.keybindings.length >= 2) {
if (!expandedCommandIds.value.has(target.id)) {
toggleExpanded(target.id)
}
}
}
function ctxAddKeybinding() {
if (contextMenuTarget.value) {
addKeybinding(contextMenuTarget.value)
}
}
function ctxResetToDefault() {
if (contextMenuTarget.value) {
resetKeybinding(contextMenuTarget.value)
}
}
function ctxRemoveKeybinding() {
if (
contextMenuTarget.value &&
contextMenuTarget.value.keybindings.length > 0
) {
handleRemoveKeybindingFromMenu(contextMenuTarget.value)
}
}
async function resetKeybinding(commandData: ICommandData) {
if (keybindingStore.resetKeybindingForCommand(commandData.id)) {
expandedCommandIds.value.delete(commandData.id)
await keybindingService.persistUserKeybindings()
} else {
console.warn(
`No changes made when resetting keybinding for command: ${commandData.id}`
)
}
}
const toast = useToast()
function resetAllKeybindings() {
const dialog = showConfirmDialog({
headerProps: {
title: t('g.resetAllKeybindingsTitle')
},
props: {
promptText: t('g.resetAllKeybindingsMessage')
},
footerProps: {
confirmText: t('g.resetAll'),
confirmVariant: 'destructive',
onCancel: () => {
dialogStore.closeDialog(dialog)
},
onConfirm: async () => {
keybindingStore.resetAllKeybindings()
await keybindingService.persistUserKeybindings()
dialogStore.closeDialog(dialog)
toast.add({
severity: 'info',
summary: t('g.info'),
detail: t('g.allKeybindingsReset'),
life: 3000
})
}
}
})
}
</script>