From 68299631f9d970ba6d1ac1ea819912fdb972d51b Mon Sep 17 00:00:00 2001 From: Ananovo Date: Sun, 2 Aug 2026 13:14:00 +0800 Subject: [PATCH] feat(web): customize composer toolbar visibility (#1298) --- .../settings/ComposerToolbarLayoutControl.tsx | 58 ++++++++++++++++--- .../hooks/useComposerToolbarLayout.test.ts | 27 ++++++++- web/src/hooks/useComposerToolbarLayout.ts | 13 ++++- web/src/lib/locales/en.ts | 6 +- web/src/lib/locales/zh-CN.ts | 6 +- 5 files changed, 95 insertions(+), 15 deletions(-) diff --git a/web/src/components/settings/ComposerToolbarLayoutControl.tsx b/web/src/components/settings/ComposerToolbarLayoutControl.tsx index dcec68d2..13247779 100644 --- a/web/src/components/settings/ComposerToolbarLayoutControl.tsx +++ b/web/src/components/settings/ComposerToolbarLayoutControl.tsx @@ -37,10 +37,10 @@ export function ComposerToolbarLayoutControl() { const moveDraggedItem = (group: ComposerToolbarGroup, index: number) => { if (draggedItem) { - const next = layout.mode === 'split' + const next = group === 'hidden' || layout.hidden.includes(draggedItem) || layout.mode === 'split' ? moveComposerToolbarItem(layout, draggedItem, group, index) : moveComposerToolbarItemInSingleLayout(layout, draggedItem, index) - const unchanged = next.left.join() === layout.left.join() && next.right.join() === layout.right.join() + const unchanged = next.left.join() === layout.left.join() && next.right.join() === layout.right.join() && next.hidden.join() === layout.hidden.join() if (!unchanged) { setLayout(next) } @@ -51,9 +51,10 @@ export function ComposerToolbarLayoutControl() { event.preventDefault() const item = draggedItem ?? event.dataTransfer.getData('text/plain') if ((COMPOSER_TOOLBAR_ITEM_IDS as readonly string[]).includes(item)) { - const next = layout.mode === 'split' - ? moveComposerToolbarItem(layout, item as ComposerToolbarItemId, group, index) - : moveComposerToolbarItemInSingleLayout(layout, item as ComposerToolbarItemId, index) + const typedItem = item as ComposerToolbarItemId + const next = group === 'hidden' || layout.hidden.includes(typedItem) || layout.mode === 'split' + ? moveComposerToolbarItem(layout, typedItem, group, index) + : moveComposerToolbarItemInSingleLayout(layout, typedItem, index) setLayout(next) } setDraggedItem(null) @@ -61,7 +62,7 @@ export function ComposerToolbarLayoutControl() { const moveItemByOffset = (item: ComposerToolbarItemId, group: ComposerToolbarGroup, index: number, offset: -1 | 1) => { const targetIndex = Math.max(0, index + offset) - const next = layout.mode === 'split' + const next = group === 'hidden' || layout.mode === 'split' ? moveComposerToolbarItem(layout, item, group, targetIndex) : moveComposerToolbarItemInSingleLayout(layout, item, targetIndex) setLayout(next) @@ -127,8 +128,10 @@ export function ComposerToolbarLayoutControl() { const singleAlignment = layout.mode === 'center' ? 'justify-center' : layout.mode === 'right' ? 'justify-end' : 'justify-start' const singleItems = [...layout.left, ...layout.right] - const selectedGroup: ComposerToolbarGroup = selectedItem && layout.right.includes(selectedItem) ? 'right' : 'left' - const selectedItems = layout.mode === 'split' ? layout[selectedGroup] : singleItems + const selectedGroup: ComposerToolbarGroup = selectedItem && layout.hidden.includes(selectedItem) + ? 'hidden' + : selectedItem && layout.right.includes(selectedItem) ? 'right' : 'left' + const selectedItems = selectedGroup === 'hidden' ? layout.hidden : layout.mode === 'split' ? layout[selectedGroup] : singleItems const selectedIndex = selectedItem ? selectedItems.indexOf(selectedItem) : -1 return ( @@ -173,6 +176,22 @@ export function ComposerToolbarLayoutControl() { +
+

{t('settings.chat.composerToolbar.hidden')}

+
+ {layout.hidden.length > 0 + ? renderGroup('hidden', layout.hidden, 'justify-start', true) + : ( +
event.preventDefault()} + onDrop={(event) => onDrop(event, 'hidden', 0)} + > + {t('settings.chat.composerToolbar.emptyHidden')} +
+ )} +
+
{selectedItem && selectedIndex >= 0 ? (
{t(ITEM_LABEL_KEYS[selectedItem])} @@ -197,7 +216,28 @@ export function ComposerToolbarLayoutControl() { > → - {layout.mode === 'split' ? ( + {selectedGroup !== 'hidden' ? ( + + ) : ( + + )} + {layout.mode === 'split' && selectedGroup !== 'hidden' ? (