chore(ui): tighten and center bulk delete toolbar

This commit is contained in:
Shantur Rathore
2026-03-03 18:52:09 +00:00
parent 219e012c1b
commit 4f8aba5658
7 changed files with 115 additions and 104 deletions

View File

@@ -1401,99 +1401,101 @@ export default function MessageSection(props: MessageSectionProps) {
role="toolbar" role="toolbar"
aria-label={t("messageSection.bulkDelete.toolbarAriaLabel", { count: selectedDeleteCount() })} aria-label={t("messageSection.bulkDelete.toolbarAriaLabel", { count: selectedDeleteCount() })}
> >
<span class="message-delete-mode-token-group" aria-hidden="true"> <div class="message-delete-mode-toolbar-row" aria-hidden="true">
<span class="message-delete-mode-count message-delete-mode-count--before" title={`${tokenStats().used} tokens currently in context`}> <span class="message-delete-mode-token-group">
{formatTokenCount(tokenStats().used)} <span class="message-delete-mode-count message-delete-mode-count--before" title={`${tokenStats().used} tokens currently in context`}>
{formatTokenCount(tokenStats().used)}
</span>
<span class="message-delete-mode-arrow" aria-hidden="true">{"\u203A"}</span>
<span class="message-delete-mode-count message-delete-mode-count--selection" title={`${selectedTokenTotal()} tokens selected (${selectedDeleteCount()} messages)`}>
{formatTokenCount(selectedTokenTotal())}
</span>
<span class="message-delete-mode-arrow" aria-hidden="true">{"\u203A"}</span>
<span class="message-delete-mode-count message-delete-mode-count--after" title={`${Math.max(0, tokenStats().used - selectedTokenTotal())} tokens remaining after deletion`}>
{formatTokenCount(Math.max(0, tokenStats().used - selectedTokenTotal()))}
</span>
</span> </span>
<span class="message-delete-mode-arrow" aria-hidden="true">{"\u203A"}</span>
<span class="message-delete-mode-count message-delete-mode-count--selection" title={`${selectedTokenTotal()} tokens selected (${selectedDeleteCount()} messages)`}>
{formatTokenCount(selectedTokenTotal())}
</span>
<span class="message-delete-mode-arrow" aria-hidden="true">{"\u203A"}</span>
<span class="message-delete-mode-count message-delete-mode-count--after" title={`${Math.max(0, tokenStats().used - selectedTokenTotal())} tokens remaining after deletion`}>
{formatTokenCount(Math.max(0, tokenStats().used - selectedTokenTotal()))}
</span>
</span>
<button
type="button"
class="message-delete-mode-button message-delete-mode-button--delete"
onClick={() => void deleteSelectedMessages()}
title={t("messageSection.bulkDelete.deleteSelectedTitle")}
aria-label={t("messageSection.bulkDelete.deleteSelectedTitle")}
>
<Trash class="w-4 h-4" aria-hidden="true" />
</button>
<div class="message-delete-mode-menu-container">
<button <button
ref={(el) => {
deleteMenuButtonRef = el
}}
type="button" type="button"
class="message-delete-mode-button message-delete-mode-button--menu" class="message-delete-mode-button message-delete-mode-button--delete"
onClick={() => setIsDeleteMenuOpen((prev) => !prev)} onClick={() => void deleteSelectedMessages()}
title={t("messageSection.bulkDelete.moreOptionsTitle")} title={t("messageSection.bulkDelete.deleteSelectedTitle")}
aria-label={t("messageSection.bulkDelete.moreOptionsTitle")} aria-label={t("messageSection.bulkDelete.deleteSelectedTitle")}
> >
<MoreHorizontal class="w-4 h-4" aria-hidden="true" /> <Trash class="w-4 h-4" aria-hidden="true" />
</button> </button>
<Show when={isDeleteMenuOpen()}>
<div <div class="message-delete-mode-menu-container">
<button
ref={(el) => { ref={(el) => {
deleteMenuRef = el deleteMenuButtonRef = el
}} }}
class="message-delete-mode-menu dropdown-surface" type="button"
class="message-delete-mode-button message-delete-mode-button--menu"
onClick={() => setIsDeleteMenuOpen((prev) => !prev)}
title={t("messageSection.bulkDelete.moreOptionsTitle")}
aria-label={t("messageSection.bulkDelete.moreOptionsTitle")}
> >
<button <MoreHorizontal class="w-4 h-4" aria-hidden="true" />
type="button" </button>
class="dropdown-item" <Show when={isDeleteMenuOpen()}>
onClick={() => { <div
selectAllForDeletion() ref={(el) => {
setIsDeleteMenuOpen(false) deleteMenuRef = el
}} }}
class="message-delete-mode-menu dropdown-surface"
> >
{t("messageSection.bulkDelete.selectAllTitle")} <button
</button> type="button"
<div class="message-delete-mode-menu-divider" aria-hidden="true" /> class="dropdown-item"
<div class="message-delete-mode-menu-row"> onClick={() => {
<span class="message-delete-mode-menu-label"> selectAllForDeletion()
{t("messageSection.bulkDelete.selectionModeLabel")} setIsDeleteMenuOpen(false)
</span> }}
<div class="message-delete-mode-menu-toggle"> >
<button {t("messageSection.bulkDelete.selectAllTitle")}
type="button" </button>
class="message-delete-mode-menu-toggle-button" <div class="message-delete-mode-menu-divider" aria-hidden="true" />
data-mode="all" <div class="message-delete-mode-menu-row">
data-active={selectionMode() === "all"} <span class="message-delete-mode-menu-label">
onClick={() => applySelectionMode("all")} {t("messageSection.bulkDelete.selectionModeLabel")}
> </span>
{t("messageSection.bulkDelete.selectionModeAll")} <div class="message-delete-mode-menu-toggle">
</button> <button
<button type="button"
type="button" class="message-delete-mode-menu-toggle-button"
class="message-delete-mode-menu-toggle-button" data-mode="all"
data-mode="tools" data-active={selectionMode() === "all"}
data-active={selectionMode() === "tools"} onClick={() => applySelectionMode("all")}
onClick={() => applySelectionMode("tools")} >
> {t("messageSection.bulkDelete.selectionModeAll")}
{t("messageSection.bulkDelete.selectionModeTools")} </button>
</button> <button
type="button"
class="message-delete-mode-menu-toggle-button"
data-mode="tools"
data-active={selectionMode() === "tools"}
onClick={() => applySelectionMode("tools")}
>
{t("messageSection.bulkDelete.selectionModeTools")}
</button>
</div>
</div> </div>
</div> </div>
</div> </Show>
</Show> </div>
</div>
<button <button
type="button" type="button"
class="message-delete-mode-button message-delete-mode-button--cancel" class="message-delete-mode-button message-delete-mode-button--cancel"
onClick={clearDeleteMode} onClick={clearDeleteMode}
title={t("messageSection.bulkDelete.cancelTitle")} title={t("messageSection.bulkDelete.cancelTitle")}
aria-label={t("messageSection.bulkDelete.cancelTitle")} aria-label={t("messageSection.bulkDelete.cancelTitle")}
> >
<X class="w-4 h-4" aria-hidden="true" /> <X class="w-4 h-4" aria-hidden="true" />
</button> </button>
</div>
<div class="message-delete-mode-hint-row keyboard-hints" aria-hidden="true"> <div class="message-delete-mode-hint-row keyboard-hints" aria-hidden="true">
<Kbd shortcut="cmd+click" /> <Kbd shortcut="cmd+click" />

View File

@@ -92,9 +92,9 @@ export const messagingMessages = {
"messageSection.bulkDelete.selectionModeLabel": "Selección", "messageSection.bulkDelete.selectionModeLabel": "Selección",
"messageSection.bulkDelete.selectionModeAll": "Todo", "messageSection.bulkDelete.selectionModeAll": "Todo",
"messageSection.bulkDelete.selectionModeTools": "Solo herramientas", "messageSection.bulkDelete.selectionModeTools": "Solo herramientas",
"messageSection.bulkDelete.selectionHint.toggle": "alternar", "messageSection.bulkDelete.selectionHint.toggle": "Seleccionar elemento",
"messageSection.bulkDelete.selectionHint.range": "rango", "messageSection.bulkDelete.selectionHint.range": "Seleccionar rango",
"messageSection.bulkDelete.selectionHint.clear": "limpiar", "messageSection.bulkDelete.selectionHint.clear": "Borrar selección",
"messageSection.bulkDelete.cancelTitle": "Cancelar selección", "messageSection.bulkDelete.cancelTitle": "Cancelar selección",
"messageSection.bulkDelete.failedTitle": "Error al eliminar", "messageSection.bulkDelete.failedTitle": "Error al eliminar",
"messageSection.bulkDelete.failedMessage": "No se pudieron eliminar los elementos seleccionados", "messageSection.bulkDelete.failedMessage": "No se pudieron eliminar los elementos seleccionados",

View File

@@ -92,9 +92,9 @@ export const messagingMessages = {
"messageSection.bulkDelete.selectionModeLabel": "Sélection", "messageSection.bulkDelete.selectionModeLabel": "Sélection",
"messageSection.bulkDelete.selectionModeAll": "Tous", "messageSection.bulkDelete.selectionModeAll": "Tous",
"messageSection.bulkDelete.selectionModeTools": "Outils uniquement", "messageSection.bulkDelete.selectionModeTools": "Outils uniquement",
"messageSection.bulkDelete.selectionHint.toggle": "basculer", "messageSection.bulkDelete.selectionHint.toggle": "Selectionner un element",
"messageSection.bulkDelete.selectionHint.range": "plage", "messageSection.bulkDelete.selectionHint.range": "Selectionner une plage",
"messageSection.bulkDelete.selectionHint.clear": "effacer", "messageSection.bulkDelete.selectionHint.clear": "Effacer la selection",
"messageSection.bulkDelete.cancelTitle": "Annuler la sélection", "messageSection.bulkDelete.cancelTitle": "Annuler la sélection",
"messageSection.bulkDelete.failedTitle": "Échec de suppression", "messageSection.bulkDelete.failedTitle": "Échec de suppression",
"messageSection.bulkDelete.failedMessage": "Impossible de supprimer les éléments sélectionnés", "messageSection.bulkDelete.failedMessage": "Impossible de supprimer les éléments sélectionnés",

View File

@@ -92,9 +92,9 @@ export const messagingMessages = {
"messageSection.bulkDelete.selectionModeLabel": "選択", "messageSection.bulkDelete.selectionModeLabel": "選択",
"messageSection.bulkDelete.selectionModeAll": "すべて", "messageSection.bulkDelete.selectionModeAll": "すべて",
"messageSection.bulkDelete.selectionModeTools": "ツールのみ", "messageSection.bulkDelete.selectionModeTools": "ツールのみ",
"messageSection.bulkDelete.selectionHint.toggle": "切り替え", "messageSection.bulkDelete.selectionHint.toggle": "項目を選択",
"messageSection.bulkDelete.selectionHint.range": "範囲", "messageSection.bulkDelete.selectionHint.range": "範囲を選択",
"messageSection.bulkDelete.selectionHint.clear": "クリア", "messageSection.bulkDelete.selectionHint.clear": "選択を解除",
"messageSection.bulkDelete.cancelTitle": "選択をキャンセル", "messageSection.bulkDelete.cancelTitle": "選択をキャンセル",
"messageSection.bulkDelete.failedTitle": "削除に失敗しました", "messageSection.bulkDelete.failedTitle": "削除に失敗しました",
"messageSection.bulkDelete.failedMessage": "選択した項目の削除に失敗しました", "messageSection.bulkDelete.failedMessage": "選択した項目の削除に失敗しました",

View File

@@ -92,9 +92,9 @@ export const messagingMessages = {
"messageSection.bulkDelete.selectionModeLabel": "Выбор", "messageSection.bulkDelete.selectionModeLabel": "Выбор",
"messageSection.bulkDelete.selectionModeAll": "Все", "messageSection.bulkDelete.selectionModeAll": "Все",
"messageSection.bulkDelete.selectionModeTools": "Только инструменты", "messageSection.bulkDelete.selectionModeTools": "Только инструменты",
"messageSection.bulkDelete.selectionHint.toggle": "переключить", "messageSection.bulkDelete.selectionHint.toggle": "Выбрать элемент",
"messageSection.bulkDelete.selectionHint.range": "диапазон", "messageSection.bulkDelete.selectionHint.range": "Выбрать диапазон",
"messageSection.bulkDelete.selectionHint.clear": "очистить", "messageSection.bulkDelete.selectionHint.clear": "Очистить выбор",
"messageSection.bulkDelete.cancelTitle": "Отменить выбор", "messageSection.bulkDelete.cancelTitle": "Отменить выбор",
"messageSection.bulkDelete.failedTitle": "Ошибка удаления", "messageSection.bulkDelete.failedTitle": "Ошибка удаления",
"messageSection.bulkDelete.failedMessage": "Не удалось удалить выбранные элементы", "messageSection.bulkDelete.failedMessage": "Не удалось удалить выбранные элементы",

View File

@@ -92,9 +92,9 @@ export const messagingMessages = {
"messageSection.bulkDelete.selectionModeLabel": "选择", "messageSection.bulkDelete.selectionModeLabel": "选择",
"messageSection.bulkDelete.selectionModeAll": "全部", "messageSection.bulkDelete.selectionModeAll": "全部",
"messageSection.bulkDelete.selectionModeTools": "仅工具", "messageSection.bulkDelete.selectionModeTools": "仅工具",
"messageSection.bulkDelete.selectionHint.toggle": "切换", "messageSection.bulkDelete.selectionHint.toggle": "选择项目",
"messageSection.bulkDelete.selectionHint.range": "范围", "messageSection.bulkDelete.selectionHint.range": "选择范围",
"messageSection.bulkDelete.selectionHint.clear": "清除", "messageSection.bulkDelete.selectionHint.clear": "清除选择",
"messageSection.bulkDelete.cancelTitle": "取消选择", "messageSection.bulkDelete.cancelTitle": "取消选择",
"messageSection.bulkDelete.failedTitle": "删除失败", "messageSection.bulkDelete.failedTitle": "删除失败",
"messageSection.bulkDelete.failedMessage": "无法删除已选择的项目", "messageSection.bulkDelete.failedMessage": "无法删除已选择的项目",

View File

@@ -11,20 +11,29 @@
.message-delete-mode-toolbar { .message-delete-mode-toolbar {
position: absolute; position: absolute;
right: 5rem; left: 50%;
transform: translateX(-50%);
bottom: 1rem; bottom: 1rem;
display: flex; display: inline-flex;
flex-wrap: wrap; flex-direction: column;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
padding: 6px 10px; padding: 6px 10px;
background: color-mix(in oklab, var(--surface-base) 62%, var(--accent-primary)); /* Match other popups (dropdown-surface / panels) */
border: 1px solid var(--accent-primary); background-color: var(--surface-base);
border: 1px solid var(--border-base);
border-radius: 12px; border-radius: 12px;
z-index: 50; z-index: 50;
box-shadow: box-shadow: var(--panel-shadow-strong);
0 0 0 1px color-mix(in oklab, var(--accent-primary) 25%, transparent), width: max-content;
0 8px 24px rgba(0, 0, 0, 0.3); max-width: min(80vw, 560px);
}
.message-delete-mode-toolbar-row {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
} }
.message-delete-mode-token-group { .message-delete-mode-token-group {
@@ -202,7 +211,6 @@
} }
.message-delete-mode-hint-row { .message-delete-mode-hint-row {
flex: 1 0 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -211,10 +219,11 @@
font-size: 10px; font-size: 10px;
color: var(--text-muted); color: var(--text-muted);
user-select: none; user-select: none;
flex-wrap: wrap;
} }
.message-delete-mode-hint-text { .message-delete-mode-hint-text {
white-space: nowrap; white-space: normal;
} }
.message-delete-mode-hint-sep { .message-delete-mode-hint-sep {