From f214eeb7b1e0782723baf7274aaf69cc370f8040 Mon Sep 17 00:00:00 2001 From: lyzno1 <92089059+lyzno1@users.noreply.github.com> Date: Sat, 16 Aug 2025 19:26:44 +0800 Subject: [PATCH] feat: add scroll to selected node button in workflow header (#24030) Co-authored-by: zhangxuhe1 --- .../workflow/header/header-in-normal.tsx | 8 ++- .../header/scroll-to-selected-node-button.tsx | 34 ++++++++++ .../nodes/_base/components/node-position.tsx | 63 ------------------- .../_base/components/workflow-panel/index.tsx | 2 - web/i18n/de-DE/workflow.ts | 2 +- web/i18n/en-US/workflow.ts | 2 +- web/i18n/es-ES/workflow.ts | 2 +- web/i18n/fa-IR/workflow.ts | 2 +- web/i18n/fr-FR/workflow.ts | 2 +- web/i18n/hi-IN/workflow.ts | 2 +- web/i18n/it-IT/workflow.ts | 2 +- web/i18n/ja-JP/workflow.ts | 2 +- web/i18n/ko-KR/workflow.ts | 2 +- web/i18n/pl-PL/workflow.ts | 2 +- web/i18n/pt-BR/workflow.ts | 2 +- web/i18n/ro-RO/workflow.ts | 2 +- web/i18n/ru-RU/workflow.ts | 2 +- web/i18n/sl-SI/workflow.ts | 2 +- web/i18n/th-TH/workflow.ts | 2 +- web/i18n/tr-TR/workflow.ts | 2 +- web/i18n/uk-UA/workflow.ts | 2 +- web/i18n/vi-VN/workflow.ts | 2 +- web/i18n/zh-Hans/workflow.ts | 2 +- web/i18n/zh-Hant/workflow.ts | 2 +- 24 files changed, 60 insertions(+), 87 deletions(-) create mode 100644 web/app/components/workflow/header/scroll-to-selected-node-button.tsx delete mode 100644 web/app/components/workflow/nodes/_base/components/node-position.tsx diff --git a/web/app/components/workflow/header/header-in-normal.tsx b/web/app/components/workflow/header/header-in-normal.tsx index 79a6509a7a..4093ffb262 100644 --- a/web/app/components/workflow/header/header-in-normal.tsx +++ b/web/app/components/workflow/header/header-in-normal.tsx @@ -17,6 +17,7 @@ import RunAndHistory from './run-and-history' import EditingTitle from './editing-title' import EnvButton from './env-button' import VersionHistoryButton from './version-history-button' +import ScrollToSelectedNodeButton from './scroll-to-selected-node-button' export type HeaderInNormalProps = { components?: { @@ -53,10 +54,13 @@ const HeaderInNormal = ({ }, [workflowStore, handleBackupDraft, selectedNode, handleNodeSelect, setShowWorkflowVersionHistoryPanel, setShowEnvPanel, setShowDebugAndPreviewPanel, setShowVariableInspectPanel, setShowChatVariablePanel]) return ( - <> +
+
+ +
{components?.left} @@ -65,7 +69,7 @@ const HeaderInNormal = ({ {components?.middle}
- +
) } diff --git a/web/app/components/workflow/header/scroll-to-selected-node-button.tsx b/web/app/components/workflow/header/scroll-to-selected-node-button.tsx new file mode 100644 index 0000000000..d3e7248d9a --- /dev/null +++ b/web/app/components/workflow/header/scroll-to-selected-node-button.tsx @@ -0,0 +1,34 @@ +import type { FC } from 'react' +import { useCallback } from 'react' +import { useNodes } from 'reactflow' +import { useTranslation } from 'react-i18next' +import type { CommonNodeType } from '../types' +import { scrollToWorkflowNode } from '../utils/node-navigation' +import cn from '@/utils/classnames' + +const ScrollToSelectedNodeButton: FC = () => { + const { t } = useTranslation() + const nodes = useNodes() + const selectedNode = nodes.find(node => node.data.selected) + + const handleScrollToSelectedNode = useCallback(() => { + if (!selectedNode) return + scrollToWorkflowNode(selectedNode.id) + }, [selectedNode]) + + if (!selectedNode) + return null + + return ( +
+ {t('workflow.panel.scrollToSelectedNode')} +
+ ) +} + +export default ScrollToSelectedNodeButton diff --git a/web/app/components/workflow/nodes/_base/components/node-position.tsx b/web/app/components/workflow/nodes/_base/components/node-position.tsx deleted file mode 100644 index e844726b4f..0000000000 --- a/web/app/components/workflow/nodes/_base/components/node-position.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { memo } from 'react' -import { useTranslation } from 'react-i18next' -import { useShallow } from 'zustand/react/shallow' -import { RiCrosshairLine } from '@remixicon/react' -import { useReactFlow, useStore } from 'reactflow' -import TooltipPlus from '@/app/components/base/tooltip' -import { useNodesSyncDraft } from '@/app/components/workflow-app/hooks' - -type NodePositionProps = { - nodeId: string -} -const NodePosition = ({ - nodeId, -}: NodePositionProps) => { - const { t } = useTranslation() - const reactflow = useReactFlow() - const { doSyncWorkflowDraft } = useNodesSyncDraft() - const { - nodePosition, - nodeWidth, - nodeHeight, - } = useStore(useShallow((s) => { - const nodes = s.getNodes() - const currentNode = nodes.find(node => node.id === nodeId)! - - return { - nodePosition: currentNode.position, - nodeWidth: currentNode.width, - nodeHeight: currentNode.height, - } - })) - const transform = useStore(s => s.transform) - - if (!nodePosition || !nodeWidth || !nodeHeight) return null - - const workflowContainer = document.getElementById('workflow-container') - const zoom = transform[2] - - const { clientWidth, clientHeight } = workflowContainer! - const { setViewport } = reactflow - - return ( - -
{ - setViewport({ - x: (clientWidth - 400 - nodeWidth * zoom) / 2 - nodePosition.x * zoom, - y: (clientHeight - nodeHeight * zoom) / 2 - nodePosition.y * zoom, - zoom: transform[2], - }) - doSyncWorkflowDraft() - }} - > - -
-
- ) -} - -export default memo(NodePosition) diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx index 4723b2dce7..c5670db9c9 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx @@ -19,7 +19,6 @@ import { useShallow } from 'zustand/react/shallow' import { useTranslation } from 'react-i18next' import NextStep from '../next-step' import PanelOperator from '../panel-operator' -import NodePosition from '@/app/components/workflow/nodes/_base/components/node-position' import HelpLink from '../help-link' import { DescriptionInput, @@ -362,7 +361,6 @@ const BasePanel: FC = ({ ) } -
diff --git a/web/i18n/de-DE/workflow.ts b/web/i18n/de-DE/workflow.ts index 7684a763a3..d40c640df8 100644 --- a/web/i18n/de-DE/workflow.ts +++ b/web/i18n/de-DE/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Alle Probleme wurden gelöst', change: 'Ändern', optional: '(optional)', - moveToThisNode: 'Bewege zu diesem Knoten', selectNextStep: 'Nächsten Schritt auswählen', addNextStep: 'Fügen Sie den nächsten Schritt in diesem Arbeitsablauf hinzu.', organizeBlocks: 'Knoten organisieren', changeBlock: 'Knoten ändern', maximize: 'Maximiere die Leinwand', minimize: 'Vollbildmodus beenden', + scrollToSelectedNode: 'Zum ausgewählten Knoten scrollen', }, nodes: { common: { diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 306dec0f09..91a1ff3ba4 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -320,7 +320,6 @@ const translation = { addNextStep: 'Add the next step in this workflow', selectNextStep: 'Select Next Step', runThisStep: 'Run this step', - moveToThisNode: 'Move to this node', checklist: 'Checklist', checklistTip: 'Make sure all issues are resolved before publishing', checklistResolved: 'All issues are resolved', @@ -329,6 +328,7 @@ const translation = { optional: '(optional)', maximize: 'Maximize Canvas', minimize: 'Exit Full Screen', + scrollToSelectedNode: 'Scroll to selected node', }, nodes: { common: { diff --git a/web/i18n/es-ES/workflow.ts b/web/i18n/es-ES/workflow.ts index 3d7e2fdafc..aff794f1f6 100644 --- a/web/i18n/es-ES/workflow.ts +++ b/web/i18n/es-ES/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Se resolvieron todos los problemas', change: 'Cambiar', optional: '(opcional)', - moveToThisNode: 'Mueve a este nodo', organizeBlocks: 'Organizar nodos', addNextStep: 'Agrega el siguiente paso en este flujo de trabajo', changeBlock: 'Cambiar Nodo', selectNextStep: 'Seleccionar siguiente paso', maximize: 'Maximizar Canvas', minimize: 'Salir de pantalla completa', + scrollToSelectedNode: 'Desplácese hasta el nodo seleccionado', }, nodes: { common: { diff --git a/web/i18n/fa-IR/workflow.ts b/web/i18n/fa-IR/workflow.ts index 6264841397..ee3ce148cf 100644 --- a/web/i18n/fa-IR/workflow.ts +++ b/web/i18n/fa-IR/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'تمام مسائل حل شده‌اند', change: 'تغییر', optional: '(اختیاری)', - moveToThisNode: 'به این گره بروید', selectNextStep: 'گام بعدی را انتخاب کنید', changeBlock: 'تغییر گره', organizeBlocks: 'گره‌ها را سازماندهی کنید', addNextStep: 'مرحله بعدی را به این فرآیند اضافه کنید', minimize: 'خروج از حالت تمام صفحه', maximize: 'بیشینه‌سازی بوم', + scrollToSelectedNode: 'به گره انتخاب شده بروید', }, nodes: { common: { diff --git a/web/i18n/fr-FR/workflow.ts b/web/i18n/fr-FR/workflow.ts index 0e9f57b56f..8022768d44 100644 --- a/web/i18n/fr-FR/workflow.ts +++ b/web/i18n/fr-FR/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Tous les problèmes ont été résolus', change: 'Modifier', optional: '(facultatif)', - moveToThisNode: 'Déplacer vers ce nœud', organizeBlocks: 'Organiser les nœuds', addNextStep: 'Ajoutez la prochaine étape dans ce flux de travail', selectNextStep: 'Sélectionner la prochaine étape', changeBlock: 'Changer de nœud', maximize: 'Maximiser le Canvas', minimize: 'Sortir du mode plein écran', + scrollToSelectedNode: 'Faites défiler jusqu’au nœud sélectionné', }, nodes: { common: { diff --git a/web/i18n/hi-IN/workflow.ts b/web/i18n/hi-IN/workflow.ts index 13dbc5a912..2d04883762 100644 --- a/web/i18n/hi-IN/workflow.ts +++ b/web/i18n/hi-IN/workflow.ts @@ -327,13 +327,13 @@ const translation = { checklistResolved: 'सभी समस्याएं हल हो गई हैं', change: 'बदलें', optional: '(वैकल्पिक)', - moveToThisNode: 'इस नोड पर जाएं', changeBlock: 'नोड बदलें', addNextStep: 'इस कार्यप्रवाह में अगला कदम जोड़ें', selectNextStep: 'अगला कदम चुनें', organizeBlocks: 'नोड्स का आयोजन करें', minimize: 'पूर्ण स्क्रीन से बाहर निकलें', maximize: 'कैनवास का अधिकतम लाभ उठाएँ', + scrollToSelectedNode: 'चुने गए नोड पर स्क्रॉल करें', }, nodes: { common: { diff --git a/web/i18n/it-IT/workflow.ts b/web/i18n/it-IT/workflow.ts index b342f0b0a8..0b687906fd 100644 --- a/web/i18n/it-IT/workflow.ts +++ b/web/i18n/it-IT/workflow.ts @@ -330,13 +330,13 @@ const translation = { checklistResolved: 'Tutti i problemi sono risolti', change: 'Cambia', optional: '(opzionale)', - moveToThisNode: 'Sposta a questo nodo', changeBlock: 'Cambia Nodo', selectNextStep: 'Seleziona il prossimo passo', organizeBlocks: 'Organizzare i nodi', addNextStep: 'Aggiungi il prossimo passo in questo flusso di lavoro', minimize: 'Esci dalla modalità schermo intero', maximize: 'Massimizza Canvas', + scrollToSelectedNode: 'Scorri fino al nodo selezionato', }, nodes: { common: { diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts index 307dc71ec0..a1e4b92482 100644 --- a/web/i18n/ja-JP/workflow.ts +++ b/web/i18n/ja-JP/workflow.ts @@ -326,9 +326,9 @@ const translation = { organizeBlocks: 'ノード整理', change: '変更', optional: '(任意)', - moveToThisNode: 'このノードに移動する', maximize: 'キャンバスを最大化する', minimize: '全画面を終了する', + scrollToSelectedNode: '選択したノードまでスクロール', }, nodes: { common: { diff --git a/web/i18n/ko-KR/workflow.ts b/web/i18n/ko-KR/workflow.ts index 49ca6c6cab..c2ec86f059 100644 --- a/web/i18n/ko-KR/workflow.ts +++ b/web/i18n/ko-KR/workflow.ts @@ -336,13 +336,13 @@ const translation = { checklistResolved: '모든 문제가 해결되었습니다', change: '변경', optional: '(선택사항)', - moveToThisNode: '이 노드로 이동', organizeBlocks: '노드 정리하기', selectNextStep: '다음 단계 선택', changeBlock: '노드 변경', addNextStep: '이 워크플로우에 다음 단계를 추가하세요.', minimize: '전체 화면 종료', maximize: '캔버스 전체 화면', + scrollToSelectedNode: '선택한 노드로 스크롤', }, nodes: { common: { diff --git a/web/i18n/pl-PL/workflow.ts b/web/i18n/pl-PL/workflow.ts index cea026b6c4..8f1d76dcf2 100644 --- a/web/i18n/pl-PL/workflow.ts +++ b/web/i18n/pl-PL/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Wszystkie problemy zostały rozwiązane', change: 'Zmień', optional: '(opcjonalne)', - moveToThisNode: 'Przenieś do tego węzła', selectNextStep: 'Wybierz następny krok', addNextStep: 'Dodaj następny krok w tym procesie roboczym', changeBlock: 'Zmień węzeł', organizeBlocks: 'Organizuj węzły', minimize: 'Wyjdź z trybu pełnoekranowego', maximize: 'Maksymalizuj płótno', + scrollToSelectedNode: 'Przewiń do wybranego węzła', }, nodes: { common: { diff --git a/web/i18n/pt-BR/workflow.ts b/web/i18n/pt-BR/workflow.ts index 3c85c37301..a490bc0687 100644 --- a/web/i18n/pt-BR/workflow.ts +++ b/web/i18n/pt-BR/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Todos os problemas foram resolvidos', change: 'Mudar', optional: '(opcional)', - moveToThisNode: 'Mova-se para este nó', changeBlock: 'Mudar Nó', addNextStep: 'Adicione o próximo passo neste fluxo de trabalho', organizeBlocks: 'Organizar nós', selectNextStep: 'Selecione o próximo passo', maximize: 'Maximize Canvas', minimize: 'Sair do Modo Tela Cheia', + scrollToSelectedNode: 'Role até o nó selecionado', }, nodes: { common: { diff --git a/web/i18n/ro-RO/workflow.ts b/web/i18n/ro-RO/workflow.ts index 0caec4993c..1e55d53235 100644 --- a/web/i18n/ro-RO/workflow.ts +++ b/web/i18n/ro-RO/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Toate problemele au fost rezolvate', change: 'Schimbă', optional: '(opțional)', - moveToThisNode: 'Mutați la acest nod', organizeBlocks: 'Organizează nodurile', addNextStep: 'Adăugați următorul pas în acest flux de lucru', changeBlock: 'Schimbă nodul', selectNextStep: 'Selectați Pasul Următor', maximize: 'Maximize Canvas', minimize: 'Iesi din modul pe tot ecranul', + scrollToSelectedNode: 'Derulați la nodul selectat', }, nodes: { common: { diff --git a/web/i18n/ru-RU/workflow.ts b/web/i18n/ru-RU/workflow.ts index d4c9b81b39..48aa9b6e58 100644 --- a/web/i18n/ru-RU/workflow.ts +++ b/web/i18n/ru-RU/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Все проблемы решены', change: 'Изменить', optional: '(необязательно)', - moveToThisNode: 'Перейдите к этому узлу', selectNextStep: 'Выберите следующий шаг', organizeBlocks: 'Организовать узлы', addNextStep: 'Добавьте следующий шаг в этот рабочий процесс', changeBlock: 'Изменить узел', minimize: 'Выйти из полноэкранного режима', maximize: 'Максимизировать холст', + scrollToSelectedNode: 'Прокрутите до выбранного узла', }, nodes: { common: { diff --git a/web/i18n/sl-SI/workflow.ts b/web/i18n/sl-SI/workflow.ts index 2df5271043..c7602b1349 100644 --- a/web/i18n/sl-SI/workflow.ts +++ b/web/i18n/sl-SI/workflow.ts @@ -318,7 +318,6 @@ const translation = { runThisStep: 'Izvedi ta korak', changeBlock: 'Spremeni vozlišče', addNextStep: 'Dodajte naslednji korak v ta delovni potek', - moveToThisNode: 'Premakni se na to vozlišče', checklistTip: 'Prepričajte se, da so vse težave rešene, preden objavite.', selectNextStep: 'Izberi naslednji korak', helpLink: 'Pomočna povezava', @@ -329,6 +328,7 @@ const translation = { minimize: 'Izhod iz celotnega zaslona', maximize: 'Maksimiziraj platno', optional: '(neobvezno)', + scrollToSelectedNode: 'Pomaknite se do izbranega vozlišča', }, nodes: { common: { diff --git a/web/i18n/th-TH/workflow.ts b/web/i18n/th-TH/workflow.ts index eea8e1e300..8d4ca5e7ca 100644 --- a/web/i18n/th-TH/workflow.ts +++ b/web/i18n/th-TH/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'ปัญหาทั้งหมดได้รับการแก้ไขแล้ว', change: 'เปลี่ยน', optional: '(ไม่บังคับ)', - moveToThisNode: 'ย้ายไปที่โหนดนี้', organizeBlocks: 'จัดระเบียบโหนด', addNextStep: 'เพิ่มขั้นตอนถัดไปในกระบวนการทำงานนี้', changeBlock: 'เปลี่ยนโหนด', selectNextStep: 'เลือกขั้นตอนถัดไป', minimize: 'ออกจากโหมดเต็มหน้าจอ', maximize: 'เพิ่มประสิทธิภาพผ้าใบ', + scrollToSelectedNode: 'เลื่อนไปยังโหนดที่เลือก', }, nodes: { common: { diff --git a/web/i18n/tr-TR/workflow.ts b/web/i18n/tr-TR/workflow.ts index 0a94550eaa..ca4ab32ebf 100644 --- a/web/i18n/tr-TR/workflow.ts +++ b/web/i18n/tr-TR/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Tüm sorunlar çözüldü', change: 'Değiştir', optional: '(isteğe bağlı)', - moveToThisNode: 'Bu düğüme geç', changeBlock: 'Düğümü Değiştir', addNextStep: 'Bu iş akışına bir sonraki adımı ekleyin', organizeBlocks: 'Düğümleri düzenle', selectNextStep: 'Sonraki Adımı Seç', minimize: 'Tam Ekrandan Çık', maximize: 'Kanvası Maksimize Et', + scrollToSelectedNode: 'Seçili düğüme kaydırma', }, nodes: { common: { diff --git a/web/i18n/uk-UA/workflow.ts b/web/i18n/uk-UA/workflow.ts index 1df2f444fd..47c1a12128 100644 --- a/web/i18n/uk-UA/workflow.ts +++ b/web/i18n/uk-UA/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Всі проблеми вирішені', change: 'Змінити', optional: '(необов\'язково)', - moveToThisNode: 'Перемістіть до цього вузла', organizeBlocks: 'Організуйте вузли', changeBlock: 'Змінити вузол', selectNextStep: 'Виберіть наступний крок', addNextStep: 'Додайте наступний крок у цей робочий процес', minimize: 'Вийти з повноекранного режиму', maximize: 'Максимізувати полотно', + scrollToSelectedNode: 'Прокрутіть до вибраного вузла', }, nodes: { common: { diff --git a/web/i18n/vi-VN/workflow.ts b/web/i18n/vi-VN/workflow.ts index 23482ee06d..ea4488d020 100644 --- a/web/i18n/vi-VN/workflow.ts +++ b/web/i18n/vi-VN/workflow.ts @@ -315,13 +315,13 @@ const translation = { checklistResolved: 'Tất cả các vấn đề đã được giải quyết', change: 'Thay đổi', optional: '(tùy chọn)', - moveToThisNode: 'Di chuyển đến nút này', changeBlock: 'Thay đổi Node', selectNextStep: 'Chọn bước tiếp theo', organizeBlocks: 'Tổ chức các nút', addNextStep: 'Thêm bước tiếp theo trong quy trình này', maximize: 'Tối đa hóa Canvas', minimize: 'Thoát chế độ toàn màn hình', + scrollToSelectedNode: 'Cuộn đến nút đã chọn', }, nodes: { common: { diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index cef27f5b9d..5b56f112c5 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -326,9 +326,9 @@ const translation = { organizeBlocks: '整理节点', change: '更改', optional: '(选填)', - moveToThisNode: '定位至此节点', maximize: '最大化画布', minimize: '退出最大化', + scrollToSelectedNode: '滚动至选中节点', }, nodes: { common: { diff --git a/web/i18n/zh-Hant/workflow.ts b/web/i18n/zh-Hant/workflow.ts index e40221f2bc..659bffa390 100644 --- a/web/i18n/zh-Hant/workflow.ts +++ b/web/i18n/zh-Hant/workflow.ts @@ -319,9 +319,9 @@ const translation = { organizeBlocks: '整理節點', change: '更改', optional: '(選擇性)', - moveToThisNode: '定位至此節點', minimize: '退出全螢幕', maximize: '最大化畫布', + scrollToSelectedNode: '捲動至選取的節點', }, nodes: { common: {