diff --git a/docs/i18n-translation-guidelines.md b/docs/i18n-translation-guidelines.md index c49f5c5c..613302ce 100644 --- a/docs/i18n-translation-guidelines.md +++ b/docs/i18n-translation-guidelines.md @@ -51,25 +51,11 @@ Locales: `en`, `zh-CN`, `zh-TW`, `ja`, `ko`, `fr`, `de`, `es`, `ru`, `he` (RTL). > "Folder sidebar feature". > > **Status (2026-09, chip reordering):** model tags and trigger words now share one drag/`⠿` -> grip reorder affordance with `Alt + ↑/↓` keyboard support, which added the 3 -> `common.reorder.*` keys. They live under `common` (not a feature namespace) because both -> editors render them; all 9 locales are translated (renderings in §2, "Chip reordering"). -> `Alt` and the `↑/↓` glyphs stay Latin/verbatim in every locale, the same precedent as -> `Shift+Enter` in `modals.model.metadata.notesHint`; zh-CN / zh-TW / ja use full-width -> parentheses and ko keeps this file's ASCII style. -> -> **Status (2026-09, download metadata phases):** the download progress UI now names the two -> post-transfer stages instead of sitting at 100% showing `0 B/s`, adding 4 -> `modals.download.progress.*` keys (`metadata`, `indexingFile`, `fetchingSourceMetadata`, -> `fetchingMetadata`). Translated in all 9 locales in the same pass, so the "no remaining -> placeholders" claim holds again. No new terminology: `metadata` reuses the §5 row, and the -> fetching phrasing mirrors each locale's `download.fetchingRepoFiles` / `fetchingVersions`. -> `{name}` and `{source}` are verbatim §1-R2 placeholders — `{source}` is filled at runtime -> with the *untranslated* platform name, so its surrounding spacing follows each locale's -> `modelCard.actions.viewOnSource` precedent (ja `{source} から`, ko `{source}에서`, -> zh `从 {source}` / `從 {source}`, he `מ-{source}`) and no brand ever appears inside the -> translated text. ja / ko keep this block's ASCII `:` like their sibling progress keys; -> zh-CN / zh-TW use full-width `:`. +> grip reorder affordance, which added the single `common.reorder.dragHandle` key (it lives +> under `common` because both editors render it). All 9 locales are translated (renderings in +> §2, "Chip reordering"). Reordering is pointer-only by design: an `Alt + Arrow` shortcut was +> prototyped and removed because it collided with the browser's Alt + Arrow handling and the +> modal's arrow-key navigation. --- @@ -378,25 +364,23 @@ are verbatim §1-R2 placeholders; `successWithFiles` is the only key carrying `{ ### Chip reordering (model tags / trigger words) -Model tags and trigger-word chips share a single reorder affordance (drag the chip or its -`⠿` grip, or move it with `Alt + ↑/↓`), so the copy sits in `common.reorder.*` instead of a -feature namespace. `dragHandle` is both the grip tooltip and the hint shown in the edit -controls row; `ariaLabel` is the per-grip screen-reader label (`{item}` is the tag/word text); -`announcement` is the aria-live message after a keyboard move and deliberately has no -`{item}`. Keep `{item}` / `{position}` / `{total}` verbatim (§1-R2) — the caller supplies -exactly those. +Model tags and trigger-word chips share a single reorder affordance (drag the chip, or its +`⠿` grip where the chip body is click-to-edit), so the copy sits in `common.reorder.dragHandle` +instead of a feature namespace. It is used twice per editor: as the grip tooltip and as the +hint shown in the edit controls row. There is deliberately **no keyboard shortcut** — an +`Alt + Arrow` binding fought the browser's own Alt + Arrow handling and the modal's arrow-key +navigation, so reordering is pointer-only and the grip is a decorative, non-focusable +affordance. Do not reintroduce a shortcut or a "position X of Y" screen-reader string without +re-adding the corresponding keys. -`Alt` and the `↑/↓` glyphs stay Latin/verbatim in every locale (same precedent as -`Shift+Enter`), and `position X of Y` reuses each locale's established ordering phrasing -(ja `{total} 件中 … 番目`, ko `총 {total}개 중 …번째`, fr `sur {total}`, ru `из {total}`, …). +`dragHandle` is a fragment, not a sentence: it labels both the grip and the hint, so keep it +short and imperative and do not append a keyboard hint in any locale. | Term | Rendering | |---|---| | drag to reorder | zh-CN 拖拽以调整顺序 · zh-TW 拖曳以調整順序 · ja ドラッグして並べ替え · ko 드래그하여 순서 변경 · fr Glisser pour réordonner · de Zum Neuordnen ziehen · es Arrastra para reordenar · ru Перетащите, чтобы изменить порядок · he גרור כדי לשנות סדר | -| position {position} of {total} | zh-CN 第 {position} 个,共 {total} 个 · zh-TW 第 {position} 個,共 {total} 個 · ja {total} 件中 {position} 番目 · ko 총 {total}개 중 {position}번째 · fr position {position} sur {total} · de Position {position} von {total} · es posición {position} de {total} · ru позиция {position} из {total} · he מיקום {position} מתוך {total} | -The grip/handle noun itself is never translated (it is an icon); the hint carries the whole -instruction, so no locale needs a separate "grip" term. +The grip itself is an icon and is never translated. --- diff --git a/locales/de.json b/locales/de.json index 2c27d48a..913ed9a7 100644 --- a/locales/de.json +++ b/locales/de.json @@ -3,9 +3,7 @@ "cancel": "Abbrechen", "confirm": "Bestätigen", "reorder": { - "dragHandle": "Zum Neuordnen ziehen (Alt + ↑/↓)", - "ariaLabel": "{item} neu anordnen, Position {position} von {total}", - "announcement": "An Position {position} von {total} verschoben" + "dragHandle": "Zum Neuordnen ziehen" }, "actions": { "save": "Speichern", diff --git a/locales/en.json b/locales/en.json index 5e6f58f8..e20f943d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -3,9 +3,7 @@ "cancel": "Cancel", "confirm": "Confirm", "reorder": { - "dragHandle": "Drag to reorder (Alt + ↑/↓)", - "ariaLabel": "Reorder {item}, position {position} of {total}", - "announcement": "Moved to position {position} of {total}" + "dragHandle": "Drag to reorder" }, "actions": { "save": "Save", diff --git a/locales/es.json b/locales/es.json index fba68110..32f82cbb 100644 --- a/locales/es.json +++ b/locales/es.json @@ -3,9 +3,7 @@ "cancel": "Cancelar", "confirm": "Confirmar", "reorder": { - "dragHandle": "Arrastra para reordenar (Alt + ↑/↓)", - "ariaLabel": "Reordenar {item}, posición {position} de {total}", - "announcement": "Movido a la posición {position} de {total}" + "dragHandle": "Arrastra para reordenar" }, "actions": { "save": "Guardar", diff --git a/locales/fr.json b/locales/fr.json index 6cc684ee..54df6875 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -3,9 +3,7 @@ "cancel": "Annuler", "confirm": "Confirmer", "reorder": { - "dragHandle": "Glisser pour réordonner (Alt + ↑/↓)", - "ariaLabel": "Réordonner {item}, position {position} sur {total}", - "announcement": "Déplacé en position {position} sur {total}" + "dragHandle": "Glisser pour réordonner" }, "actions": { "save": "Enregistrer", diff --git a/locales/he.json b/locales/he.json index 86d45820..d176e6ae 100644 --- a/locales/he.json +++ b/locales/he.json @@ -3,9 +3,7 @@ "cancel": "ביטול", "confirm": "אישור", "reorder": { - "dragHandle": "גרור כדי לשנות סדר (Alt + ↑/↓)", - "ariaLabel": "סדר מחדש את {item}, מיקום {position} מתוך {total}", - "announcement": "הועבר למיקום {position} מתוך {total}" + "dragHandle": "גרור כדי לשנות סדר" }, "actions": { "save": "שמירה", diff --git a/locales/ja.json b/locales/ja.json index 6bedb64d..79a7f703 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -3,9 +3,7 @@ "cancel": "キャンセル", "confirm": "確認", "reorder": { - "dragHandle": "ドラッグして並べ替え(Alt + ↑/↓)", - "ariaLabel": "{item} を並べ替え、{total} 件中 {position} 番目", - "announcement": "{total} 件中 {position} 番目に移動しました" + "dragHandle": "ドラッグして並べ替え" }, "actions": { "save": "保存", diff --git a/locales/ko.json b/locales/ko.json index be1b7ff2..cfc3ec9b 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -3,9 +3,7 @@ "cancel": "취소", "confirm": "확인", "reorder": { - "dragHandle": "드래그하여 순서 변경 (Alt + ↑/↓)", - "ariaLabel": "{item} 순서 변경, 총 {total}개 중 {position}번째", - "announcement": "총 {total}개 중 {position}번째로 이동했습니다" + "dragHandle": "드래그하여 순서 변경" }, "actions": { "save": "저장", diff --git a/locales/ru.json b/locales/ru.json index a84e8c2d..25691fba 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -3,9 +3,7 @@ "cancel": "Отмена", "confirm": "Подтвердить", "reorder": { - "dragHandle": "Перетащите, чтобы изменить порядок (Alt + ↑/↓)", - "ariaLabel": "Изменить порядок {item}, позиция {position} из {total}", - "announcement": "Перемещено на позицию {position} из {total}" + "dragHandle": "Перетащите, чтобы изменить порядок" }, "actions": { "save": "Сохранить", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 02b471df..6d59d766 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -3,9 +3,7 @@ "cancel": "取消", "confirm": "确认", "reorder": { - "dragHandle": "拖拽以调整顺序(Alt + ↑/↓)", - "ariaLabel": "调整 {item} 的顺序,第 {position} 个,共 {total} 个", - "announcement": "已移动到第 {position} 个,共 {total} 个" + "dragHandle": "拖拽以调整顺序" }, "actions": { "save": "保存", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 05663c35..39f97379 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -3,9 +3,7 @@ "cancel": "取消", "confirm": "確認", "reorder": { - "dragHandle": "拖曳以調整順序(Alt + ↑/↓)", - "ariaLabel": "調整 {item} 的順序,第 {position} 個,共 {total} 個", - "announcement": "已移動到第 {position} 個,共 {total} 個" + "dragHandle": "拖曳以調整順序" }, "actions": { "save": "儲存", diff --git a/static/css/components/shared/edit-metadata.css b/static/css/components/shared/edit-metadata.css index ad790d55..f13071ef 100644 --- a/static/css/components/shared/edit-metadata.css +++ b/static/css/components/shared/edit-metadata.css @@ -124,6 +124,7 @@ cursor: grab; /* Keep a touch drag on the handle from scrolling the surrounding panel */ touch-action: none; + user-select: none; transition: opacity 0.2s ease, color 0.2s ease; } @@ -137,18 +138,11 @@ margin-right: 4px; } -.reorder-handle:hover, -.reorder-handle:focus-visible { +.reorder-handle:hover { opacity: 0.9; color: var(--lora-accent); } -.reorder-handle:focus-visible { - outline: 2px solid var(--lora-accent); - outline-offset: 1px; - border-radius: 2px; -} - .reorder-handle:active { cursor: grabbing; } @@ -199,19 +193,6 @@ body.reorder-drag-active * { cursor: grabbing !important; } -/* Screen-reader-only live region announcing reorder moves */ -.reorder-sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - .metadata-item-content { color: var(--lora-accent) !important; font-size: 0.85em; diff --git a/static/js/components/shared/ModelTags.js b/static/js/components/shared/ModelTags.js index 4b6691ec..bf1f5421 100644 --- a/static/js/components/shared/ModelTags.js +++ b/static/js/components/shared/ModelTags.js @@ -9,7 +9,7 @@ import { getPriorityTagSuggestions } from '../../utils/priorityTagHelpers.js'; import { state } from '../../state/index.js'; import { enablePointerSort } from './pointerSort.js'; import { - createReorderSupport, + refreshReorderState, renderReorderHandle, renderReorderHint, } from './reorderSupport.js'; @@ -24,7 +24,6 @@ const MODEL_TYPE_SUGGESTION_KEY_MAP = { }; const METADATA_ITEM_SELECTOR = '.metadata-item'; const METADATA_ITEMS_CONTAINER_SELECTOR = '.metadata-items'; -const METADATA_DRAG_HANDLE_SELECTOR = '.reorder-handle'; /** * Tag items have no click action of their own, so the whole chip stays @@ -436,7 +435,7 @@ function createTagEditUI(currentTags, editBtnHTML = '') {
${currentTags.map(tag => `
- ${renderReorderHandle(translate('common.reorder.dragHandle'))} + ${renderReorderHandle(translate('common.reorder.dragHandle', {}, 'Drag to reorder'))}
- ${renderReorderHint(translate('common.reorder.dragHandle'))} + ${renderReorderHint(translate('common.reorder.dragHandle', {}, 'Drag to reorder'))} @@ -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'))}