Merge pull request #17 from richardnixondev/chore/remove-cmdk-palette

chore: remove Cmd+K command palette (kept blocking navigation)
This commit is contained in:
authentik Default Admin 2026-05-17 23:42:08 +01:00 committed by GitHub
commit 74c6c4d759
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 5 additions and 204 deletions

View file

@ -7,7 +7,7 @@ packages = ["src"]
[project]
name = "reddit-media-collector"
version = "1.5.2"
version = "1.5.3"
description = "Self-hosted media collector for Reddit with Immich integration"
readme = "README.md"
requires-python = ">=3.11"

View file

@ -1360,87 +1360,8 @@ body:not(.discreet) .discreet-banner {
}
}
/* Cmd+K palette overlay */
/* Alpine x-cloak — still useful for other Alpine components mounted later */
[x-cloak] { display: none !important; }
.cmdk-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: flex-start;
justify-content: center;
padding-top: 10vh;
z-index: 3000;
}
.cmdk-card {
background: #272729;
border: 1px solid #343536;
border-radius: 10px;
width: 90%;
max-width: 560px;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.6);
overflow: hidden;
position: relative;
}
.cmdk-close {
position: absolute;
top: 8px;
right: 8px;
z-index: 2;
width: 32px;
height: 32px;
padding: 0;
background: transparent;
border: none;
color: #818384;
font-size: 22px;
line-height: 1;
cursor: pointer;
border-radius: 4px;
}
.cmdk-close:hover {
background: #343536;
color: #ff4500;
}
.cmdk-card > input {
border: none;
border-bottom: 1px solid #343536;
border-radius: 0;
padding: 16px;
font-size: 16px;
background: #1a1a1b;
}
.cmdk-card > input:focus {
border-color: #ff4500;
}
.cmdk-results {
max-height: 50vh;
overflow-y: auto;
}
.cmdk-group-label {
padding: 8px 16px 4px;
font-size: 11px;
text-transform: uppercase;
color: #818384;
letter-spacing: 1px;
}
.cmdk-item {
padding: 8px 16px;
cursor: pointer;
font-size: 13px;
}
.cmdk-item.active,
.cmdk-item:hover {
background: #343536;
color: #ff4500;
}
.cmdk-hint {
padding: 8px 16px;
font-size: 11px;
color: #818384;
border-top: 1px solid #343536;
background: #1a1a1b;
}
/* PIN unlock screen */
.unlock-wrap {

View file

@ -1727,105 +1727,9 @@ async function removeTagRelation(parentId, childId, kind) {
}
window.removeTagRelation = removeTagRelation;
// ---- Cmd+K palette (Alpine component) ----
// Hard close: removes overlay from layout via plain DOM, bypassing Alpine.
// Used by document-level listeners and the X button so a stuck Alpine state
// can never trap the user.
function cmdkForceClose() {
const el = document.getElementById('cmdk-overlay');
if (!el) return;
el.style.display = 'none';
const data = window.Alpine && Alpine.$data ? Alpine.$data(el) : null;
if (data) data.open = false;
}
window.cmdkForceClose = cmdkForceClose;
// Document-level listener installed once at DOMContentLoaded. Uses
// `capture: true` so it runs before any focused input swallows the key.
// Independent of Alpine, so it works even if the component never mounted.
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const el = document.getElementById('cmdk-overlay');
if (el && getComputedStyle(el).display !== 'none') {
e.preventDefault();
e.stopPropagation();
cmdkForceClose();
}
} else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
e.preventDefault();
const el = document.getElementById('cmdk-overlay');
const data = window.Alpine && Alpine.$data && el ? Alpine.$data(el) : null;
if (data) data.toggle();
}
}, true);
});
function cmdkPalette() {
return {
open: false,
query: '',
active: 0,
results: {authors: [], subreddits: [], tags: []},
toggle() { this.open = !this.open; if (this.open) { this.query=''; this.results={authors:[],subreddits:[],tags:[]}; this.active=0; this.$nextTick(()=>this.$el.querySelector('input')?.focus()); } },
close() { this.open = false; cmdkForceClose(); },
async search() {
const q = this.query.trim();
if (!q) { this.results = {authors: [], subreddits: [], tags: []}; return; }
try {
const r = await fetch(`/api/search?q=${encodeURIComponent(q)}&limit=6`);
if (r.ok) this.results = await r.json();
this.active = 0;
} catch (_) {}
},
groups() {
const out = [];
if (this.results.authors?.length) out.push({label: 'Autores', items: this.results.authors.map(a => ({label: 'u/'+a, kind: 'author', value: a}))});
if (this.results.subreddits?.length) out.push({label: 'Subreddits', items: this.results.subreddits.map(s => ({label: 'r/'+s, kind: 'subreddit', value: s}))});
if (this.results.tags?.length) out.push({label: 'Tags', items: this.results.tags.map(t => ({label: `#${t.name}${t.category ? ' ('+t.category+')' : ''}`, kind: 'tag', value: t.name}))});
return out;
},
hasResults() { return this.groups().some(g => g.items.length > 0); },
flatIndex(gi, ii) {
let n = 0;
const gs = this.groups();
for (let i = 0; i < gi; i++) n += gs[i].items.length;
return n + ii;
},
flatItems() {
return this.groups().flatMap(g => g.items);
},
move(d) {
const total = this.flatItems().length;
if (!total) return;
this.active = (this.active + d + total) % total;
},
choose() {
const items = this.flatItems();
if (items[this.active]) this.select(items[this.active]);
},
select(item) {
this.close();
if (item.kind === 'author') {
switchTab('gallery', true);
document.getElementById('filter-author').value = item.value;
loadGallery();
} else if (item.kind === 'subreddit') {
switchTab('gallery', true);
document.getElementById('filter-subreddit').value = item.value;
loadGallery();
} else if (item.kind === 'tag') {
// No tag filter on gallery yet — drop into the tag in the bulk
// bar as a shortcut for adding to current selection.
const bulk = document.getElementById('bulk-tag-input');
if (bulk) bulk.value = item.value;
showAlert(`Tag "${item.value}" pronta para bulk-aplicar`, 'success');
}
},
};
}
window.cmdkPalette = cmdkPalette;
// (The Cmd+K command palette was removed — it kept blocking navigation
// regardless of how many close paths we added. `/api/search` stays in the
// backend in case anything wants the universal typeahead by URL.)
// Alpine.store('prefs') — NSFW gate + discreet mode (Fase 1).
document.addEventListener('alpine:init', () => {

View file

@ -58,29 +58,5 @@
{% include 'partials/_modal_author.html' %}
{% include 'partials/_modal_media.html' %}
{% include 'partials/_modal_tag.html' %}
<div id="cmdk-overlay" class="cmdk-overlay" x-data="cmdkPalette()" x-show="open" x-cloak
@click.self="close()"
onclick="if(event.target===this){cmdkForceClose();}">
<div class="cmdk-card" @click.stop onclick="event.stopPropagation();">
<button type="button" class="cmdk-close" onclick="cmdkForceClose();" title="Fechar (Esc)">&times;</button>
<input type="text" x-model="query" @input.debounce.150ms="search()"
@keydown.arrow-down.prevent="move(1)" @keydown.arrow-up.prevent="move(-1)"
@keydown.enter.prevent="choose()"
onkeydown="if(event.key==='Escape'){event.preventDefault();event.stopPropagation();cmdkForceClose();}"
placeholder="Buscar autores, subreddits, tags..." autocomplete="off">
<div class="cmdk-results" x-show="hasResults()">
<template x-for="(group, gi) in groups()" :key="gi">
<div>
<div class="cmdk-group-label" x-text="group.label"></div>
<template x-for="(item, ii) in group.items" :key="gi + ':' + ii">
<div class="cmdk-item" :class="{active: flatIndex(gi, ii) === active}" @click="select(item)" x-text="item.label"></div>
</template>
</div>
</template>
</div>
<div class="cmdk-hint">&#8984;K / Ctrl+K para abrir &middot; Esc ou X para fechar &middot; clique fora tamb&eacute;m fecha</div>
</div>
</div>
</body>
</html>