This is an automated email from the ASF dual-hosted git repository.

jscheffl pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new e4d4514aeee [v3-1-test] Fix text selection jumping in logs pane to 
match text editor behavior (#57309) (#57453)
e4d4514aeee is described below

commit e4d4514aeee0eb330599eceabdf61df453db496f
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Oct 28 22:54:49 2025 +0100

    [v3-1-test] Fix text selection jumping in logs pane to match text editor 
behavior (#57309) (#57453)
    
    * Fix text selection jumping in logs pane to match text editor behavior
    
      When selecting text by clicking and dragging in the logs pane, the
      selection would jump unexpectedly when clicking in empty space at
      the end of lines or below the last log line. This was caused by the
      virtualized list rendering with absolutely positioned elements that
      didn't extend to fill available space.
    
      Changes:
      - Make log line containers full width to enable selection in empty areas
      - Add invisible filler element below last log line to prevent EOF jump
      - Ensure minimum height covers all empty space at bottom of container
    
      The logs pane now behaves like traditional text editors (e.g. Notepad)
      where users can click anywhere and drag to select multiple lines without
      the selection jumping.
    
    * Fix log view vanishing when text wrap is enabled
    
      When toggling text wrap on in the logs pane, all log content would
      disappear. This was caused by three issues:
    
      1. CSS textWrap property was set to pre instead of pre-wrap, which
         preserves whitespace but does not wrap text
      2. Virtualizer was not remeasuring items when wrap toggled, causing
         items to be positioned with cached single-line heights even when
         text became multi-line
      3. Width constraints conflicted - items need to be constrained to 100%
         when wrapped (to force wrapping) but max-content when unwrapped
         (to allow horizontal scrolling)
    
      Changes:
      - Change textWrap from pre to pre-wrap when wrap is enabled
      - Add useLayoutEffect to force virtualizer remeasurement on wrap toggle
      - Add width=100% to VStack to provide proper width reference
      - Set item width conditionally: 100% when wrapped, max-content when not
      - Keep minWidth=100% always for text selection in empty areas
    (cherry picked from commit d6fa3b089cc18e87cff89b15d7b7ddd1dcafa8c3)
    
    Co-authored-by: Dheeraj Turaga <[email protected]>
---
 .../src/pages/TaskInstance/Logs/TaskLogContent.tsx | 25 ++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogContent.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogContent.tsx
index dab2a4c6676..8347bc54ee6 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogContent.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogContent.tsx
@@ -100,6 +100,11 @@ export const TaskLogContent = ({ error, isLoading, 
logError, parsedLogs, wrap }:
     }
   }, [isLoading, rowVirtualizer, hash, parsedLogs]);
 
+  useLayoutEffect(() => {
+    // Force remeasurement when wrap changes since item heights will change
+    rowVirtualizer.measure();
+  }, [wrap, rowVirtualizer]);
+
   const handleScrollTo = (to: "bottom" | "top") => {
     if (parsedLogs.length > 0) {
       rowVirtualizer.scrollToIndex(to === "bottom" ? parsedLogs.length - 1 : 
0);
@@ -126,10 +131,17 @@ export const TaskLogContent = ({ error, isLoading, 
logError, parsedLogs, wrap }:
         position="relative"
         py={3}
         ref={parentRef}
-        textWrap={wrap ? "pre" : "nowrap"}
+        textWrap={wrap ? "pre-wrap" : "nowrap"}
         width="100%"
       >
-        <VStack alignItems="flex-start" gap={0} 
h={`${rowVirtualizer.getTotalSize()}px`}>
+        <VStack
+          alignItems="flex-start"
+          gap={0}
+          h={`${rowVirtualizer.getTotalSize()}px`}
+          minH="100%"
+          position="relative"
+          width="100%"
+        >
           {rowVirtualizer.getVirtualItems().map((virtualRow) => (
             <Box
               _ltr={{
@@ -146,6 +158,7 @@ export const TaskLogContent = ({ error, isLoading, 
logError, parsedLogs, wrap }:
               data-index={virtualRow.index}
               data-testid={`virtualized-item-${virtualRow.index}`}
               key={virtualRow.key}
+              minWidth="100%"
               position="absolute"
               ref={rowVirtualizer.measureElement}
               top={`${virtualRow.start}px`}
@@ -154,6 +167,14 @@ export const TaskLogContent = ({ error, isLoading, 
logError, parsedLogs, wrap }:
               {parsedLogs[virtualRow.index] ?? undefined}
             </Box>
           ))}
+          <Box
+            bottom={0}
+            left={0}
+            minH={`calc(100% - ${rowVirtualizer.getTotalSize()}px)`}
+            position="absolute"
+            top={`${rowVirtualizer.getTotalSize()}px`}
+            width="100%"
+          />
         </VStack>
       </Code>
 

Reply via email to