diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
index bff2facde..a9f16b977 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/PipelineConsole.tsx
@@ -39,7 +39,6 @@ export default function PipelineConsole() {
,
- "separator",
{
text: "View as plain text",
icon: DOCUMENT,
diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/StageNodeLink.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/StageNodeLink.tsx
index 218ff9752..eaa534d7b 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/StageNodeLink.tsx
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/StageNodeLink.tsx
@@ -14,7 +14,7 @@ export default function StageNodeLink({ agent }: StageNodeLinkProps) {
}
return (
-
+
) => {
setMainViewVisibility(e.target.value as MainViewVisibility);
};
@@ -121,6 +126,7 @@ export default function StagesCustomization() {
+
>
);
}
diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/providers/user-preference-provider.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/providers/user-preference-provider.tsx
index 1a674418a..2e742f1f1 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/providers/user-preference-provider.tsx
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/providers/user-preference-provider.tsx
@@ -30,6 +30,11 @@ interface LayoutPreferences {
setTreeViewWidth: (width: number) => void;
setStageViewWidth: (width: number) => void;
setStageViewHeight: (height: number) => void;
+ /**
+ * Returns true if the current window width is less than the mobile breakpoint.
+ * Used for disabling customization options in favor of a mobile-friendly layout.
+ */
+ isMobile: boolean;
}
// CONTEXT
@@ -65,20 +70,27 @@ const loadFromLocalStorage = (key: string, fallback: T): T => {
return fallback;
};
+const MOBILE_BREAKPOINT = 700;
+
// PROVIDER
export const LayoutPreferencesProvider = ({
children,
}: {
children: ReactNode;
}) => {
- const [stageViewPosition, setStageViewPositionState] =
+ const [windowWidth, setWindowWidth] = useState(
+ typeof window !== "undefined" ? window.innerWidth : 1000,
+ );
+
+ const [persistedStageViewPosition, setStageViewPositionState] =
useState(
loadFromLocalStorage(LS_KEYS.stageViewPosition, StageViewPosition.TOP),
);
- const [mainViewVisibility, setMainViewVisibilityState] =
+ const [persistedMainViewVisibility, setMainViewVisibilityState] =
useState(
loadFromLocalStorage(LS_KEYS.mainViewVisibility, MainViewVisibility.BOTH),
);
+
const [treeViewWidth, setTreeViewWidthState] = useState(
loadFromLocalStorage(LS_KEYS.treeViewWidth, 300),
);
@@ -89,14 +101,33 @@ export const LayoutPreferencesProvider = ({
loadFromLocalStorage(LS_KEYS.stageViewHeight, 250),
);
- // Save to localStorage
+ // Handle responsive override
+ const isMobile = windowWidth < MOBILE_BREAKPOINT;
+ const stageViewPosition = isMobile
+ ? StageViewPosition.TOP
+ : persistedStageViewPosition;
+ const mainViewVisibility = isMobile
+ ? MainViewVisibility.GRAPH_ONLY
+ : persistedMainViewVisibility;
+
+ // Save to localStorage only when not in mobile mode
useEffect(() => {
- window.localStorage.setItem(LS_KEYS.stageViewPosition, stageViewPosition);
- }, [stageViewPosition]);
+ if (!isMobile) {
+ window.localStorage.setItem(
+ LS_KEYS.stageViewPosition,
+ persistedStageViewPosition,
+ );
+ }
+ }, [persistedStageViewPosition, isMobile]);
useEffect(() => {
- window.localStorage.setItem(LS_KEYS.mainViewVisibility, mainViewVisibility);
- }, [mainViewVisibility]);
+ if (!isMobile) {
+ window.localStorage.setItem(
+ LS_KEYS.mainViewVisibility,
+ persistedMainViewVisibility,
+ );
+ }
+ }, [persistedMainViewVisibility, isMobile]);
useEffect(() => {
window.localStorage.setItem(
@@ -119,7 +150,13 @@ export const LayoutPreferencesProvider = ({
);
}, [stageViewHeight]);
- // Setter wrappers
+ // Update window width on resize
+ useEffect(() => {
+ const handleResize = () => setWindowWidth(window.innerWidth);
+ window.addEventListener("resize", handleResize);
+ return () => window.removeEventListener("resize", handleResize);
+ }, []);
+
const setStageViewPosition = (position: StageViewPosition) =>
setStageViewPositionState(position);
const setMainViewVisibility = (visibility: MainViewVisibility) =>
@@ -142,6 +179,7 @@ export const LayoutPreferencesProvider = ({
setTreeViewWidth,
setStageViewWidth,
setStageViewHeight,
+ isMobile,
}}
>
{children}
diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/split-view.scss b/src/main/frontend/pipeline-console-view/pipeline-console/main/split-view.scss
index d4e8cd3bc..de68970ee 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/split-view.scss
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/split-view.scss
@@ -3,6 +3,7 @@
display: grid;
gap: var(--section-padding);
animation: fade-in 0.1s ease-in-out both;
+ align-items: start;
}
@keyframes fade-in {
@@ -12,7 +13,7 @@
}
.pgv-split-view__side-panel {
- position: relative;
+ display: contents;
}
.pgv-split-view__divider {
diff --git a/src/main/frontend/pipeline-console-view/pipeline-console/main/stage-details.tsx b/src/main/frontend/pipeline-console-view/pipeline-console/main/stage-details.tsx
index b767df0d6..140a5de64 100644
--- a/src/main/frontend/pipeline-console-view/pipeline-console/main/stage-details.tsx
+++ b/src/main/frontend/pipeline-console-view/pipeline-console/main/stage-details.tsx
@@ -80,7 +80,7 @@ export default function StageDetails({ stage }: StageDetailsProps) {
{stage.pauseDurationMillis !== 0 && (
-
+