From e09fe5888bd5c36729edaede7605fee2319da192 Mon Sep 17 00:00:00 2001 From: Will Miao Date: Thu, 17 Sep 2026 09:07:24 +0800 Subject: [PATCH] refactor(reorder): drop the Alt + Arrow shortcut, keep drag only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reorder shortcut cannot be made reliable in this UI. `Alt + Arrow` is the browser's tab-history / back-forward gesture on several platforms, and the modal already binds bare `ArrowLeft`/`ArrowRight` to model navigation, so the binding either did nothing — a keypress with nothing focused never reaches a listener on the tag list — or fought the browser. An affordance that occasionally navigates the page away is worse than having no keyboard path at all, so drop it. Reordering is pointer-only again: drag the chip (tags) or its `⠿` grip (trigger words, whose chip body is click-to-edit). Everything that existed only to serve the shortcut goes with it — the keydown listener, the hover tracking used to resolve the target chip, the aria-live announcements, the per-grip position labels and `moveItemWithinContainer`. The grip becomes a decorative, non-focusable `` (`aria-hidden`, behind a 5px drag threshold) instead of a ` @@ -561,7 +560,7 @@ function setupDeleteButtons() { const scope = tag?.closest('.model-tags-container'); tag.remove(); - scope?._tagReorderSupport?.refresh(); + refreshTagReorderState(scope); // Update status of items in the suggestion dropdown updateSuggestionsDropdown(); @@ -582,28 +581,28 @@ function setupTagDragAndDrop(scopeContainer) { } const scope = container.closest('.model-tags-container') || container; - let support = scope._tagReorderSupport; - if (!support || scope._tagReorderContainer !== container) { - support = createReorderSupport({ - container, - scope, - handleSelector: METADATA_DRAG_HANDLE_SELECTOR, - sortConfig: TAG_SORT_CONFIG, - }); - scope._tagReorderSupport = support; - scope._tagReorderContainer = container; - } enablePointerSort(container, { ...TAG_SORT_CONFIG, - onSorted: (item) => { + onSorted: () => { updateSuggestionsDropdown(); - support.refresh(); - support.announce(item); + refreshTagReorderState(scope); }, }); - support.refresh(); + refreshTagReorderState(scope); +} + +/** + * Refresh the "sortable" flag (and therefore the grip + hint) of a tags section + * @param {Element} [tagsSection] - The .model-tags-container element + */ +function refreshTagReorderState(tagsSection) { + refreshReorderState({ + container: tagsSection?.querySelector(METADATA_ITEMS_CONTAINER_SELECTOR), + scope: tagsSection || undefined, + itemSelector: METADATA_ITEM_SELECTOR, + }); } /** @@ -644,7 +643,7 @@ function addNewTag(tag, scopeElement = null) { newTag.className = 'metadata-item'; newTag.dataset.tag = tag; newTag.innerHTML = ` - ${renderReorderHandle(translate('common.reorder.dragHandle'))} + ${renderReorderHandle(translate('common.reorder.dragHandle', {}, 'Drag to reorder'))} @@ -275,7 +277,7 @@ export function renderTriggerWords(words, filePath) {