parent
f3b133ee78
commit
91c9d4a4a2
3 changed files with 92 additions and 106 deletions
|
|
@ -5,10 +5,11 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { Button } from "@components/Button";
|
||||
import { EquicordDevs, IS_MAC } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Button, Text, useEffect, useRef, useState } from "@webpack/common";
|
||||
import { useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { cleanupCommandPaletteRuntime, registerBuiltInCommands, wrapChatBarChildren } from "./registry";
|
||||
import { CommandPaletteSettingsPanel } from "./settingsPanel";
|
||||
|
|
@ -16,7 +17,6 @@ import { openCommandPalette } from "./ui";
|
|||
|
||||
const DEFAULT_KEYS = IS_MAC ? ["Meta", "Shift", "P"] : ["Control", "Shift", "P"];
|
||||
|
||||
const cl = classNameFactory("vc-cp-");
|
||||
const isRecordingGlobal = false;
|
||||
let openScheduled = false;
|
||||
|
||||
|
|
@ -37,7 +37,6 @@ function formatKeybind(keybind: string | string[]): string {
|
|||
function KeybindRecorder() {
|
||||
const [isListening, setIsListening] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const currentKeybind = settings.use(["hotkey"]).hotkey;
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -52,11 +51,11 @@ function KeybindRecorder() {
|
|||
}
|
||||
|
||||
const keys: string[] = [];
|
||||
if (IS_MAC && event.ctrlKey) {
|
||||
if (event.ctrlKey) keys.push("CONTROL");
|
||||
if (event.metaKey) keys.push("CTRL");
|
||||
} else if (event.ctrlKey) {
|
||||
keys.push("CTRL");
|
||||
if (event.metaKey) {
|
||||
keys.push("META");
|
||||
}
|
||||
if (event.ctrlKey) {
|
||||
keys.push(IS_MAC ? "CONTROL" : "CTRL");
|
||||
}
|
||||
if (event.shiftKey) keys.push("SHIFT");
|
||||
if (event.altKey) keys.push("ALT");
|
||||
|
|
@ -79,8 +78,6 @@ function KeybindRecorder() {
|
|||
document.addEventListener("keydown", handleKeyDown, true);
|
||||
window.addEventListener("blur", handleBlur);
|
||||
|
||||
buttonRef.current?.focus();
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown, true);
|
||||
window.removeEventListener("blur", handleBlur);
|
||||
|
|
@ -93,31 +90,34 @@ function KeybindRecorder() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="vc-cp-keybind-input">
|
||||
<div className="vc-cp-keybind-info">
|
||||
<Text variant="text-md/semibold">Command Palette Hotkey</Text>
|
||||
<Text variant="text-sm/normal" style={{ color: "var(--text-muted)" }}>
|
||||
<div className="vc-command-palette-keybind-input">
|
||||
<div className="vc-command-palette-keybind-info">
|
||||
<BaseText size="md" weight="semibold">Command Palette Hotkey</BaseText>
|
||||
<BaseText size="sm" weight="normal" style={{ color: "var(--text-muted)" }}>
|
||||
Hotkey used to open the command palette
|
||||
</Text>
|
||||
</BaseText>
|
||||
{error && (
|
||||
<Text variant="text-xs/normal" className="vc-cp-keybind-conflict">
|
||||
<BaseText size="xs" weight="normal" className="vc-command-palette-keybind-conflict">
|
||||
{error}
|
||||
</Text>
|
||||
</BaseText>
|
||||
)}
|
||||
</div>
|
||||
<div className="vc-cp-keybind-controls">
|
||||
<button
|
||||
ref={buttonRef}
|
||||
className={`vc-cp-keybind-button ${isListening ? "listening" : ""}`}
|
||||
<div className="vc-command-palette-keybind-controls">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
className={`vc-command-palette-keybind-button ${isListening ? "listening" : ""}`}
|
||||
onClick={() => setIsListening(true)}
|
||||
>
|
||||
{isListening ? "Press any key..." : formatKeybind(currentKeybind)}
|
||||
</button>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.PRIMARY}
|
||||
onClick={handleReset}
|
||||
>
|
||||
{isListening ? (
|
||||
<BaseText size="sm" weight="normal" style={{ color: "var(--white)", opacity: 0.8 }}>
|
||||
Press any key...
|
||||
</BaseText>
|
||||
) : (
|
||||
formatKeybind(currentKeybind)
|
||||
)}
|
||||
</Button>
|
||||
<Button size="small" variant="secondary" onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Button, TextButton } from "@components/Button";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { Switch } from "@components/Switch";
|
||||
import { Button, Forms, React, Select, TextInput } from "@webpack/common";
|
||||
import { React, Select, TextInput } from "@webpack/common";
|
||||
|
||||
import { openCommandPicker } from "./CommandPicker";
|
||||
import {
|
||||
|
|
@ -169,7 +172,7 @@ function renderActionFields(command: CustomCommandDefinition, onChange: (action:
|
|||
onChange={value => onChange({ type: "command", commandId: value })}
|
||||
/>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
size="small"
|
||||
onClick={() => openCommandPicker({
|
||||
initialQuery: action.commandId,
|
||||
onSelect: command => onChange({ type: "command", commandId: command.id })
|
||||
|
|
@ -187,11 +190,11 @@ function renderActionFields(command: CustomCommandDefinition, onChange: (action:
|
|||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
||||
<Forms.FormText style={{ color: "var(--text-muted, #a5a6ab)" }}>
|
||||
<Paragraph size="sm" style={{ color: "var(--text-muted, #a5a6ab)" }}>
|
||||
Current page: {currentSummary}
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
size="small"
|
||||
onClick={() => openCommandPicker({
|
||||
initialSelectedIds: selectedSettings ? [selectedSettings.id] : undefined,
|
||||
filter: entry => entry.categoryId === "discord-settings",
|
||||
|
|
@ -232,7 +235,7 @@ function renderActionFields(command: CustomCommandDefinition, onChange: (action:
|
|||
checked={Boolean(action.openExternal)}
|
||||
onChange={value => onChange({ type: "url", url: action.url, openExternal: value })}
|
||||
/>
|
||||
<Forms.FormText>Open externally</Forms.FormText>
|
||||
<Paragraph size="sm">Open externally</Paragraph>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -248,7 +251,7 @@ function renderActionFields(command: CustomCommandDefinition, onChange: (action:
|
|||
onChange={value => onChange({ type: "macro", steps: parseListInput(value) })}
|
||||
/>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
size="small"
|
||||
onClick={() => openCommandPicker({
|
||||
allowMultiple: true,
|
||||
initialSelectedIds: action.steps,
|
||||
|
|
@ -263,25 +266,24 @@ function renderActionFields(command: CustomCommandDefinition, onChange: (action:
|
|||
{action.steps.length > 0 && (
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
|
||||
{action.steps.map((step, index) => (
|
||||
<Button
|
||||
<TextButton
|
||||
key={`${step}-${index}`}
|
||||
size={Button.Sizes.MIN}
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
const next = [...action.steps];
|
||||
next.splice(index, 1);
|
||||
onChange({ type: "macro", steps: next });
|
||||
}}
|
||||
look={Button.Looks.LINK}
|
||||
>
|
||||
{step} ✕
|
||||
</Button>
|
||||
</TextButton>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{invalidSteps.length > 0 && (
|
||||
<Forms.FormText style={{ color: "var(--status-danger-text, #f04747)" }}>
|
||||
<Paragraph size="sm" style={{ color: "var(--status-danger-text, #f04747)" }}>
|
||||
Unknown command IDs: {invalidSteps.join(", ")}
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -350,11 +352,11 @@ export function CommandPaletteSettingsPanel() {
|
|||
|
||||
return (
|
||||
<section>
|
||||
<Forms.FormTitle tag="h4" style={{ color: "var(--text-normal, #dcddde)" }}>Custom Commands</Forms.FormTitle>
|
||||
<Forms.FormText className="vc-cp-settings-help">
|
||||
<Heading tag="h4" style={{ color: "var(--text-normal, #dcddde)" }}>Custom Commands</Heading>
|
||||
<Paragraph size="sm" className="vc-command-palette-settings-help">
|
||||
{/* Hank, don't abbreviate cyberpunk hank */}
|
||||
1) Name the command · 2) Add optional description/keywords/tags/category · 3) Choose an action and fill its details (IDs should match existing palette commands for aliases and macros).
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
||||
{commands.map(command => {
|
||||
const manualAdvanced = advancedOpen[command.id];
|
||||
|
|
@ -370,14 +372,15 @@ export function CommandPaletteSettingsPanel() {
|
|||
return (
|
||||
<div key={command.id} style={{ padding: 14, borderRadius: 8, background: "var(--background-primary)", border: "1px solid var(--background-base-low)", display: "flex", flexDirection: "column", gap: 12 }}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<Forms.FormTitle tag="h5" style={{ color: "var(--text-normal, #dcddde)" }}>{command.label || "Untitled Command"}</Forms.FormTitle>
|
||||
<Button
|
||||
size={Button.Sizes.MIN}
|
||||
look={Button.Looks.LINK}
|
||||
<Heading tag="h5" style={{ color: "var(--text-normal, #dcddde)", marginBottom: 0 }}>
|
||||
{command.label || "Untitled Command"}
|
||||
</Heading>
|
||||
<TextButton
|
||||
variant="secondary"
|
||||
onClick={() => setCollapsed(prev => ({ ...prev, [command.id]: !isCollapsed }))}
|
||||
>
|
||||
{isCollapsed ? "Expand" : "Collapse"}
|
||||
</Button>
|
||||
</TextButton>
|
||||
</div>
|
||||
|
||||
{!isCollapsed && (
|
||||
|
|
@ -416,20 +419,19 @@ export function CommandPaletteSettingsPanel() {
|
|||
serialize={option => option.value}
|
||||
/>
|
||||
</div>
|
||||
<Forms.FormText style={{ color: "var(--text-muted, #a5a6ab)", fontSize: 12 }}>
|
||||
<Paragraph size="xs" style={{ color: "var(--text-muted, #a5a6ab)", fontSize: 12 }}>
|
||||
{ACTION_HELP_TEXT[command.action.type]}
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
</div>
|
||||
|
||||
{renderActionFields(command, action => handleUpdate(command.id, prev => duplicateWithUpdates(prev, { action })))}
|
||||
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
look={Button.Looks.LINK}
|
||||
<TextButton
|
||||
variant="secondary"
|
||||
onClick={() => setAdvancedOpen(prev => ({ ...prev, [command.id]: !(showAdvanced ?? false) }))}
|
||||
>
|
||||
{showAdvanced ? "Hide advanced options" : "Show advanced options"}
|
||||
</Button>
|
||||
</TextButton>
|
||||
|
||||
{showAdvanced && (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 12, background: "var(--background-secondary-alt)", borderRadius: 8, padding: 12 }}>
|
||||
|
|
@ -454,9 +456,9 @@ export function CommandPaletteSettingsPanel() {
|
|||
/>
|
||||
{tagMeta.length > 0 && (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
|
||||
<Forms.FormText style={{ color: "var(--text-muted, #a5a6ab)", fontSize: 12 }}>
|
||||
<Paragraph size="xs" style={{ color: "var(--text-muted, #a5a6ab)", fontSize: 12 }}>
|
||||
Suggestions
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
|
||||
{tagMeta
|
||||
.filter(meta => !(command.tags ?? []).some(tag => normalizeTag(tag) === meta.id))
|
||||
|
|
@ -464,8 +466,8 @@ export function CommandPaletteSettingsPanel() {
|
|||
.map(meta => (
|
||||
<Button
|
||||
key={meta.id}
|
||||
size={Button.Sizes.MIN}
|
||||
look={Button.Looks.LINK}
|
||||
size="min"
|
||||
variant="secondary"
|
||||
onClick={() => handleUpdate(command.id, prev => duplicateWithUpdates(prev, {
|
||||
tags: normalizeTagList([...(prev.tags ?? []), meta.label])
|
||||
}))}
|
||||
|
|
@ -484,9 +486,9 @@ export function CommandPaletteSettingsPanel() {
|
|||
select={value => handleUpdate(command.id, prev => duplicateWithUpdates(prev, { categoryId: value || undefined }))}
|
||||
serialize={option => option.value}
|
||||
/>
|
||||
<Forms.FormText style={{ color: "var(--text-muted, #a5a6ab)", fontSize: 12 }}>
|
||||
<Paragraph size="xs" style={{ color: "var(--text-muted, #a5a6ab)", fontSize: 12 }}>
|
||||
Choose where this command appears in the palette.
|
||||
</Forms.FormText>
|
||||
</Paragraph>
|
||||
{categoryId && (
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
|
||||
<span style={{ display: "inline-flex", alignItems: "center", gap: 6, borderRadius: 999, padding: "2px 10px", fontSize: 12, background: "var(--background-base-low)", color: "var(--text-normal, #dcddde)" }}>
|
||||
|
|
@ -501,7 +503,7 @@ export function CommandPaletteSettingsPanel() {
|
|||
checked={Boolean(command.danger)}
|
||||
onChange={value => handleUpdate(command.id, prev => duplicateWithUpdates(prev, { danger: value }))}
|
||||
/>
|
||||
<Forms.FormText>Show as dangerous</Forms.FormText>
|
||||
<Paragraph size="sm">Show as dangerous</Paragraph>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -509,8 +511,8 @@ export function CommandPaletteSettingsPanel() {
|
|||
)}
|
||||
|
||||
<Button
|
||||
color={Button.Colors.RED}
|
||||
size={Button.Sizes.SMALL}
|
||||
variant="dangerPrimary"
|
||||
size="small"
|
||||
onClick={() => handleRemove(command.id)}
|
||||
>
|
||||
Remove Command
|
||||
|
|
@ -519,11 +521,7 @@ export function CommandPaletteSettingsPanel() {
|
|||
);
|
||||
})}
|
||||
</div>
|
||||
<Button
|
||||
color={Button.Colors.BRAND}
|
||||
style={{ marginTop: 16 }}
|
||||
onClick={handleAdd}
|
||||
>
|
||||
<Button variant="primary" style={{ marginTop: 16 }} onClick={handleAdd}>
|
||||
Add Command
|
||||
</Button>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -682,37 +682,32 @@
|
|||
}
|
||||
|
||||
/* Keybind recorder styles */
|
||||
.vc-cp-keybind-input {
|
||||
.vc-command-palette-keybind-input {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 12px;
|
||||
background-color: var(--background-secondary-alt);
|
||||
padding: 12px 0;
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.vc-cp-keybind-input:hover {
|
||||
background-color: var(--background-secondary-alt);
|
||||
}
|
||||
|
||||
.vc-cp-keybind-info {
|
||||
.vc-command-palette-keybind-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.vc-cp-keybind-controls {
|
||||
.vc-command-palette-keybind-controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.vc-cp-keybind-button {
|
||||
.vc-command-palette-keybind-button {
|
||||
padding: 8px 16px;
|
||||
background-color: var(--background-base-low);
|
||||
border: 2px solid var(--input-border);
|
||||
|
|
@ -728,55 +723,48 @@
|
|||
box-shadow: 0 0 0 1px var(--background-modifier-accent);
|
||||
}
|
||||
|
||||
.vc-cp-keybind-button:hover:not(:disabled) {
|
||||
.vc-command-palette-keybind-button:hover:not(:disabled) {
|
||||
background-color: var(--background-modifier-hover);
|
||||
border-color: var(--brand-experiment);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
.vc-cp-keybind-button:active:not(:disabled) {
|
||||
.vc-command-palette-keybind-button.listening:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, var(--brand-experiment) 0%, var(--brand-experiment-560) 100%);
|
||||
border-color: var(--status-danger, #f04747);
|
||||
box-shadow: none;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.vc-command-palette-keybind-button:active:not(:disabled) {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 1px 4px rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
.vc-cp-keybind-button:disabled {
|
||||
.vc-command-palette-keybind-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.vc-cp-keybind-button.listening {
|
||||
.vc-command-palette-keybind-button.listening {
|
||||
background: linear-gradient(135deg, var(--brand-experiment) 0%, var(--brand-experiment-560) 100%);
|
||||
color: var(--white);
|
||||
border-color: var(--brand-experiment);
|
||||
animation: vc-cp-keybind-pulse 1.5s ease-in-out infinite;
|
||||
box-shadow: 0 0 12px var(--brand-experiment);
|
||||
border-color: var(--status-danger, #f04747);
|
||||
animation: none;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgb(var(--status-danger-rgb, 240 71 71) / 60%);
|
||||
}
|
||||
|
||||
@keyframes vc-cp-keybind-pulse {
|
||||
|
||||
/* stylelint-disable-next-line rule-empty-line-before */
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.85;
|
||||
transform: scale(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
.vc-cp-keybind-conflict {
|
||||
.vc-command-palette-keybind-conflict {
|
||||
color: var(--text-feedback-critical);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
margin-top: 2px;
|
||||
animation: vc-cp-keybind-shake 0.3s ease;
|
||||
animation: vc-command-palette-keybind-shake 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes vc-cp-keybind-shake {
|
||||
@keyframes vc-command-palette-keybind-shake {
|
||||
|
||||
/* stylelint-disable-next-line rule-empty-line-before */
|
||||
0%,
|
||||
|
|
@ -795,4 +783,4 @@
|
|||
|
||||
/* stylelint-enable no-descending-specificity */
|
||||
|
||||
/* fuck you css */
|
||||
/* fuck you css */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue