chore: make sidebar non sticky

This commit is contained in:
Alexander Medvedev 2026-08-31 21:35:25 +02:00
parent 35e34b36f9
commit 7091dc152f
18 changed files with 125 additions and 42 deletions

View File

@ -1805,8 +1805,6 @@ h3 {
padding: 2rem; padding: 2rem;
border-radius: 12px; border-radius: 12px;
border: 1px solid var(--border); border: 1px solid var(--border);
position: sticky;
top: 120px;
} }
.sidebar-widget h3 { .sidebar-widget h3 {

View File

@ -76,8 +76,9 @@
"success": "Meldung eingereicht. Unser Team wird sie prüfen." "success": "Meldung eingereicht. Unser Team wird sie prüfen."
}, },
"commands": { "commands": {
"title": "Befehle & Berechtigungen", "title": "Befehle",
"no_commands": "Keine Befehle für dieses Plugin angegeben.", "no_commands": "Keine Befehle für dieses Plugin angegeben.",
"aliases": "Aliasse",
"permission": "Berechtigung", "permission": "Berechtigung",
"no_permission": "Keine erforderlich", "no_permission": "Keine erforderlich",
"copy_command": "Befehl kopieren", "copy_command": "Befehl kopieren",

View File

@ -76,8 +76,9 @@
"success": "Report submitted. Our moderation team will investigate." "success": "Report submitted. Our moderation team will investigate."
}, },
"commands": { "commands": {
"title": "Commands & Permissions", "title": "Commands",
"no_commands": "No commands listed for this plugin.", "no_commands": "No commands listed for this plugin.",
"aliases": "Aliases",
"permission": "Permission", "permission": "Permission",
"no_permission": "None required", "no_permission": "None required",
"copy_command": "Copy command", "copy_command": "Copy command",

View File

@ -76,8 +76,9 @@
"success": "Reporte enviado. Nuestro equipo lo revisará." "success": "Reporte enviado. Nuestro equipo lo revisará."
}, },
"commands": { "commands": {
"title": "Comandos y Permisos", "title": "Comandos",
"no_commands": "No hay comandos listados para este plugin.", "no_commands": "No hay comandos listados para este plugin.",
"aliases": "Alias",
"permission": "Permiso", "permission": "Permiso",
"no_permission": "Ninguno requerido", "no_permission": "Ninguno requerido",
"copy_command": "Copiar comando", "copy_command": "Copiar comando",

View File

@ -76,8 +76,9 @@
"success": "Signalement envoyé. Notre équipe va l'examiner." "success": "Signalement envoyé. Notre équipe va l'examiner."
}, },
"commands": { "commands": {
"title": "Commandes et Permissions", "title": "Commandes",
"no_commands": "Aucune commande listée pour ce plugin.", "no_commands": "Aucune commande listée pour ce plugin.",
"aliases": "Alias",
"permission": "Permission", "permission": "Permission",
"no_permission": "Aucune requise", "no_permission": "Aucune requise",
"copy_command": "Copier la commande", "copy_command": "Copier la commande",

View File

@ -76,8 +76,9 @@
"success": "Segnalazione inviata. Il team di moderazione verificherà." "success": "Segnalazione inviata. Il team di moderazione verificherà."
}, },
"commands": { "commands": {
"title": "Comandi e Permessi", "title": "Comandi",
"no_commands": "Nessun comando elencato per questo plugin.", "no_commands": "Nessun comando elencato per questo plugin.",
"aliases": "Alias",
"permission": "Permesso", "permission": "Permesso",
"no_permission": "Nessuno richiesto", "no_permission": "Nessuno richiesto",
"copy_command": "Copia comando", "copy_command": "Copia comando",

View File

@ -76,8 +76,9 @@
"success": "報告を受け付けました。運営チームが確認いたします。" "success": "報告を受け付けました。運営チームが確認いたします。"
}, },
"commands": { "commands": {
"title": "コマンドと権限", "title": "コマンド",
"no_commands": "このプラグインに登録されているコマンドはありません。", "no_commands": "このプラグインに登録されているコマンドはありません。",
"aliases": "エイリアス",
"permission": "権限", "permission": "権限",
"no_permission": "権限不要", "no_permission": "権限不要",
"copy_command": "コマンドをコピー", "copy_command": "コマンドをコピー",

View File

@ -76,8 +76,9 @@
"success": "신고가 접수되었습니다. 검토 후 처리하겠습니다." "success": "신고가 접수되었습니다. 검토 후 처리하겠습니다."
}, },
"commands": { "commands": {
"title": "명령어 및 권한", "title": "명령어",
"no_commands": "이 플러그인에 등록된 명령어가 없습니다.", "no_commands": "이 플러그인에 등록된 명령어가 없습니다.",
"aliases": "별칭",
"permission": "권한", "permission": "권한",
"no_permission": "필요 없음", "no_permission": "필요 없음",
"copy_command": "명령어 복사", "copy_command": "명령어 복사",

View File

@ -76,8 +76,9 @@
"success": "Melding verzonden. Ons moderatieteam zal dit onderzoeken." "success": "Melding verzonden. Ons moderatieteam zal dit onderzoeken."
}, },
"commands": { "commands": {
"title": "Commando's & Permissies", "title": "Commando's",
"no_commands": "Geen commando's vermeld voor deze plugin.", "no_commands": "Geen commando's vermeld voor deze plugin.",
"aliases": "Aliassen",
"permission": "Permissie", "permission": "Permissie",
"no_permission": "Geen vereist", "no_permission": "Geen vereist",
"copy_command": "Kopieer commando", "copy_command": "Kopieer commando",

View File

@ -76,8 +76,9 @@
"success": "Zgłoszenie wysłane. Zespół moderacji zajmie się sprawą." "success": "Zgłoszenie wysłane. Zespół moderacji zajmie się sprawą."
}, },
"commands": { "commands": {
"title": "Polecenia i Uprawnienia", "title": "Polecenia",
"no_commands": "Brak poleceń dla tego pluginu.", "no_commands": "Brak poleceń dla tego pluginu.",
"aliases": "Aliasy",
"permission": "Uprawnienie", "permission": "Uprawnienie",
"no_permission": "Brak wymaganego", "no_permission": "Brak wymaganego",
"copy_command": "Kopiuj polecenie", "copy_command": "Kopiuj polecenie",

View File

@ -76,8 +76,9 @@
"success": "Denúncia enviada com sucesso. Nossa equipe irá analisar." "success": "Denúncia enviada com sucesso. Nossa equipe irá analisar."
}, },
"commands": { "commands": {
"title": "Comandos e Permissões", "title": "Comandos",
"no_commands": "Nenhum comando listado para este plugin.", "no_commands": "Nenhum comando listado para este plugin.",
"aliases": "Aliases",
"permission": "Permissão", "permission": "Permissão",
"no_permission": "Nenhuma necessária", "no_permission": "Nenhuma necessária",
"copy_command": "Copiar comando", "copy_command": "Copiar comando",

View File

@ -76,8 +76,9 @@
"success": "Жалоба отправлена на рассмотрение модераторам." "success": "Жалоба отправлена на рассмотрение модераторам."
}, },
"commands": { "commands": {
"title": "Команды и Права", "title": "Команды",
"no_commands": "У этого плагина нет зарегистрированных команд.", "no_commands": "У этого плагина нет зарегистрированных команд.",
"aliases": "Алиасы",
"permission": "Право", "permission": "Право",
"no_permission": "Не требуется", "no_permission": "Не требуется",
"copy_command": "Скопировать команду", "copy_command": "Скопировать команду",

View File

@ -76,9 +76,10 @@
"success": "Bildirim iletildi. Moderasyon ekibimiz inceleyecektir." "success": "Bildirim iletildi. Moderasyon ekibimiz inceleyecektir."
}, },
"commands": { "commands": {
"title": "Komutlar ve İzinler", "title": "Komutlar",
"no_commands": "Bu eklenti için listelenmiş komut yok.", "no_commands": "Bu eklenti için listelenmiş komut yok.",
"permission": "İzin", "aliases": "Takma Adlar",
"permission": "Yetki",
"no_permission": "Gerekli değil", "no_permission": "Gerekli değil",
"copy_command": "Komutu kopyala", "copy_command": "Komutu kopyala",
"copy_permission": "İzni kopyala", "copy_permission": "İzni kopyala",

View File

@ -76,8 +76,9 @@
"success": "举报已提交,审核团队将尽快处理。" "success": "举报已提交,审核团队将尽快处理。"
}, },
"commands": { "commands": {
"title": "命令与权限", "title": "指令",
"no_commands": "此插件未列出任何指令。", "no_commands": "此插件未列出任何指令。",
"aliases": "别名",
"permission": "权限", "permission": "权限",
"no_permission": "无需权限", "no_permission": "无需权限",
"copy_command": "复制指令", "copy_command": "复制指令",

View File

@ -208,7 +208,7 @@ const PluginCommandsSidebarWidget = ({ commands }: { commands?: PluginCommand[]
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.85rem' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.85rem' }}>
<h3 style={{ margin: 0, display: 'flex', alignItems: 'center', gap: '0.45rem', fontSize: '0.82rem' }}> <h3 style={{ margin: 0, display: 'flex', alignItems: 'center', gap: '0.45rem', fontSize: '0.82rem' }}>
<Terminal size={14} color="var(--primary)" /> <Terminal size={14} color="var(--primary)" />
{t('plugin.commands.title', 'Commands & Permissions')} {t('plugin.commands.title', 'Commands')}
</h3> </h3>
<span style={{ <span style={{
fontSize: '0.7rem', fontSize: '0.7rem',
@ -283,6 +283,34 @@ const PluginCommandsSidebarWidget = ({ commands }: { commands?: PluginCommand[]
</button> </button>
</div> </div>
{/* Aliases row */}
{cmd.aliases && Array.isArray(cmd.aliases) && cmd.aliases.length > 0 ? (
<div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: '0.3rem', marginTop: '-0.1rem' }}>
<span style={{ fontSize: '0.66rem', color: 'var(--text-muted)', fontWeight: 600 }}>
{t('plugin.commands.aliases', 'Aliases')}:
</span>
{cmd.aliases.map((alias: string, aIdx: number) => {
const formattedAlias = alias.startsWith('/') ? alias : `/${alias}`;
return (
<span
key={aIdx}
style={{
fontFamily: 'var(--font-mono)',
fontSize: '0.68rem',
color: 'var(--text-2, #cbd5e1)',
background: 'rgba(255, 255, 255, 0.05)',
border: '1px solid rgba(255, 255, 255, 0.08)',
borderRadius: '4px',
padding: '1px 5px',
}}
>
{formattedAlias}
</span>
);
})}
</div>
) : null}
{/* Permission row */} {/* Permission row */}
{cmd.permission ? ( {cmd.permission ? (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '0.5rem' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '0.5rem' }}>

View File

@ -236,6 +236,7 @@ const AddPlugin = () => {
const [commands, setCommands] = useState<Array<{ const [commands, setCommands] = useState<Array<{
name: string; name: string;
aliases: string;
permission: string; permission: string;
descriptions: Record<string, string>; descriptions: Record<string, string>;
}>>([]); }>>([]);
@ -245,13 +246,14 @@ const AddPlugin = () => {
...prev, ...prev,
{ {
name: '', name: '',
aliases: '',
permission: '', permission: '',
descriptions: { [activeLocale]: '' } descriptions: { [activeLocale]: '' }
} }
]); ]);
}; };
const updateCommandField = (index: number, field: 'name' | 'permission', value: string) => { const updateCommandField = (index: number, field: 'name' | 'permission' | 'aliases', value: string) => {
const cleanValue = field === 'name' ? value.replace(/^\/+/, '') : value; const cleanValue = field === 'name' ? value.replace(/^\/+/, '') : value;
setCommands(prev => { setCommands(prev => {
const next = [...prev]; const next = [...prev];
@ -462,12 +464,18 @@ const AddPlugin = () => {
try { try {
const validCommands = commands const validCommands = commands
.filter(c => c.name.trim().replace(/^\/+/, '').length > 0) .filter(c => c.name.trim().replace(/^\/+/, '').length > 0)
.map((c, idx) => ({ .map((c, idx) => {
name: c.name.trim().replace(/^\/+/, ''), const aliases = c.aliases
permission: c.permission.trim() || undefined, ? c.aliases.split(',').map(a => a.trim().replace(/^\/+/, '')).filter(a => a.length > 0)
description: c.descriptions, : undefined;
display_order: idx, return {
})); name: c.name.trim().replace(/^\/+/, ''),
aliases: aliases && aliases.length > 0 ? aliases : undefined,
permission: c.permission.trim() || undefined,
description: c.descriptions,
display_order: idx,
};
});
const metadata = { const metadata = {
name, name,
@ -810,11 +818,11 @@ const AddPlugin = () => {
)} )}
</div> </div>
{/* Commands & Permissions */} {/* Commands */}
<div className="mp-card"> <div className="mp-card">
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.85rem' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.85rem' }}>
<div className="mp-card-title" style={{ margin: 0 }}> <div className="mp-card-title" style={{ margin: 0 }}>
<Terminal size={14} />Commands & Permissions <Terminal size={14} />Commands
</div> </div>
<button <button
type="button" type="button"
@ -857,8 +865,8 @@ const AddPlugin = () => {
gap: '0.65rem' gap: '0.65rem'
}} }}
> >
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}> <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr auto', gap: '0.75rem', alignItems: 'flex-start' }}>
<div style={{ flex: 1 }}> <div>
<label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}> <label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}>
Command <span style={{ color: 'var(--mp-error)' }}>*</span> Command <span style={{ color: 'var(--mp-error)' }}>*</span>
</label> </label>
@ -872,7 +880,20 @@ const AddPlugin = () => {
required required
/> />
</div> </div>
<div style={{ flex: 1 }}> <div>
<label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}>
Aliases <span style={{ color: 'var(--mp-text-3)', fontWeight: 400 }}>(optional, comma-separated)</span>
</label>
<input
className="mp-input"
style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', padding: '0.45rem 0.65rem' }}
placeholder="hub, lobby, s"
value={cmd.aliases}
maxLength={128}
onChange={e => updateCommandField(idx, 'aliases', e.target.value)}
/>
</div>
<div>
<label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}> <label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}>
Permission Node <span style={{ color: 'var(--mp-text-3)', fontWeight: 400 }}>(optional)</span> Permission Node <span style={{ color: 'var(--mp-text-3)', fontWeight: 400 }}>(optional)</span>
</label> </label>

View File

@ -208,6 +208,7 @@ const StoreListing = ({ plugin, onSaved }: Props) => {
const [commands, setCommands] = useState<Array<{ const [commands, setCommands] = useState<Array<{
id?: number; id?: number;
name: string; name: string;
aliases: string;
permission: string; permission: string;
descriptions: Record<string, string>; descriptions: Record<string, string>;
}>>(() => { }>>(() => {
@ -235,6 +236,7 @@ const StoreListing = ({ plugin, onSaved }: Props) => {
return { return {
id: cmd.id, id: cmd.id,
name: (cmd.name || '').replace(/^\/+/, ''), name: (cmd.name || '').replace(/^\/+/, ''),
aliases: Array.isArray(cmd.aliases) ? cmd.aliases.join(', ') : '',
permission: cmd.permission || '', permission: cmd.permission || '',
descriptions: descMap, descriptions: descMap,
}; };
@ -246,13 +248,14 @@ const StoreListing = ({ plugin, onSaved }: Props) => {
...prev, ...prev,
{ {
name: '', name: '',
aliases: '',
permission: '', permission: '',
descriptions: { [activeLocale]: '' }, descriptions: { [activeLocale]: '' },
}, },
]); ]);
}; };
const updateCommandField = (index: number, field: 'name' | 'permission', value: string) => { const updateCommandField = (index: number, field: 'name' | 'permission' | 'aliases', value: string) => {
const cleanValue = field === 'name' ? value.replace(/^\/+/, '') : value; const cleanValue = field === 'name' ? value.replace(/^\/+/, '') : value;
setCommands(prev => { setCommands(prev => {
const next = [...prev]; const next = [...prev];
@ -313,13 +316,19 @@ const StoreListing = ({ plugin, onSaved }: Props) => {
setSaveSuccess(false); setSaveSuccess(false);
const validCommands = commands const validCommands = commands
.filter(c => c.name.trim().replace(/^\/+/, '').length > 0) .filter(c => c.name.trim().replace(/^\/+/, '').length > 0)
.map((c, idx) => ({ .map((c, idx) => {
id: c.id, const aliases = c.aliases
name: c.name.trim().replace(/^\/+/, ''), ? c.aliases.split(',').map(a => a.trim().replace(/^\/+/, '')).filter(a => a.length > 0)
permission: c.permission.trim() || undefined, : undefined;
description: c.descriptions, return {
display_order: idx, id: c.id,
})); name: c.name.trim().replace(/^\/+/, ''),
aliases: aliases && aliases.length > 0 ? aliases : undefined,
permission: c.permission.trim() || undefined,
description: c.descriptions,
display_order: idx,
};
});
const metadata = { const metadata = {
name, name,
@ -631,11 +640,11 @@ const StoreListing = ({ plugin, onSaved }: Props) => {
</p> </p>
</div> </div>
{/* ── Commands & Permissions ── */} {/* ── Commands ── */}
<div className="mp-card"> <div className="mp-card">
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.85rem' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '0.85rem' }}>
<div className="mp-card-title" style={{ margin: 0 }}> <div className="mp-card-title" style={{ margin: 0 }}>
<Terminal size={14} />Commands & Permissions <Terminal size={14} />Commands
</div> </div>
<button <button
type="button" type="button"
@ -678,8 +687,8 @@ const StoreListing = ({ plugin, onSaved }: Props) => {
gap: '0.65rem' gap: '0.65rem'
}} }}
> >
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}> <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr auto', gap: '0.75rem', alignItems: 'flex-start' }}>
<div style={{ flex: 1 }}> <div>
<label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}> <label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}>
Command <span style={{ color: 'var(--mp-error)' }}>*</span> Command <span style={{ color: 'var(--mp-error)' }}>*</span>
</label> </label>
@ -693,7 +702,20 @@ const StoreListing = ({ plugin, onSaved }: Props) => {
required required
/> />
</div> </div>
<div style={{ flex: 1 }}> <div>
<label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}>
Aliases <span style={{ color: 'var(--mp-text-3)', fontWeight: 400 }}>(optional, comma-separated)</span>
</label>
<input
className="mp-input"
style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', padding: '0.45rem 0.65rem' }}
placeholder="hub, lobby, s"
value={cmd.aliases}
maxLength={128}
onChange={e => updateCommandField(idx, 'aliases', e.target.value)}
/>
</div>
<div>
<label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}> <label className="mp-label" style={{ fontSize: '0.72rem', marginBottom: '0.25rem' }}>
Permission Node <span style={{ color: 'var(--mp-text-3)', fontWeight: 400 }}>(optional)</span> Permission Node <span style={{ color: 'var(--mp-text-3)', fontWeight: 400 }}>(optional)</span>
</label> </label>

View File

@ -8,6 +8,7 @@ export interface PluginCommand {
name: string; name: string;
permission?: string; permission?: string;
description?: Record<string, string> | string | null; description?: Record<string, string> | string | null;
aliases?: string[];
display_order?: number; display_order?: number;
created_at?: string; created_at?: string;
} }