From 7f65b1b395521b57e1a28da2bece8f2a09c0d8bf Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 19 May 2026 22:59:44 -0400 Subject: [PATCH] CMDPalette: Migrate to new ui for modals --- .../commandPalette/CommandPicker.tsx | 56 +++- .../commandPalette/runtime/registryStore.ts | 8 +- src/equicordplugins/commandPalette/style.css | 78 +++--- .../commandPalette/ui/CommandPaletteModal.tsx | 257 +++++++++--------- 4 files changed, 210 insertions(+), 189 deletions(-) diff --git a/src/equicordplugins/commandPalette/CommandPicker.tsx b/src/equicordplugins/commandPalette/CommandPicker.tsx index 746e6eeec..152ebcfc4 100644 --- a/src/equicordplugins/commandPalette/CommandPicker.tsx +++ b/src/equicordplugins/commandPalette/CommandPicker.tsx @@ -4,11 +4,12 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { Heading } from "@components/Heading"; import { CogWheel } from "@components/Icons"; import { classNameFactory } from "@utils/css"; import { classes } from "@utils/misc"; import { RenderModalProps } from "@vencord/discord-types"; -import { Modal,openModal, TextInput, useCallback, useEffect, useMemo, useRef, useState } from "@webpack/common"; +import { Modal, openModal, TextInput, useCallback, useEffect, useMemo, useRef, useState } from "@webpack/common"; import { type CommandEntry, getCategoryGroupLabel, getCategoryWeight, getCommandSearchText, getRecentRank, getRegistryVersion, listCommands, subscribeRegistry @@ -64,6 +65,7 @@ function CommandPickerModal({ modalProps, commands: providedCommands, allowMulti const [pickedIds, setPickedIds] = useState(() => normalizedInitialSelected); const itemRefs = useRef<(HTMLButtonElement | null)[]>([]); const pickedIdsRef = useRef([]); + const listRef = useRef(null); useEffect(() => { if (providedCommands) return; @@ -142,7 +144,12 @@ function CommandPickerModal({ modalProps, commands: providedCommands, allowMulti for (const entry of availableCommands) { if (!includeHiddenInSearch && entry.hiddenInSearch) continue; - const searchText = getCommandSearchText(entry.id); + const searchText = getCommandSearchText(entry.id) || [ + entry.label, + entry.description ?? "", + ...entry.keywords ?? [], + ...entry.tags ?? [] + ].filter(Boolean).join(" ").toLowerCase(); if (!searchText.includes(lowerQuery)) continue; const score = scoreCommand(entry); @@ -203,8 +210,22 @@ function CommandPickerModal({ modalProps, commands: providedCommands, allowMulti useEffect(() => { if (selectedIndex < 0) return; + const rawContainer = listRef.current as any; + const container = rawContainer?.getScrollerNode ? rawContainer.getScrollerNode() : rawContainer; const node = itemRefs.current[selectedIndex]; - node?.scrollIntoView({ block: "nearest" }); + if (!container || !node) return; + + const containerRect = container.getBoundingClientRect(); + const nodeRect = node.getBoundingClientRect(); + + if (nodeRect.bottom > containerRect.bottom) { + container.scrollTop += nodeRect.bottom - containerRect.bottom; + return; + } + + if (nodeRect.top < containerRect.top) { + container.scrollTop -= containerRect.top - nodeRect.top; + } }, [selectedIndex, items]); itemRefs.current = itemRefs.current.slice(0, items.length); @@ -321,17 +342,26 @@ function CommandPickerModal({ modalProps, commands: providedCommands, allowMulti }; return ( - -
-
- + + Command Palette + +
+ +
+ } + > +
{!hasCommands &&
{emptyStateText}
} {items.map((item, index) => { diff --git a/src/equicordplugins/commandPalette/runtime/registryStore.ts b/src/equicordplugins/commandPalette/runtime/registryStore.ts index 9a06a96de..030109615 100644 --- a/src/equicordplugins/commandPalette/runtime/registryStore.ts +++ b/src/equicordplugins/commandPalette/runtime/registryStore.ts @@ -20,7 +20,11 @@ export interface RegistryStore { getTreeCommandCache(): Map; } +let instance: RegistryStore | null = null; + export function createRegistryStore(): RegistryStore { + if (instance) return instance; + let registryVersion = 0; let cachedSortedCommands: CommandEntry[] | null = null; const categoryCommandCache = new Map(); @@ -38,7 +42,7 @@ export function createRegistryStore(): RegistryStore { for (const listener of registryListeners) listener(registryVersion); }; - return { + instance = { getRegistryVersion: () => registryVersion, bumpRegistryVersion: () => { registryVersion += 1; @@ -77,4 +81,6 @@ export function createRegistryStore(): RegistryStore { getCategoryCommandCache: () => categoryCommandCache, getTreeCommandCache: () => treeCommandCache }; + + return instance; } diff --git a/src/equicordplugins/commandPalette/style.css b/src/equicordplugins/commandPalette/style.css index 8a1482da1..d6df8cc2e 100644 --- a/src/equicordplugins/commandPalette/style.css +++ b/src/equicordplugins/commandPalette/style.css @@ -6,9 +6,8 @@ /* Command Palette Modal Container */ .vc-command-palette { - --cp-bg: #0f0f10; - --cp-bg-secondary: #18181a; - --cp-bg-tertiary: #1f1f21; + --cp-bg-secondary: transparent; + --cp-bg-tertiary: transparent; --cp-bg-dropdown: #0b0b0d; --cp-bg-hover: #252528; --cp-bg-selected: #2a2a2e; @@ -26,10 +25,6 @@ --cp-radius-sm: 8px; --cp-shadow: 0 16px 44px rgb(0 0 0 / 38%), 0 6px 18px rgb(0 0 0 / 28%); - background: var(--cp-bg) !important; - border-radius: var(--cp-radius) !important; - box-shadow: var(--cp-shadow) !important; - border: 1px solid var(--cp-border) !important; overflow: visible !important; box-sizing: border-box; will-change: opacity, transform; @@ -42,19 +37,19 @@ display: flex; flex-direction: column; width: 100%; - height: 100%; - background: var(--cp-bg); - border-radius: var(--cp-radius); box-sizing: border-box; position: relative; - overflow: hidden; } /* Input Area */ .vc-command-palette-input { - padding: 12px 22px 10px; - border-bottom: 1px solid var(--cp-border); - position: relative; + border-bottom: none; + position: sticky; + top: 0; + z-index: 10; + width: 105%; + box-sizing: border-box; + background: var(--bg-overlay-3, var(--background-secondary)); } .vc-command-palette-main-input input { @@ -299,21 +294,8 @@ .vc-command-palette-list { flex: 1 1 auto; min-height: 0; - overflow-y: auto; padding: 0; -} - -.vc-command-palette-list::-webkit-scrollbar { - width: 6px; -} - -.vc-command-palette-list::-webkit-scrollbar-track { - background: transparent; -} - -.vc-command-palette-list::-webkit-scrollbar-thumb { - background: var(--cp-bg-tertiary); - border-radius: 3px; + width: 100%; } /* Section Labels */ @@ -431,24 +413,18 @@ align-items: center; justify-content: space-between; padding: 10px 20px; - background: var(--cp-bg); - border-top: 1px solid var(--cp-border); gap: 16px; min-height: 48px; -} - -.vc-command-palette-picker .vc-command-palette-shell { - max-height: 62vh; + position: sticky; + bottom: 0; + z-index: 10; + background: var(--bg-overlay-3, var(--background-secondary)); } .vc-command-palette-picker .vc-command-palette-input { padding: 12px 16px 10px; } -.vc-command-palette-picker .vc-command-palette-list { - max-height: 54vh; -} - .vc-command-palette-action-bar-action { display: flex; align-items: center; @@ -516,11 +492,13 @@ .vc-command-palette-calculator { padding: 12px 16px; background: transparent; - border-bottom: 1px solid var(--cp-border); + border-bottom: none; } + + .vc-command-palette-calculator-title { - font-size: 13px; + font-size: 14px; font-weight: 600; color: var(--cp-text-secondary); margin: 0 0 10px; @@ -1023,4 +1001,24 @@ input.vc-command-palette-main-search-input:read-only, input.vc-command-palette-main-search-input:not(:read-only), .vc-command-palette-main-search-input input:not(:read-only) { color: var(--cp-text-primary) !important; +} + +.vc-command-palette-header { + display: flex; + flex-direction: column; + gap: 12px; + width: 100%; +} + +.vc-command-palette-header-title { + color: var(--header-primary); + font-size: 20px; + font-weight: 600; +} + +.vc-command-palette-header .vc-command-palette-input { + position: static; + border-bottom: none; + background: transparent; + padding: 0; } \ No newline at end of file diff --git a/src/equicordplugins/commandPalette/ui/CommandPaletteModal.tsx b/src/equicordplugins/commandPalette/ui/CommandPaletteModal.tsx index 03ccff695..2f8f232ea 100644 --- a/src/equicordplugins/commandPalette/ui/CommandPaletteModal.tsx +++ b/src/equicordplugins/commandPalette/ui/CommandPaletteModal.tsx @@ -6,6 +6,7 @@ import "../style.css"; +import { Heading } from "@components/Heading"; import { IS_MAC } from "@utils/constants"; import { classNameFactory, classNameToSelector } from "@utils/css"; import { copyWithToast } from "@utils/discord"; @@ -347,24 +348,9 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: R useEffect(() => { return () => { - if (closeReasonRef.current === "explicit-root") { - clearPersistedNavigation(); - return; - } - - if (closeReasonRef.current === "programmatic") { - clearPersistedNavigation(); - return; - } - - if (navigationLevel.type === "category") { - persistedCategoryId = navigationLevel.categoryId; - return; - } - clearPersistedNavigation(); }; - }, [navigationLevel]); + }, []); useEffect(() => { setIsActionsMenuOpen(false); @@ -630,7 +616,8 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: R useEffect(() => { if (selectedIndex < 0) return; if (selectionSource !== "keyboard") return; - const container = listRef.current; + const rawContainer = listRef.current as any; + const container = rawContainer?.getScrollerNode ? rawContainer.getScrollerNode() : rawContainer; const node = itemRefs.current[selectedIndex]; if (!container || !node) return; @@ -658,8 +645,10 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: R setSelectedKey(null); setKeyboardSelectedKey(null); - if (listRef.current) { - listRef.current.scrollTop = 0; + const rawContainer = listRef.current as any; + const container = rawContainer?.getScrollerNode ? rawContainer.getScrollerNode() : rawContainer; + if (container) { + container.scrollTop = 0; } }, [isPageOpen, trimmedQuery]); @@ -758,7 +747,6 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: R setSelectedKey(null); setKeyboardSelectedKey(null); setQuery(""); - setExpanded(false); }; const setActivePageValue = (fieldKey: string, value: string) => { @@ -873,7 +861,6 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: R setSelectedKey(null); setKeyboardSelectedKey(null); setQuery(""); - setExpanded(false); return true; }; @@ -972,7 +959,6 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: R setSelectedKey(null); setKeyboardSelectedKey(null); setQuery(""); - setExpanded(false); }; const updateSelection = (index: number, item: PaletteCandidate) => { @@ -1045,7 +1031,6 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: R clearPromptState(); setSelectedKey(null); setQuery(""); - setExpanded(false); }; const onKeyDown = async (event: ReactKeyboardEvent) => { @@ -1439,121 +1424,123 @@ export function CommandPaletteModal({ modalProps, instanceKey }: { modalProps: R + Command Palette + + {!isPageOpen && ( + { + if (activePromptCommand && value.length > 0) { + clearPromptState(); + } + + const next = value.trim(); + setQuery(value); + + if (next.length === 0) { + if (navigationLevel.type !== "root") { + goBack(); + return; + } + } + }} + placeholder={mainInputPlaceholder} + readOnly={false} + > + {shouldRenderPromptPreview && promptCommand && ( +
+ {shouldShowPromptCommandLabel && ( + {promptCommand.label} + )} +
+ {activePromptCommand ? ( +
setShowPromptDropdown(true)} + > + { + setPromptInputValue(value); + setShowPromptDropdown(true); + if (selectedPromptCandidateId) { + setSelectedPromptCandidateId(null); + } + }} + placeholder={selectedPromptCandidate ? "" : (promptCommand.queryPlaceholder ?? "Action")} + onFocus={() => setShowPromptDropdown(true)} + onBlur={() => { + window.setTimeout(() => { + const active = document.activeElement; + if (promptContainerRef.current?.contains(active)) return; + setShowPromptDropdown(false); + if (selectedPromptCandidateId || promptInputValue.trim().length > 0) return; + clearPromptState(); + }, 0); + }} + /> +
+ ) : ( + + )} + {shouldShowPromptDropdown && ( + { + const candidate = promptDropdownSuggestions[index]; + if (!candidate) return; + setSelectedPromptCandidateId(candidate.id); + }} + onPick={candidate => { + setSelectedPromptCandidateId(candidate.id); + if (activePromptIsSingleSelect) { + setPromptInputValue(candidate.label); + } else { + setPromptInputValue(""); + } + if (activePromptIsSingleSelect) { + setShowPromptDropdown(false); + } + setFocusPromptInput(true); + }} + /> + )} +
+
+ )} +
+ )} + + {!isPageOpen && calculatorResult && ( + + )} +
+ } >
{!isPageOpen && ( - { - if (activePromptCommand && value.length > 0) { - clearPromptState(); - } - - const next = value.trim(); - setQuery(value); - - if (next.length > 0) { - setExpanded(true); - } else { - if (navigationLevel.type !== "root") { - goBack(); - return; - } - setExpanded(false); - } - }} - placeholder={mainInputPlaceholder} - readOnly={false} - > - {shouldRenderPromptPreview && promptCommand && ( -
- {shouldShowPromptCommandLabel && ( - {promptCommand.label} - )} -
- {activePromptCommand ? ( -
setShowPromptDropdown(true)} - > - { - setPromptInputValue(value); - setShowPromptDropdown(true); - if (selectedPromptCandidateId) { - setSelectedPromptCandidateId(null); - } - }} - placeholder={selectedPromptCandidate ? "" : (promptCommand.queryPlaceholder ?? "Action")} - onFocus={() => setShowPromptDropdown(true)} - onBlur={() => { - window.setTimeout(() => { - const active = document.activeElement; - if (promptContainerRef.current?.contains(active)) return; - setShowPromptDropdown(false); - if (selectedPromptCandidateId || promptInputValue.trim().length > 0) return; - clearPromptState(); - }, 0); - }} - /> -
- ) : ( - - )} - {shouldShowPromptDropdown && ( - { - const candidate = promptDropdownSuggestions[index]; - if (!candidate) return; - setSelectedPromptCandidateId(candidate.id); - }} - onPick={candidate => { - setSelectedPromptCandidateId(candidate.id); - if (activePromptIsSingleSelect) { - setPromptInputValue(candidate.label); - } else { - setPromptInputValue(""); - } - if (activePromptIsSingleSelect) { - setShowPromptDropdown(false); - } - setFocusPromptInput(true); - }} - /> - )} -
-
- )} -
- )} - - {!isPageOpen && calculatorResult && ( - - )} - - {!isPageOpen && ( -
+
{!hasCommandItems &&
{emptyStateText}
} {items.map((item, index) => { if (item.type === "section") {