diff --git a/src-web/components/HeadersEditor.tsx b/src-web/components/HeadersEditor.tsx index 05d08ac58..79dc12ea3 100644 --- a/src-web/components/HeadersEditor.tsx +++ b/src-web/components/HeadersEditor.tsx @@ -1,5 +1,7 @@ import type { HttpRequestHeader } from '@yaakapp-internal/models'; import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; +import { Fragment } from 'react'; +import type { InheritedHeader } from '../hooks/useInheritedHeaders'; import { charsets } from '../lib/data/charsets'; import { connections } from '../lib/data/connections'; import { encodings } from '../lib/data/encodings'; @@ -8,6 +10,7 @@ import { mimeTypes } from '../lib/data/mimetypes'; import { CountBadge } from './core/CountBadge'; import { DetailsBanner } from './core/DetailsBanner'; import type { GenericCompletionConfig } from './core/Editor/genericCompletion'; +import { Icon } from './core/Icon'; import type { InputProps } from './core/Input'; import type { Pair, PairEditorProps } from './core/PairEditor'; import { PairEditorRow } from './core/PairEditor'; @@ -18,7 +21,7 @@ import { HStack } from './core/Stacks'; type Props = { forceUpdateKey: string; headers: HttpRequestHeader[]; - inheritedHeaders?: HttpRequestHeader[]; + inheritedHeaders?: InheritedHeader[]; inheritedHeadersLabel?: string; stateKey: string; onChange: (headers: HttpRequestHeader[]) => void; @@ -62,21 +65,29 @@ export function HeadersEditor({ } > -
+
{validInheritedHeaders?.map((pair, i) => ( - + + + + {pair.source.model === 'default' && } + {pair.source.model === 'workspace' && } + {pair.source.model === 'workspace' && pair.source.name} + {pair.source.model === 'folder' && } + {pair.source.model === 'folder' && pair.source.name} + + ))}
diff --git a/src-web/hooks/useInheritedHeaders.ts b/src-web/hooks/useInheritedHeaders.ts index a60610f05..b8447705b 100644 --- a/src-web/hooks/useInheritedHeaders.ts +++ b/src-web/hooks/useInheritedHeaders.ts @@ -14,16 +14,34 @@ const ancestorsAtom = atom((get) => [...get(foldersAtom), ...get(workspacesAtom) export type HeaderModel = HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace; -export function useInheritedHeaders(baseModel: HeaderModel | null) { +export type HeaderSource = { + id: string; + name: string; + model: 'workspace' | 'folder' | 'default'; +}; + +export type InheritedHeader = HttpRequestHeader & { + source: HeaderSource; +}; + +export function useInheritedHeaders(baseModel: HeaderModel | null): InheritedHeader[] { const parents = useAtomValue(ancestorsAtom); + const defaultSource: HeaderSource = { id: 'default', name: 'Default', model: 'default' }; + if (baseModel == null) return []; - if (baseModel.model === 'workspace') return defaultHeaders; + if (baseModel.model === 'workspace') { + return defaultHeaders.map((h) => ({ ...h, source: defaultSource })); + } - const next = (child: HeaderModel): HttpRequestHeader[] => { + const next = (child: HeaderModel): InheritedHeader[] => { // Short-circuit at workspace level - return global defaults + workspace headers if (child.model === 'workspace') { - return [...defaultHeaders, ...child.headers]; + const workspaceSource: HeaderSource = { id: child.id, name: child.name, model: 'workspace' }; + return [ + ...defaultHeaders.map((h) => ({ ...h, source: defaultSource })), + ...child.headers.map((h) => ({ ...h, source: workspaceSource })), + ]; } // Recurse up the tree @@ -38,13 +56,18 @@ export function useInheritedHeaders(baseModel: HeaderModel | null) { } const headers = next(parent); - return [...headers, ...parent.headers]; + const parentSource: HeaderSource = { + id: parent.id, + name: parent.name, + model: parent.model as 'workspace' | 'folder', + }; + return [...headers, ...parent.headers.map((h) => ({ ...h, source: parentSource }))]; }; const allHeaders = next(baseModel); // Deduplicate by header name (case-insensitive), keeping the latest (most specific) value - const headersByName = new Map(); + const headersByName = new Map(); for (const header of allHeaders) { headersByName.set(header.name.toLowerCase(), header); }