Files
vencord-wayland-keybinds/about.tsx
T
Evandro Fonseca Junior 34b61e2509 WaylandKeybinds: localized info panel with setup steps
- Info panel in the plugin modal: live status with the number of keybinds
  GNOME registered, the three setup steps and a button that opens
  Discord's Keybinds page (closes only the plugin modal, which otherwise
  blocks settings navigation).
- Follows Discord's language: English, Portuguese (pt-BR) and Spanish
  (es-ES and es-419 use each region's action names); bold labels quote
  Discord's own strings.
- Shared helpers moved to discord.ts; copy and locale choice in copy.ts,
  covered by basis-path tests (41 tests).
- README: current settings path (User Settings > System > Keybinds).
2026-11-02 01:24:50 -03:00

87 lines
3.2 KiB
TypeScript

/*
* Vencord, a Discord client mod
* Copyright (c) 2026 Evandro
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Button } from "@components/Button";
import { findByPropsLazy } from "@webpack";
import { i18n, SettingsRouter, useState, useStateFromStores } from "@webpack/common";
import { pickCopy } from "./copy";
import { KeybindsStore, portalActive } from "./discord";
function locale(): string {
try {
return i18n.intl.currentLocale ?? document.documentElement.lang;
} catch {
return document.documentElement.lang;
}
}
// the panel renders inside the plugin modal, so the top of Discord's modal stack at mount is that modal
const ModalStack = findByPropsLazy("getOpenModalKeys", "closeModal");
function topModalKey(): string | undefined {
try {
return [...ModalStack.getOpenModalKeys()].at(-1);
} catch {
return undefined;
}
}
function openKeybinds(pluginModalKey: string | undefined) {
// settings ignore navigation while another modal sits on top of them
if (pluginModalKey) ModalStack.closeModal(pluginModalKey);
SettingsRouter.openUserSettings("system_custom_keybinds_category");
}
function StatusIcon({ active }: { active: boolean; }) {
return (
<svg className="vc-wk-status-icon" viewBox="0 0 20 20" width="20" height="20" aria-hidden="true">
<circle cx="10" cy="10" r="8.25" fill="none" stroke="currentColor" strokeWidth="1.5" />
{active
? <path d="m6.5 10.25 2.4 2.4 4.6-5" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
: <path d="M10 9v4.5M10 6.25v.01" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" />}
</svg>
);
}
export function AboutPanel() {
const copy = pickCopy(locale());
const active = portalActive();
const [modalKey] = useState(topModalKey);
const registered = useStateFromStores([KeybindsStore], () =>
Object.keys(KeybindsStore.getUserAgnosticState() ?? {})
.filter(id => KeybindsStore.getWaylandPortalShortcutState(id)?.bound).length
);
if (!active) {
return (
<p className="vc-wk-status">
<StatusIcon active={false} />
{copy.inactive}
</p>
);
}
return (
<section className="vc-wk-panel" aria-labelledby="vc-wk-title">
<p className="vc-wk-status vc-wk-status-on">
<StatusIcon active />
{copy.active(registered)}
</p>
<h3 id="vc-wk-title" className="vc-wk-title">{copy.title}</h3>
<p className="vc-wk-lead">{copy.lead}</p>
{/* list-style is removed for the numbered badges, so keep the list role for screen readers */}
<ol className="vc-wk-steps" role="list">
{copy.steps.map(([before, bold, after], i) => <li key={i}><span>{before}{bold && <b>{bold}</b>}{after}</span></li>)}
</ol>
<div className="vc-wk-actions">
<Button onClick={() => openKeybinds(modalKey)}>{copy.open}</Button>
<p className="vc-wk-tip">{copy.tip}</p>
</div>
</section>
);
}