diff --git a/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js b/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js
index 596bd9e71..734a888b1 100644
--- a/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js
+++ b/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js
@@ -59,7 +59,7 @@ const HttpRequestPane = ({ item, collection }) => {
const focusedTab = find(tabs, (t) => t.uid === activeTabUid);
const requestPaneTab = focusedTab?.requestPaneTab;
const getProperty = useCallback(
- (key) => (item.draft ? get(item, `draft.${key}`, []) : get(item, key, [])),
+ (key, defaultValue = []) => (item.draft ? get(item, `draft.${key}`, defaultValue) : get(item, key, defaultValue)),
[item.draft, item]
);
@@ -74,7 +74,10 @@ const HttpRequestPane = ({ item, collection }) => {
const responseVars = getProperty('request.vars.res');
const auth = getProperty('request.auth');
const tags = getProperty('tags');
- const app = item.draft ? get(item, 'draft.app') : get(item, 'app');
+ const app = getProperty('app', null);
+ const appTabEnabled = app?.enabled === true;
+ // A previously selected App tab may be restored while apps are disabled in settings.
+ const effectiveTab = requestPaneTab === 'app' && !appTabEnabled ? 'params' : requestPaneTab;
const activeCounts = useMemo(() => ({
params: params.filter((p) => p.enabled).length,
@@ -116,14 +119,16 @@ const HttpRequestPane = ({ item, collection }) => {
}, [activeCounts, body.mode, hasAuth, script, item.preRequestScriptErrorMessage, item.postResponseScriptErrorMessage, item.testScriptErrorMessage, tests, docs, app, tags]);
const allTabs = useMemo(
- () => TAB_CONFIG.map(({ key, label }) => ({ key, label, indicator: indicators[key] })),
- [indicators]
+ () => TAB_CONFIG
+ .filter(({ key }) => key !== 'app' || appTabEnabled)
+ .map(({ key, label }) => ({ key, label, indicator: indicators[key] })),
+ [indicators, appTabEnabled]
);
const tabPanel = useMemo(() => {
- const Component = TAB_PANELS[requestPaneTab];
+ const Component = TAB_PANELS[effectiveTab];
return Component ?
:
404 | Not found
;
- }, [requestPaneTab, item, collection]);
+ }, [effectiveTab, item, collection]);
if (!activeTabUid || !focusedTab?.uid || !requestPaneTab) {
return
An error occurred!
;
@@ -143,7 +148,7 @@ const HttpRequestPane = ({ item, collection }) => {
{
const rawSettings = getPropertyFromDraftOrRequest('settings');
const settings = { ...DEFAULT_SETTINGS, ...rawSettings };
const { encodeUrl, followRedirects, maxRedirects, timeout } = settings;
+ const enableApp = getPropertyFromDraftOrRequest('app.enabled') === true;
// Reusable function to update settings
const updateSetting = useCallback((settingUpdate) => {
@@ -45,6 +47,14 @@ const Settings = ({ item, collection }) => {
const onToggleFollowRedirects = useCallback(() =>
updateSetting({ followRedirects: !followRedirects }), [followRedirects, updateSetting]);
+ const onToggleEnableApp = useCallback(() => {
+ const next = !enableApp;
+ dispatch(toggleAppMode({ enabled: next, itemUid: item.uid, collectionUid: collection.uid }));
+ if (next) {
+ dispatch(setTabAppPreview({ uid: item.uid, appPreview: false }));
+ }
+ }, [enableApp, dispatch, item.uid, collection.uid]);
+
const onMaxRedirectsChange = useCallback((e) => {
const value = e.target.value;
// Only allow empty string or digits
@@ -131,6 +141,17 @@ const Settings = ({ item, collection }) => {
/>
+
+
+
+
{
);
}
+ const itemSource = item.draft ? item.draft : item;
+ // Preview state is runtime-only, kept on the tab; unset means "preview on" so
+ // an app-enabled request opens in preview mode by default.
const appEnabled = item.type !== 'app'
- && (item.draft ? get(item, 'draft.app.enabled', false) : get(item, 'app.enabled', false));
+ && get(itemSource, 'app.enabled', false) === true
+ && focusedTab.appPreview !== false;
if (item.type === 'app' || appEnabled) {
return ;
}
diff --git a/packages/bruno-app/src/components/RequestTabs/CollectionHeader/index.js b/packages/bruno-app/src/components/RequestTabs/CollectionHeader/index.js
index efac933a2..1af8bf0b7 100644
--- a/packages/bruno-app/src/components/RequestTabs/CollectionHeader/index.js
+++ b/packages/bruno-app/src/components/RequestTabs/CollectionHeader/index.js
@@ -24,13 +24,13 @@ import OpenAPISyncIcon from 'components/Icons/OpenAPISync';
import { switchWorkspace, renameWorkspaceAction, exportWorkspaceAction, confirmWorkspaceCreation, cancelWorkspaceCreation } from 'providers/ReduxStore/slices/workspaces/actions';
import { updateWorkspace } from 'providers/ReduxStore/slices/workspaces';
import { showInFolder } from 'providers/ReduxStore/slices/collections/actions';
-import { toggleCollectionFileMode, toggleAppMode } from 'providers/ReduxStore/slices/collections';
+import { toggleCollectionFileMode } from 'providers/ReduxStore/slices/collections';
import { toggleAiSidebar } from 'providers/ReduxStore/slices/chat';
import MigrateToYmlModal from 'components/CollectionSettings/Overview/Migration/MigrateToYmlModal';
import { findItemInCollection, findItemInCollectionByPathname } from 'utils/collections';
import find from 'lodash/find';
import get from 'lodash/get';
-import { addTab, focusTab } from 'providers/ReduxStore/slices/tabs';
+import { addTab, focusTab, setTabAppPreview } from 'providers/ReduxStore/slices/tabs';
import { uuid } from 'utils/common';
import toast from 'react-hot-toast';
import Dropdown from 'components/Dropdown';
@@ -82,13 +82,15 @@ const CollectionHeader = ({ collection, isScratchCollection }) => {
|| (focusedTab.pathname ? findItemInCollectionByPathname(collection, focusedTab.pathname) : null))
: null;
const isHttpRequestActive = activeItem?.type === 'http-request';
- const appEnabled = activeItem
- ? (activeItem.draft ? get(activeItem, 'draft.app.enabled', false) : get(activeItem, 'app.enabled', false))
- : false;
+ const activeItemSource = activeItem ? activeItem.draft || activeItem : null;
+ // The "Enable App" request setting (persisted as app.enabled) gates the whole
+ // Request/App/File mode toggle.
+ const appAvailable = isHttpRequestActive && get(activeItemSource, 'app.enabled', false) === true;
+ const appEnabled = appAvailable && focusedTab?.appPreview !== false;
const handleToggleAppMode = (enabled) => {
if (isHttpRequestActive) {
- dispatch(toggleAppMode({ enabled, itemUid: activeItem.uid, collectionUid: collection.uid }));
+ dispatch(setTabAppPreview({ uid: focusedTab.uid, appPreview: enabled }));
}
};
@@ -296,9 +298,9 @@ const CollectionHeader = ({ collection, isScratchCollection }) => {
// Build overflow menu items for the "..." dropdown
const overflowMenuItems = [
{ id: 'variables', label: 'Variables', leftSection: IconEye, onClick: viewVariables },
- // File mode is exposed via the Request/App/File view-mode toggle when a request is active;
- // keep it in the overflow as a fallback for non-request contexts.
- ...(!isHttpRequestActive
+ // File mode is exposed via the Request/App/File view-mode toggle when the active
+ // request has apps enabled; keep it in the overflow as a fallback everywhere else.
+ ...(!appAvailable
? [{ id: 'file-mode', label: collection.fileMode ? 'Switch to Code Mode' : 'Switch to File Mode', leftSection: collection.fileMode ? IconFileOff : IconFileCode, onClick: handleFileModeClick }]
: []),
...(!hasOpenApiSyncConfigured
@@ -653,7 +655,7 @@ const CollectionHeader = ({ collection, isScratchCollection }) => {
{!isScratchCollection && (
<>
- {isHttpRequestActive && (
+ {appAvailable && (